From 59c6178a54c414fd19e064f0077d00b82a1eb812 Mon Sep 17 00:00:00 2001
From: Justin Ruggles <justin.ruggles@gmail.com>
Date: Thu, 26 Feb 2009 02:29:24 +0000
Subject: [PATCH] Use a shared function to validate FLAC extradata.

Originally committed as revision 17602 to svn://svn.ffmpeg.org/ffmpeg/trunk
---
 libavcodec/Makefile       |  7 +++---
 libavcodec/flac.h         | 16 +++++++++++++
 libavcodec/flacdec.c      | 47 +++++++++++++++++++++++++++++++--------
 libavformat/flacenc.c     | 25 ++++++++++++++++-----
 libavformat/matroskaenc.c | 16 ++++++-------
 libavformat/oggenc.c      | 11 +++++----
 6 files changed, 91 insertions(+), 31 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index afb1ede3fe..afa5fac152 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -345,15 +345,16 @@ OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER)       += adpcm.o
 
 # libavformat dependencies
 OBJS-$(CONFIG_EAC3_DEMUXER)            += ac3_parser.o ac3tab.o aac_ac3_parser.o
+OBJS-$(CONFIG_FLAC_MUXER)              += flacdec.o
 OBJS-$(CONFIG_GXF_DEMUXER)             += mpeg12data.o
-OBJS-$(CONFIG_MATROSKA_AUDIO_MUXER)    += xiph.o mpeg4audio.o
+OBJS-$(CONFIG_MATROSKA_AUDIO_MUXER)    += xiph.o mpeg4audio.o flacdec.o
 OBJS-$(CONFIG_MATROSKA_DEMUXER)        += mpeg4audio.o
-OBJS-$(CONFIG_MATROSKA_MUXER)          += xiph.o mpeg4audio.o
+OBJS-$(CONFIG_MATROSKA_MUXER)          += xiph.o mpeg4audio.o flacdec.o
 OBJS-$(CONFIG_MOV_DEMUXER)             += mpeg4audio.o mpegaudiodata.o
 OBJS-$(CONFIG_MPEGTS_MUXER)            += mpegvideo.o
 OBJS-$(CONFIG_NUT_MUXER)               += mpegaudiodata.o
 OBJS-$(CONFIG_OGG_DEMUXER)             += flacdec.o
-OBJS-$(CONFIG_OGG_MUXER)               += xiph.o
+OBJS-$(CONFIG_OGG_MUXER)               += xiph.o flacdec.o
 OBJS-$(CONFIG_RTP_MUXER)               += mpegvideo.o
 
 # external codec libraries
diff --git a/libavcodec/flac.h b/libavcodec/flac.h
index 9a4f820831..8af79f2bd8 100644
--- a/libavcodec/flac.h
+++ b/libavcodec/flac.h
@@ -42,6 +42,11 @@ enum {
     FLAC_METADATA_TYPE_INVALID = 127
 };
 
+enum FLACExtradataFormat {
+    FLAC_EXTRADATA_FORMAT_STREAMINFO  = 0,
+    FLAC_EXTRADATA_FORMAT_FULL_HEADER = 1
+};
+
 /**
  * Data needed from the Streaminfo header for use by the raw FLAC demuxer
  * and/or the FLAC decoder.
@@ -68,4 +73,15 @@ typedef struct FLACStreaminfo {
 void ff_flac_parse_streaminfo(AVCodecContext *avctx, struct FLACStreaminfo *s,
                               const uint8_t *buffer);
 
+/**
+ * Validate the FLAC extradata.
+ * @param[in]  avctx codec context containing the extradata.
+ * @param[out] format extradata format.
+ * @param[out] streaminfo_start pointer to start of 34-byte STREAMINFO data.
+ * @return 1 if valid, 0 if not valid.
+ */
+int ff_flac_is_extradata_valid(AVCodecContext *avctx,
+                               enum FLACExtradataFormat *format,
+                               uint8_t **streaminfo_start);
+
 #endif /* AVCODEC_FLAC_H */
diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c
index d6e0c8620f..42310b24a0 100644
--- a/libavcodec/flacdec.c
+++ b/libavcodec/flacdec.c
@@ -96,26 +96,55 @@ static int64_t get_utf8(GetBitContext *gb)
 }
 
 static void allocate_buffers(FLACContext *s);
-static int metadata_parse(FLACContext *s);
+
+int ff_flac_is_extradata_valid(AVCodecContext *avctx,
+                               enum FLACExtradataFormat *format,
+                               uint8_t **streaminfo_start)
+{
+    if (!avctx->extradata || avctx->extradata_size < FLAC_STREAMINFO_SIZE) {
+        av_log(avctx, AV_LOG_ERROR, "extradata NULL or too small.\n");
+        return 0;
+    }
+    if (AV_RL32(avctx->extradata) != MKTAG('f','L','a','C')) {
+        /* extradata contains STREAMINFO only */
+        if (avctx->extradata_size != FLAC_STREAMINFO_SIZE) {
+            av_log(avctx, AV_LOG_WARNING, "extradata contains %d bytes too many.\n",
+                   FLAC_STREAMINFO_SIZE-avctx->extradata_size);
+        }
+        *format = FLAC_EXTRADATA_FORMAT_STREAMINFO;
+        *streaminfo_start = avctx->extradata;
+    } else {
+        if (avctx->extradata_size < 8+FLAC_STREAMINFO_SIZE) {
+            av_log(avctx, AV_LOG_ERROR, "extradata too small.\n");
+            return 0;
+        }
+        *format = FLAC_EXTRADATA_FORMAT_FULL_HEADER;
+        *streaminfo_start = &avctx->extradata[8];
+    }
+    return 1;
+}
 
 static av_cold int flac_decode_init(AVCodecContext *avctx)
 {
+    enum FLACExtradataFormat format;
+    uint8_t *streaminfo;
     FLACContext *s = avctx->priv_data;
     s->avctx = avctx;
 
     avctx->sample_fmt = SAMPLE_FMT_S16;
 
-    if (avctx->extradata_size > 4) {
+    /* for now, the raw FLAC header is allowed to be passed to the decoder as
+       frame data instead of extradata. */
+    if (!avctx->extradata)
+        return 0;
+
+    if (!ff_flac_is_extradata_valid(avctx, &format, &streaminfo))
+        return -1;
+
         /* initialize based on the demuxer-supplied streamdata header */
-        if (avctx->extradata_size == FLAC_STREAMINFO_SIZE) {
             ff_flac_parse_streaminfo(avctx, (FLACStreaminfo *)s,
-                                     avctx->extradata);
+                                     streaminfo);
             allocate_buffers(s);
-        } else {
-            init_get_bits(&s->gb, avctx->extradata, avctx->extradata_size*8);
-            metadata_parse(s);
-        }
-    }
 
     return 0;
 }
diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c
index 093a07a823..5595d76b7d 100644
--- a/libavformat/flacenc.c
+++ b/libavformat/flacenc.c
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavcodec/flac.h"
 #include "avformat.h"
 
 static int flac_write_header(struct AVFormatContext *s)
@@ -26,9 +27,15 @@ static int flac_write_header(struct AVFormatContext *s)
     static const uint8_t header[8] = {
         0x66, 0x4C, 0x61, 0x43, 0x80, 0x00, 0x00, 0x22
     };
-    uint8_t *streaminfo = s->streams[0]->codec->extradata;
+    AVCodecContext *codec = s->streams[0]->codec;
+    uint8_t *streaminfo;
     int len = s->streams[0]->codec->extradata_size;
-    if(streaminfo != NULL && len > 0) {
+    enum FLACExtradataFormat format;
+
+    if (!ff_flac_is_extradata_valid(codec, &format, &streaminfo))
+        return -1;
+
+    if (format == FLAC_EXTRADATA_FORMAT_STREAMINFO) {
         put_buffer(s->pb, header, 8);
         put_buffer(s->pb, streaminfo, len);
     }
@@ -38,16 +45,22 @@ static int flac_write_header(struct AVFormatContext *s)
 static int flac_write_trailer(struct AVFormatContext *s)
 {
     ByteIOContext *pb = s->pb;
-    uint8_t *streaminfo = s->streams[0]->codec->extradata;
-    int len = s->streams[0]->codec->extradata_size;
+    uint8_t *streaminfo;
+    enum FLACExtradataFormat format;
     int64_t file_size;
 
-    if (streaminfo && len > 0 && !url_is_streamed(s->pb)) {
+    if (!ff_flac_is_extradata_valid(s->streams[0]->codec, &format, &streaminfo))
+        return -1;
+
+    if (!url_is_streamed(pb)) {
+        /* rewrite the STREAMINFO header block data */
         file_size = url_ftell(pb);
         url_fseek(pb, 8, SEEK_SET);
-        put_buffer(pb, streaminfo, len);
+        put_buffer(pb, streaminfo, FLAC_STREAMINFO_SIZE);
         url_fseek(pb, file_size, SEEK_SET);
         put_flush_packet(pb);
+    } else {
+        av_log(s, AV_LOG_WARNING, "unable to rewrite FLAC header.\n");
     }
     return 0;
 }
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 44787be2d2..0b0adef1ba 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -28,6 +28,7 @@
 #include "libavutil/md5.h"
 #include "libavcodec/xiph.h"
 #include "libavcodec/mpeg4audio.h"
+#include "libavcodec/flac.h"
 
 typedef struct ebml_master {
     int64_t         pos;                ///< absolute offset in the file where the master's elements start
@@ -420,23 +421,20 @@ static int put_xiph_codecpriv(AVFormatContext *s, ByteIOContext *pb, AVCodecCont
     return 0;
 }
 
-#define FLAC_STREAMINFO_SIZE 34
-
 static int put_flac_codecpriv(AVFormatContext *s, ByteIOContext *pb, AVCodecContext *codec)
 {
-    // if the extradata_size is greater than FLAC_STREAMINFO_SIZE,
-    // assume that it's in Matroska format already
-    if (codec->extradata_size < FLAC_STREAMINFO_SIZE) {
+    uint8_t *streaminfo;
+    enum FLACExtradataFormat format;
+
+    if (!ff_flac_is_extradata_valid(codec, &format, &streaminfo)) {
         av_log(s, AV_LOG_ERROR, "Invalid FLAC extradata\n");
         return -1;
-    } else if (codec->extradata_size == FLAC_STREAMINFO_SIZE) {
+    }
+    if (format == FLAC_EXTRADATA_FORMAT_STREAMINFO) {
         // only the streaminfo packet
         put_buffer(pb, "fLaC", 4);
         put_byte(pb, 0x80);
         put_be24(pb, FLAC_STREAMINFO_SIZE);
-    } else if(memcmp("fLaC", codec->extradata, 4)) {
-        av_log(s, AV_LOG_ERROR, "Invalid FLAC extradata\n");
-        return -1;
     }
     put_buffer(pb, codec->extradata, codec->extradata_size);
     return 0;
diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index 4fdae05c8f..bbbee0a906 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -22,6 +22,7 @@
 #include "libavutil/crc.h"
 #include "libavcodec/xiph.h"
 #include "libavcodec/bytestream.h"
+#include "libavcodec/flac.h"
 #include "avformat.h"
 #include "internal.h"
 
@@ -82,12 +83,14 @@ static int ogg_write_page(AVFormatContext *s, const uint8_t *data, int size,
     return size;
 }
 
-static int ogg_build_flac_headers(const uint8_t *extradata, int extradata_size,
+static int ogg_build_flac_headers(AVCodecContext *avctx,
                                   OGGStreamContext *oggstream, int bitexact)
 {
     const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
+    enum FLACExtradataFormat format;
+    uint8_t *streaminfo;
     uint8_t *p;
-    if (extradata_size != 34)
+    if (!ff_flac_is_extradata_valid(avctx, &format, &streaminfo))
         return -1;
     oggstream->header_len[0] = 51;
     oggstream->header[0] = av_mallocz(51); // per ogg flac specs
@@ -100,7 +103,7 @@ static int ogg_build_flac_headers(const uint8_t *extradata, int extradata_size,
     bytestream_put_buffer(&p, "fLaC", 4);
     bytestream_put_byte(&p, 0x00); // streaminfo
     bytestream_put_be24(&p, 34);
-    bytestream_put_buffer(&p, extradata, 34);
+    bytestream_put_buffer(&p, streaminfo, FLAC_STREAMINFO_SIZE);
     oggstream->header_len[1] = 1+3+4+strlen(vendor)+4;
     oggstream->header[1] = av_mallocz(oggstream->header_len[1]);
     p = oggstream->header[1];
@@ -136,7 +139,7 @@ static int ogg_write_header(AVFormatContext *s)
         oggstream = av_mallocz(sizeof(*oggstream));
         st->priv_data = oggstream;
         if (st->codec->codec_id == CODEC_ID_FLAC) {
-            if (ogg_build_flac_headers(st->codec->extradata, st->codec->extradata_size,
+            if (ogg_build_flac_headers(st->codec,
                                        oggstream, st->codec->flags & CODEC_FLAG_BITEXACT) < 0) {
                 av_log(s, AV_LOG_ERROR, "Extradata corrupted\n");
                 av_freep(&st->priv_data);