diff mbox

[FFmpeg-devel,1/2] Add experimental muxing support for FLAC in ISO BMFF (MP4).

Message ID 20161101011100.GA8366@brak.lan
State Superseded
Headers show

Commit Message

Matthew Gregan Nov. 1, 2016, 1:11 a.m. UTC
At 2016-10-31T11:04:16-0300, James Almer wrote:
> > +static int mov_write_dfla_tag(AVIOContext *pb, MOVTrack *track)
> > +{
> > +    const size_t FLAC_STREAMINFO_SIZE = 34;
> > +    int64_t pos = avio_tell(pb);
> > +    avio_wb32(pb, 0);
> > +    ffio_wfourcc(pb, "dfLa");
> > +    avio_w8(pb, 0); /* version */
> > +    avio_wb24(pb, 0); /* flags */
> > +
> > +    /* Expect the encoder to pass a METADATA_BLOCK_TYPE_STREAMINFO. */
> 
> The encoder may (or most likely will) also pass an updated streaminfo as
> packet side data.  See the flac muxer, you'll have to do the same here.

Thanks.  It looks like libavcodec/flacenc.c#flac_encode_frame updates the
extradata at the same time it emits the packet side data, so I'm slightly
confused why this is needed.

Seems to be easy to handle by extending the existing case for
AV_CODEC_ID_MP4ALS in mov_write_single_packet.  That assumes pkt->size == 0,
which seems to be true when the packet side data is emitted in
flac_encode_frame.

Updated patch attached.

Comments

James Almer Nov. 1, 2016, 3:56 a.m. UTC | #1
On 10/31/2016 10:11 PM, Matthew Gregan wrote:
> At 2016-10-31T11:04:16-0300, James Almer wrote:
>>> +static int mov_write_dfla_tag(AVIOContext *pb, MOVTrack *track)
>>> +{
>>> +    const size_t FLAC_STREAMINFO_SIZE = 34;
>>> +    int64_t pos = avio_tell(pb);
>>> +    avio_wb32(pb, 0);
>>> +    ffio_wfourcc(pb, "dfLa");
>>> +    avio_w8(pb, 0); /* version */
>>> +    avio_wb24(pb, 0); /* flags */
>>> +
>>> +    /* Expect the encoder to pass a METADATA_BLOCK_TYPE_STREAMINFO. */
>>
>> The encoder may (or most likely will) also pass an updated streaminfo as
>> packet side data.  See the flac muxer, you'll have to do the same here.
> 
> Thanks.  It looks like libavcodec/flacenc.c#flac_encode_frame updates the
> extradata at the same time it emits the packet side data, so I'm slightly
> confused why this is needed.

The muxer gets the extradata as created by the encoder only during init().
Even if the encoder changes it in any way afterwards, it will never make
it to the muxer. That's why packet side data is used for this.

The reason the encoder used the AVCodecContext extradata to store the
updated Streaminfo before creating the packet side data is most likely
because it was an already allocated buffer. Technically, it's not the
"proper" thing to do, but it also doesn't seem to make a difference.

> 
> Seems to be easy to handle by extending the existing case for
> AV_CODEC_ID_MP4ALS in mov_write_single_packet.  That assumes pkt->size == 0,
> which seems to be true when the packet side data is emitted in
> flac_encode_frame.
> 
> Updated patch attached.

Looks like it works now.

No comments about the actual implementation or the spec. I'll let someone
more familiar with mp4/mov do that instead.
diff mbox

Patch

From ba197e6e9df3916f7dd433736c580b27f3c6ca75 Mon Sep 17 00:00:00 2001
Message-Id: <ba197e6e9df3916f7dd433736c580b27f3c6ca75.1477962243.git.kinetik@flim.org>
From: Matthew Gregan <kinetik@flim.org>
Date: Thu, 20 Oct 2016 17:28:11 +1300
Subject: [PATCH 1/2] Add experimental muxing support for FLAC in ISO BMFF
 (MP4).

Based on the draft spec at https://git.xiph.org/?p=flac.git;a=blob;f=doc/isoflac.txt

'-strict -2' is required to create files in this format.

Signed-off-by: Matthew Gregan <kinetik@flim.org>
---
 libavformat/isom.c   |  2 ++
 libavformat/movenc.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/libavformat/isom.c b/libavformat/isom.c
index ab79e22..aacbe43 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -60,6 +60,7 @@  const AVCodecTag ff_mp4_obj_type[] = {
     { AV_CODEC_ID_EAC3        , 0xA6 },
     { AV_CODEC_ID_DTS         , 0xA9 }, /* mp4ra.org */
     { AV_CODEC_ID_VP9         , 0xC0 }, /* nonstandard, update when there is a standard value */
+    { AV_CODEC_ID_FLAC        , 0xC1 }, /* nonstandard, update when there is a standard value */
     { AV_CODEC_ID_TSCC2       , 0xD0 }, /* nonstandard, camtasia uses it */
     { AV_CODEC_ID_VORBIS      , 0xDD }, /* nonstandard, gpac uses it */
     { AV_CODEC_ID_DVD_SUBTITLE, 0xE0 }, /* nonstandard, see unsupported-embedded-subs-2.mp4 */
@@ -345,6 +346,7 @@  const AVCodecTag ff_codec_movaudio_tags[] = {
     { AV_CODEC_ID_WMAV2,           MKTAG('W', 'M', 'A', '2') },
     { AV_CODEC_ID_EVRC,            MKTAG('s', 'e', 'v', 'c') }, /* 3GPP2 */
     { AV_CODEC_ID_SMV,             MKTAG('s', 's', 'm', 'v') }, /* 3GPP2 */
+    { AV_CODEC_ID_FLAC,            MKTAG('f', 'L', 'a', 'C') }, /* nonstandard */
     { AV_CODEC_ID_NONE, 0 },
 };
 
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 6228192..0a6ffb0 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -654,6 +654,27 @@  static int mov_write_wfex_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
     return update_size(pb, pos);
 }
 
+static int mov_write_dfla_tag(AVIOContext *pb, MOVTrack *track)
+{
+    const size_t FLAC_STREAMINFO_SIZE = 34;
+    int64_t pos = avio_tell(pb);
+    avio_wb32(pb, 0);
+    ffio_wfourcc(pb, "dfLa");
+    avio_w8(pb, 0); /* version */
+    avio_wb24(pb, 0); /* flags */
+
+    /* Expect the encoder to pass a METADATA_BLOCK_TYPE_STREAMINFO. */
+    if (track->par->extradata_size != FLAC_STREAMINFO_SIZE)
+      return AVERROR_INVALIDDATA;
+
+    /* TODO: Write other METADATA_BLOCK_TYPEs if the encoder makes them available. */
+    avio_w8(pb, 1 << 7 | 0); /* LastMetadataBlockFlag << 7 | BlockType (STREAMINFO) */
+    avio_wb24(pb, track->par->extradata_size); /* Length */
+    avio_write(pb, track->par->extradata, track->par->extradata_size); /* BlockData[Length] */
+
+    return update_size(pb, pos);
+}
+
 static int mov_write_chan_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
 {
     uint32_t layout_tag, bitmap;
@@ -963,8 +984,13 @@  static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
                 avio_wb16(pb, 16);
             avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
         } else { /* reserved for mp4/3gp */
-            avio_wb16(pb, 2);
-            avio_wb16(pb, 16);
+            if (track->par->codec_id == AV_CODEC_ID_FLAC) {
+                avio_wb16(pb, track->par->channels);
+                avio_wb16(pb, av_get_bytes_per_sample(track->par->format) * 8);
+            } else {
+                avio_wb16(pb, 2);
+                avio_wb16(pb, 16);
+            }
             avio_wb16(pb, 0);
         }
 
@@ -1009,6 +1035,8 @@  static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
         mov_write_extradata_tag(pb, track);
     else if (track->par->codec_id == AV_CODEC_ID_WMAPRO)
         mov_write_wfex_tag(s, pb, track);
+    else if (track->par->codec_id == AV_CODEC_ID_FLAC)
+        mov_write_dfla_tag(pb, track);
     else if (track->vos_len > 0)
         mov_write_glbl_tag(pb, track);
 
@@ -1177,6 +1205,7 @@  static int mp4_get_codec_tag(AVFormatContext *s, MOVTrack *track)
     else if (track->par->codec_id == AV_CODEC_ID_DIRAC)     tag = MKTAG('d','r','a','c');
     else if (track->par->codec_id == AV_CODEC_ID_MOV_TEXT)  tag = MKTAG('t','x','3','g');
     else if (track->par->codec_id == AV_CODEC_ID_VC1)       tag = MKTAG('v','c','-','1');
+    else if (track->par->codec_id == AV_CODEC_ID_FLAC)      tag = MKTAG('f','L','a','C');
     else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO)  tag = MKTAG('m','p','4','v');
     else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO)  tag = MKTAG('m','p','4','a');
     else if (track->par->codec_id == AV_CODEC_ID_DVD_SUBTITLE)  tag = MKTAG('m','p','4','s');
@@ -5019,7 +5048,8 @@  static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
                     trk->start_cts = 0;
             }
 
-            if (trk->par->codec_id == AV_CODEC_ID_MP4ALS) {
+            if (trk->par->codec_id == AV_CODEC_ID_MP4ALS ||
+                trk->par->codec_id == AV_CODEC_ID_FLAC) {
                 int side_size = 0;
                 uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
                 if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) {
@@ -5733,6 +5763,16 @@  static int mov_init(AVFormatContext *s)
                            i, track->par->sample_rate);
                 }
             }
+            if (track->mode == MODE_MP4 &&
+                track->par->codec_id == AV_CODEC_ID_FLAC) {
+                if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+                    av_log(s, AV_LOG_ERROR,
+                           "FLAC in MP4 support is experimental, add "
+                           "'-strict %d' if you want to use it.\n",
+                           FF_COMPLIANCE_EXPERIMENTAL);
+                    return AVERROR_EXPERIMENTAL;
+                }
+            }
         } else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
             track->timescale = st->time_base.den;
         } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
-- 
2.10.1