[FFmpeg-devel] [PATCH] [RFC] avformat/movenc: support writing iTunes cover image

Timo Teräs timo.teras at iki.fi
Thu Mar 29 09:45:13 EEST 2018


Fixes https://trac.ffmpeg.org/ticket/2798

This makes movenc handle AV_DISPOSITION_ATTACHED_PIC and write
the associated pictures in iTunes cover atom. This corresponds
to how 'mov' demuxer parses and exposes the cover images when
reading.

Tested to produce valid stream with:
 ffmpeg -i movie.mp4 -i thumb.jpg -disposition:v:1 attached_pic
        -map 0 -map 1 -c copy out.mp4

The cover image is also copied corretly with:
 ffmpeg -i movie.mp4 -map 0 -c copy out.mp4

AtomicParseley says that the attached_pic stream is properly
not visible in the main tracks of the file. Though, I am not
sure if there's any side-effects of having internal stream
without any packets. We may need to arm the mov muxer with
additional checks for streams without any packets.

It may make sense to move the code in mov_write_packet() that
grabs the attached picture to generic code in mux.c. Seems there's
currently no other muxer supporting cover images than mp3 and
it seems to use special code to handle them.

Signed-off-by: Timo Teräs <timo.teras at iki.fi>
Cc: Matthieu Bouron <matthieu.bouron at gmail.com>
---
 fftools/ffmpeg.c     |  1 +
 libavformat/movenc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 1b2e37b8d8..b6751d7faa 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -3562,6 +3562,7 @@ static int init_output_stream(OutputStream *ost, char *error, int error_len)
             { "hearing_impaired"    , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_HEARING_IMPAIRED  },    .unit = "flags" },
             { "visual_impaired"     , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED   },    .unit = "flags" },
             { "clean_effects"       , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CLEAN_EFFECTS     },    .unit = "flags" },
+            { "attached_pic"        , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ATTACHED_PIC      },    .unit = "flags" },
             { "captions"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS          },    .unit = "flags" },
             { "descriptions"        , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS      },    .unit = "flags" },
             { "dependent"           , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEPENDENT         },    .unit = "flags" },
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 230aeb6a6d..5400b2747e 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -3414,6 +3414,54 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb,
     return size;
 }
 
+static int mov_write_covr(AVIOContext *pb, AVFormatContext *s)
+{
+    int64_t pos = 0;
+    int i;
+
+    for (i = 0; i < s->nb_streams; i++) {
+        AVStream *st = s->streams[i];
+        enum AVCodecID codec_id = st->codecpar->codec_id;
+        int type;
+
+        if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
+            continue;
+
+        switch (codec_id) {
+        case AV_CODEC_ID_MJPEG:
+            type = 0xD;
+            break;
+        case AV_CODEC_ID_PNG:
+            type = 0xE;
+            break;
+        case AV_CODEC_ID_BMP:
+            type = 0x1B;
+            break;
+        default:
+            av_log(s, AV_LOG_ERROR, "unsupported codec %s for cover, skipping",
+                   av_fourcc2str(st->codecpar->codec_tag));
+            continue;
+        }
+
+        if (!pos) {
+            pos = avio_tell(pb);
+            avio_wb32(pb, 0);
+            ffio_wfourcc(pb, "covr");
+        }
+
+        avio_wb32(pb, 16 + st->attached_pic.size);
+        ffio_wfourcc(pb, "data");
+        avio_wb32(pb, type);
+        avio_wb32(pb , 0);
+        avio_write(pb, st->attached_pic.data, st->attached_pic.size);
+    }
+
+    if (pos)
+         return update_size(pb, pos);
+
+    return 0;
+}
+
 /* iTunes meta data list */
 static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
                               AVFormatContext *s)
@@ -3451,6 +3499,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov,
     mov_write_int8_metadata  (s, pb, "hdvd",    "hd_video",  1);
     mov_write_int8_metadata  (s, pb, "pgap",    "gapless_playback",1);
     mov_write_int8_metadata  (s, pb, "cpil",    "compilation", 1);
+    mov_write_covr(pb, s);
     mov_write_trkn_tag(pb, mov, s, 0); // track number
     mov_write_trkn_tag(pb, mov, s, 1); // disc number
     mov_write_tmpo_tag(pb, s);
@@ -5480,6 +5529,24 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
     if (!pkt) {
         mov_flush_fragment(s, 1);
         return 1;
+    } if (s->streams[pkt->stream_index]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
+        AVStream *st = s->streams[pkt->stream_index];
+        int ret;
+
+        if (st->nb_frames >= 1) {
+             /* warn only once */
+             if (st->nb_frames == 1)
+                 av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d,"
+                        " ignoring.\n", pkt->stream_index);
+             return 0;
+        }
+
+        if ((ret = av_copy_packet(&st->attached_pic, pkt)) < 0)
+            return ret;
+
+        st->attached_pic.stream_index = st->index;
+        st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
+        return 0;
     } else {
         int i;
         MOVMuxContext *mov = s->priv_data;
-- 
2.16.2



More information about the ffmpeg-devel mailing list