[FFmpeg-devel] [PATCH] flacenc: Support attaching pictures
Paul B Mahol
onemda at gmail.com
Tue Aug 6 11:54:01 CEST 2013
On 8/5/13, James Almer <jamrial at gmail.com> wrote:
> This is based on the implementation from the mp3 muxer, so certain
> chunks of code were copied and adapted where needed.
>
> Signed-off-by: James Almer <jamrial at gmail.com>
> ---
> libavformat/flacenc.c | 197
> +++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 187 insertions(+), 10 deletions(-)
>
> diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c
> index 87e9362..ca97de5 100644
> --- a/libavformat/flacenc.c
> +++ b/libavformat/flacenc.c
> @@ -23,9 +23,20 @@
> #include "avformat.h"
> #include "avio_internal.h"
> #include "flacenc.h"
> +#include "id3v2.h"
> #include "vorbiscomment.h"
> #include "libavcodec/bytestream.h"
> +#include "libavutil/pixdesc.h"
>
> +typedef struct FLACContext {
> + /* index of the audio stream */
> + int audio_stream_idx;
> + /* number of attached pictures we still need to write */
> + int pics_to_write;
> + /* audio packets are queued here until we get all the attached pictures
> */
> + AVPacketList *queue, *queue_end;
> + int type;
> +} FLACContext;
>
> static int flac_write_block_padding(AVIOContext *pb, unsigned int
> n_padding_bytes,
> int last_block)
> @@ -62,19 +73,113 @@ static int flac_write_block_comment(AVIOContext *pb,
> AVDictionary **m,
> return 0;
> }
>
> +static int flac_write_block_picture(AVFormatContext *s, AVPacket *pkt,
> + int last_block)
> +{
> + AVDictionaryEntry *m;
> + AVIOContext *pb = s->pb;
> + FLACContext *flac = s->priv_data;
> + AVStream *st = s->streams[pkt->stream_index];
> + const CodecMime *mime = ff_id3v2_mime_tags;
> + const char *mimetype = NULL, *desc = "";
> + int i, mimelen, desclen, type = 3;
> +
> + if (st->codec->codec_id == AV_CODEC_ID_GIF) {
> + avpriv_report_missing_feature(s, "GIF picture");
> + return AVERROR_PATCHWELCOME;
> + } else if (st->codec->codec_id != AV_CODEC_ID_MJPEG &&
> + st->codec->codec_id != AV_CODEC_ID_PNG) {
> + av_log(s, AV_LOG_ERROR, "Unsupported picture format");
> + return AVERROR(EINVAL);
> + }
> +
> + /* get the mimetype*/
> + while (mime->id != AV_CODEC_ID_NONE) {
> + if (mime->id == st->codec->codec_id) {
> + mimetype = mime->str;
> + break;
> + }
> + mime++;
> + }
> + if (!mimetype) {
> + av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot
> "
> + "write an attached picture.\n", st->index);
> + return AVERROR(EINVAL);
> + }
> + mimelen = strlen(mimetype);
> +
> + /* get the picture type */
> + m = av_dict_get(st->metadata, "comment", NULL, 0);
> + for (i = 0; m && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) {
> + if (strstr(ff_id3v2_picture_types[i], m->value) ==
> ff_id3v2_picture_types[i]) {
> + type = i;
> + break;
> + }
> + }
> + if (type == 1 || type == 2) {
> + if (flac->type & type) {
> + av_log(s, AV_LOG_ERROR, "Only one picture of type \"%s\" can be
> muxed.\n",
> + ff_id3v2_picture_types[type]);
> + return AVERROR(EINVAL);
> + }
> + if (type == 1 && st->codec->codec_id != AV_CODEC_ID_PNG) {
> + av_log(s, AV_LOG_ERROR, "Picture of type \"%s\" must be
> PNG.\n",
> + ff_id3v2_picture_types[type]);
> + return AVERROR(EINVAL);
> + }
> + flac->type |= type;
> + }
> +
> + /* get the description */
> + if ((m = av_dict_get(st->metadata, "title", NULL, 0)))
> + desc = m->value;
> + desclen = strlen(desc);
> +
> + /* start writing */
> + avio_w8 (pb, last_block ? 0x86 : 0x06);
> + avio_wb24 (pb, mimelen + desclen + 32 + pkt->size);
> + avio_wb32 (pb, type);
> + avio_wb32 (pb, mimelen);
> + avio_write(pb, mimetype, mimelen);
> + avio_wb32 (pb, desclen);
> + avio_write(pb, desc, desclen);
> + avio_wb32 (pb, st->codec->width);
> + avio_wb32 (pb, st->codec->height);
> + avio_wb32 (pb,
> av_get_bits_per_pixel(av_pix_fmt_desc_get(st->codec->pix_fmt)));
> + avio_wb32 (pb, 0); // TODO: Number of colors for indexed pics (GIF)
> + avio_wb32 (pb, pkt->size);
> + avio_write(pb, pkt->data, pkt->size);
> +
> + return 0;
> +}
> +
> static int flac_write_header(struct AVFormatContext *s)
> {
> int ret;
> - AVCodecContext *codec = s->streams[0]->codec;
> + AVCodecContext *codec;
> + FLACContext *flac = s->priv_data;
>
> - if (s->nb_streams > 1) {
> - av_log(s, AV_LOG_ERROR, "only one stream is supported\n");
> - return AVERROR(EINVAL);
> + flac->audio_stream_idx = -1;
> + for (ret = 0; ret < s->nb_streams; ret++) {
> + AVStream *st = s->streams[ret];
> + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
> + if (flac->audio_stream_idx >= 0 || st->codec->codec_id !=
> AV_CODEC_ID_FLAC) {
> + av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one
> FLAC "
> + "audio stream is required.\n");
> + return AVERROR(EINVAL);
> + }
> + flac->audio_stream_idx = ret;
> + } else if (st->codec->codec_type != AVMEDIA_TYPE_VIDEO) {
> + av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are
> allowed in FLAC.\n");
> + return AVERROR(EINVAL);
> + }
> }
> - if (codec->codec_id != AV_CODEC_ID_FLAC) {
> - av_log(s, AV_LOG_ERROR, "unsupported codec\n");
> + if (flac->audio_stream_idx < 0) {
> + av_log(s, AV_LOG_ERROR, "No audio stream present.\n");
> return AVERROR(EINVAL);
> }
> + flac->pics_to_write = s->nb_streams - 1;
> + codec = s->streams[flac->audio_stream_idx]->codec;
>
> ret = ff_flac_write_header(s->pb, codec, 0);
> if (ret)
> @@ -89,19 +194,45 @@ static int flac_write_header(struct AVFormatContext
> *s)
> * every 10s. So one might add padding to allow that later
> * but there seems to be no simple way to get the duration here.
> * So let's try the flac default of 8192 bytes */
> - flac_write_block_padding(s->pb, 8192, 1);
> + if (!flac->pics_to_write)
> + flac_write_block_padding(s->pb, 8192, 1);
>
> return ret;
> }
>
> +static int flac_queue_flush(AVFormatContext *s)
> +{
> + FLACContext *flac = s->priv_data;
> + AVPacketList *pktl;
> +
> + flac_write_block_padding(s->pb, 8192, 1);
> + while ((pktl = flac->queue)) {
> + avio_write(s->pb, pktl->pkt.data, pktl->pkt.size);
> + av_free_packet(&pktl->pkt);
> + flac->queue = pktl->next;
> + av_freep(&pktl);
> + }
> + flac->queue_end = NULL;
> +
> + return 0;
> +}
> +
> static int flac_write_trailer(struct AVFormatContext *s)
> {
> AVIOContext *pb = s->pb;
> + FLACContext *flac = s->priv_data;
> + AVCodecContext *codec = s->streams[flac->audio_stream_idx]->codec;
> uint8_t *streaminfo;
> enum FLACExtradataFormat format;
> int64_t file_size;
>
> - if (!avpriv_flac_is_extradata_valid(s->streams[0]->codec, &format,
> &streaminfo))
> + if (flac->pics_to_write) {
> + av_log(s, AV_LOG_WARNING, "No packets were sent for some of the "
> + "attached pictures.\n");
> + flac_queue_flush(s);
> + }
> +
> + if (!avpriv_flac_is_extradata_valid(codec, &format, &streaminfo))
> return -1;
>
> if (pb->seekable) {
> @@ -119,7 +250,52 @@ static int flac_write_trailer(struct AVFormatContext
> *s)
>
> static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt)
> {
> - avio_write(s->pb, pkt->data, pkt->size);
> + FLACContext *flac = s->priv_data;
> +
> + if (pkt->stream_index == flac->audio_stream_idx) {
> + if (flac->pics_to_write) {
> + /* buffer audio packets until we get all the pictures */
> + AVPacketList *pktl = av_mallocz(sizeof(*pktl));
> + if (!pktl)
> + return AVERROR(ENOMEM);
> +
> + pktl->pkt = *pkt;
> + pktl->pkt.buf = av_buffer_ref(pkt->buf);
> + if (!pktl->pkt.buf) {
> + av_freep(&pktl);
> + return AVERROR(ENOMEM);
> + }
So this eats endless memory? How much of it is buffered?
This is extremly sloppy/ugly/complicated/buggy design.
> +
> + if (flac->queue_end)
> + flac->queue_end->next = pktl;
> + else
> + flac->queue = pktl;
> + flac->queue_end = pktl;
> + } else {
> + avio_write(s->pb, pkt->data, pkt->size);
> + return 0;
> + }
> + } else {
> + AVStream *st = s->streams[pkt->stream_index];
> + int ret;
> +
> + /* warn only once for each stream */
> + if (st->nb_frames == 1) {
> + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream
> %d,"
> + " ignoring.\n", pkt->stream_index);
> + }
> + if (!flac->pics_to_write || st->nb_frames >= 1)
> + return 0;
> +
> + if ((ret = flac_write_block_picture(s, pkt, 0)) < 0)
> + return ret;
> + flac->pics_to_write--;
> +
> + /* flush the buffered audio packets */
> + if (!flac->pics_to_write)
> + return flac_queue_flush(s);
> + }
> +
> return 0;
> }
>
> @@ -128,8 +304,9 @@ AVOutputFormat ff_flac_muxer = {
> .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"),
> .mime_type = "audio/x-flac",
> .extensions = "flac",
> + .priv_data_size = sizeof(FLACContext),
> .audio_codec = AV_CODEC_ID_FLAC,
> - .video_codec = AV_CODEC_ID_NONE,
> + .video_codec = AV_CODEC_ID_PNG,
> .write_header = flac_write_header,
> .write_packet = flac_write_packet,
> .write_trailer = flac_write_trailer,
> --
> 1.8.1.5
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
More information about the ffmpeg-devel
mailing list