diff mbox

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

Message ID 20161118032423.GH8366@brak.lan
State Accepted
Headers show

Commit Message

Matthew Gregan Nov. 18, 2016, 3:25 a.m. UTC
At 2016-11-17T22:57:49-0300, James Almer wrote:
> > @@ -345,6 +346,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
> > +    { AV_CODEC_ID_FLAC,            MKTAG('f', 'L', 'a', 'C') }, /* nonstandard */
> 
> This is enables muxing into mov, which bypasses the experimental check below
> since mov isn't MODE_MP4.

Thanks.  Updated the check to reject non-MP4 as invalid before checking the
experimental flag.  I based the original check on the VP9/MP4 muxing, which
has the same bug, should I send a patch to restrict that to MP4 too?

Updated patch attached.

Comments

James Almer Nov. 21, 2016, 6:56 p.m. UTC | #1
On 11/18/2016 12:25 AM, Matthew Gregan wrote:
> At 2016-11-17T22:57:49-0300, James Almer wrote:
>>> > > @@ -345,6 +346,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = {
>>> > > +    { AV_CODEC_ID_FLAC,            MKTAG('f', 'L', 'a', 'C') }, /* nonstandard */
>> > 
>> > This is enables muxing into mov, which bypasses the experimental check below
>> > since mov isn't MODE_MP4.
> Thanks.  Updated the check to reject non-MP4 as invalid before checking the
> experimental flag.  I based the original check on the VP9/MP4 muxing, which
> has the same bug, should I send a patch to restrict that to MP4 too?
> 
> Updated patch attached.
> 
> 
> 0001-Add-experimental-muxing-support-for-FLAC-in-ISO-BMFF.patch
> 
> 
> From 6309c0c60835a843a846915c7534a61786291ad3 Mon Sep 17 00:00:00 2001
> From: Matthew Gregan <kinetik@flim.org>
> Date: Thu, 20 Oct 2016 17:28:11 +1300
> Subject: 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.

Seeing the maintainer hasn't replied, i have pushed it. Seems to adhere to
the (draft) spec correctly, and is under an experimental strict check.

I tried creating some FLAC only files and they play fine on Firefox 51 b1,
which is the first version with FLAC support. However an h264+flac file
gave me audio only and no video. Do you know if it's a bug that should be
reported to Mozilla, or just expected behavior as they only mention support
for h264+aac and no other specific AV combination?
Matthew Gregan Nov. 22, 2016, 3:02 a.m. UTC | #2
At 2016-11-21T15:56:23-0300, James Almer wrote:
> Seeing the maintainer hasn't replied, i have pushed it. Seems to adhere to
> the (draft) spec correctly, and is under an experimental strict check.

Thanks for your assistance getting this reviewed and landed, much
appreciated.

> I tried creating some FLAC only files and they play fine on Firefox 51 b1,
> which is the first version with FLAC support. However an h264+flac file
> gave me audio only and no video. Do you know if it's a bug that should be
> reported to Mozilla, or just expected behavior as they only mention support
> for h264+aac and no other specific AV combination?

That's a bug caused by missing functionality in the new Rust demuxer (which
is only used for VP9, FLAC, and Opus in MP4 for now, but the presence of any
of those track types triggers the new demuxer for the entire stream).  It'll
be fixed by https://bugzilla.mozilla.org/show_bug.cgi?id=1317609.
diff mbox

Patch

From 6309c0c60835a843a846915c7534a61786291ad3 Mon Sep 17 00:00:00 2001
From: Matthew Gregan <kinetik@flim.org>
Date: Thu, 20 Oct 2016 17:28:11 +1300
Subject: 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.

diff --git a/libavformat/isom.c b/libavformat/isom.c
index 1fa46bd..cd0b0b7 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 5452ff7..21b0050 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -33,6 +33,7 @@ 
 #include "avc.h"
 #include "libavcodec/ac3_parser.h"
 #include "libavcodec/dnxhddata.h"
+#include "libavcodec/flac.h"
 #include "libavcodec/get_bits.h"
 #include "libavcodec/put_bits.h"
 #include "libavcodec/vc1_common.h"
@@ -655,6 +656,26 @@  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)
+{
+    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 | FLAC_METADATA_TYPE_STREAMINFO); /* LastMetadataBlockFlag << 7 | BlockType */
+    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;
@@ -964,8 +985,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, track->par->bits_per_raw_sample);
+            } else {
+                avio_wb16(pb, 2);
+                avio_wb16(pb, 16);
+            }
             avio_wb16(pb, 0);
         }
 
@@ -1010,6 +1036,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);
 
@@ -1178,6 +1206,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');
@@ -5043,7 +5072,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))) {
@@ -5757,6 +5787,19 @@  static int mov_init(AVFormatContext *s)
                            i, track->par->sample_rate);
                 }
             }
+            if (track->par->codec_id == AV_CODEC_ID_FLAC) {
+                if (track->mode != MODE_MP4) {
+                    av_log(s, AV_LOG_ERROR, "FLAC only supported in MP4.\n");
+                    return AVERROR(EINVAL);
+                }
+                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