diff mbox

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

Message ID 20180329064513.19252-1-timo.teras@iki.fi
State Superseded
Headers show

Commit Message

Timo Teräs March 29, 2018, 6:45 a.m. UTC
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@iki.fi>
Cc: Matthieu Bouron <matthieu.bouron@gmail.com>
---
 fftools/ffmpeg.c     |  1 +
 libavformat/movenc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)

Comments

Michael Niedermayer March 31, 2018, 12:13 a.m. UTC | #1
On Thu, Mar 29, 2018 at 09:45:13AM +0300, Timo Teräs wrote:
> 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@iki.fi>
> Cc: Matthieu Bouron <matthieu.bouron@gmail.com>
> ---
>  fftools/ffmpeg.c     |  1 +
>  libavformat/movenc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 68 insertions(+)
[...]

> @@ -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) {

The if() should be in the next line or it should be a else if
it looks like someone forgot a "else" even though it makes no
difference here.

thanks

[...]
Timo Teräs March 31, 2018, 6:54 a.m. UTC | #2
Hi

On Sat, 31 Mar 2018 02:13:14 +0200
Michael Niedermayer <michael@niedermayer.cc> wrote:

> On Thu, Mar 29, 2018 at 09:45:13AM +0300, Timo Teräs wrote:
> > 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.
> [...]
> 
> > @@ -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) {  
> 
> The if() should be in the next line or it should be a else if
> it looks like someone forgot a "else" even though it makes no
> difference here.

Thanks. Will fix this, and have few other minor cleanups too. Will
resend, but I have additional questions/notes too:

I think I will audit all for (...; ... < mov->nb_streams; ...) loops
to see if they need to skip these attachments. In most cases it's
implicitly done because they don't get track->entry incremented now.
However, there might be few off especially with empty_moov flag.

Wondering also if all those "track->entry <= 0" checks should be made
to static inline function mov_track_has_data() or similar?

Any comments if the patch should be split e.g. adding the attached_pic
flag on it's own?

Or if the code in mov_write_packet() to stuff the received data to the
buffers should be in generic code, or made a helper function?

And finally, should the codec tag/type negotiation code somehow be
updated to recognize attachment_pic and accept only the valid three
image formats supported for now? If yes, how to do it?

Thanks,
Timo
Michael Niedermayer April 1, 2018, 12:01 a.m. UTC | #3
On Sat, Mar 31, 2018 at 09:54:01AM +0300, Timo Teras wrote:
> Hi
> 
> On Sat, 31 Mar 2018 02:13:14 +0200
> Michael Niedermayer <michael@niedermayer.cc> wrote:
> 
> > On Thu, Mar 29, 2018 at 09:45:13AM +0300, Timo Teräs wrote:
> > > 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.
> > [...]
> > 
> > > @@ -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) {  
> > 
> > The if() should be in the next line or it should be a else if
> > it looks like someone forgot a "else" even though it makes no
> > difference here.
> 
> Thanks. Will fix this, and have few other minor cleanups too. Will
> resend, but I have additional questions/notes too:
> 
> I think I will audit all for (...; ... < mov->nb_streams; ...) loops
> to see if they need to skip these attachments. In most cases it's
> implicitly done because they don't get track->entry incremented now.
> However, there might be few off especially with empty_moov flag.
> 

> Wondering also if all those "track->entry <= 0" checks should be made
> to static inline function mov_track_has_data() or similar?

sounds like a good idea


> 
> Any comments if the patch should be split e.g. adding the attached_pic
> flag on it's own?

yes, that certainly should be split into a seperate patch


> 
> Or if the code in mov_write_packet() to stuff the received data to the
> buffers should be in generic code, or made a helper function?

If it can be shared it could be in common code. yes

> 
> And finally, should the codec tag/type negotiation code somehow be
> updated to recognize attachment_pic and accept only the valid three
> image formats supported for now? If yes, how to do it?
> 
> Thanks,
> Timo
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Timo Teräs April 2, 2018, 8:36 a.m. UTC | #4
Hi,

This patch set adds support for writing iTunes cover images in mov muxer.

Copying of the picture to attached_pic was not yet made common code. It is
better to do this when additional users are introduced.

Passes fate. valgrind says runs to add/copy cover images were without errors.

Changes since first RFC patch:
- split ffmpeg attached_pic definition to separate commit
- added missed 'else' in mov_write_packet() per review comment
- additional checks to ignore attached_pic streams as needed
- mov_write_covr() simplified slightly
- av_packet_ref() used instead of deprecated av_copy_packet()

Timo Teräs (3):
  ffmpeg: allow setting attached_pic disposition
  avformat/movenc: add rtp_hinting_needed() helper function
  avformat/movenc: support writing iTunes cover image

 fftools/ffmpeg.c       |   1 +
 libavformat/avformat.h |   2 +-
 libavformat/movenc.c   | 105 +++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 87 insertions(+), 21 deletions(-)
diff mbox

Patch

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;