[FFmpeg-devel] [PATCH 22/28] added: metadata support to oggenc with vorbis streams (submitted upstream Issue #555)

Mans Rullgard mans
Wed Jun 30 11:09:50 CEST 2010


From: Cory Fields <theuni-nospam- at xbmc.org>

---
 libavformat/oggenc.c |  150 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 147 insertions(+), 3 deletions(-)

diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index c35b497..a0560ac 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -44,6 +44,7 @@ typedef struct {
     unsigned page_counter;
     uint8_t *header[3];
     int header_len[3];
+    int free_header; // if the header needs to be freed outside of libvorbis
     /** for theora granule */
     int kfgshift;
     int64_t last_kf_pts;
@@ -211,6 +212,139 @@ static uint8_t *ogg_write_vorbiscomment(int offset, int bitexact,
     return p0;
 }
 
+static int ogg_build_comment_header(OGGStreamContext *oggstream, AVMetadata *m) {
+    uint8_t *p = NULL;
+    uint32_t i, c;
+    size_t   size;
+
+    uint8_t  *packet_type    = NULL;
+    char     *packet_magic   = NULL;
+    uint32_t vendor_length   = 0;
+    char     *vendor_string  = NULL;
+    uint32_t list_length     = 0;
+    uint32_t *comment_length = NULL;
+    char     *comment_string = NULL;
+    uint8_t  framing_bit     = 0;
+
+    int32_t  name_size, value_size;
+    char     *name, *value;
+
+    // load the old header in if it is set
+    if (oggstream->header_len[1] > 0) {
+        p = oggstream->header[1];
+        packet_type  = (uint8_t*)p;
+        p += sizeof(uint8_t);
+        packet_magic = (char*)p;
+        p += 6;
+
+        // if the header is a valid vorbis header then read it
+        if (*packet_type == 3 && strncmp(packet_magic, "vorbis", 6) == 0) {
+            // copy the vendor so we can re-use it later
+            vendor_length = *(uint32_t*)p;
+            p += sizeof(uint32_t);
+            vendor_string = (char*)av_mallocz(vendor_length + 1);
+            if (!vendor_string)
+              return AVERROR_NOMEM;
+            memcpy(vendor_string, p, vendor_length);
+            p += vendor_length;
+
+            list_length = *(uint32_t*)p;
+            p += sizeof(uint32_t);
+
+            for(i = list_length; i > 0; --i) {
+                comment_length = (uint32_t*)p;
+                p += sizeof(uint32_t);
+
+                // copy the comment so we can use strtok on it
+                comment_string = (char*)av_mallocz(*comment_length + 1);
+                if (!comment_string) {
+                    av_free(vendor_string);
+                    return AVERROR_NOMEM;
+                }
+
+                memcpy(comment_string, p, *comment_length);
+                p += *comment_length;
+
+                //split up the comment name & value
+                name  = strtok(comment_string, "=");
+                value = name + strlen(name) + 1;
+
+                //set the name & value
+                av_metadata_set2(&m, name, value, 0);
+
+                //clean up
+                av_free(comment_string);
+            }
+
+            framing_bit = *(uint8_t*)p;
+        }
+    }
+
+    if (!vendor_length) {
+        vendor_string = (char*)av_mallocz(sizeof(LIBAVFORMAT_IDENT));
+        memcpy(vendor_string, LIBAVFORMAT_IDENT, sizeof(LIBAVFORMAT_IDENT) - 1);
+        vendor_length = sizeof(LIBAVFORMAT_IDENT) - 1;
+        framing_bit = 1;
+    }
+
+    // calculate the total header size
+    size  = 7; // packet_type + packet_magic
+    size += sizeof(uint32_t) + vendor_length;
+    size += sizeof(uint32_t); // list length
+    if (m)
+        for(c = 0; c < m->count; ++c)
+            size += sizeof(uint32_t) + strlen(m->elems[c].key) + 1 + strlen(m->elems[c].value);
+    size += 1; // framing bit
+
+    // allocate a new header
+    oggstream->header_len[1] = size;
+    oggstream->header    [1] = (uint8_t*)av_mallocz(size);
+    if (!oggstream->header[1]) {
+        oggstream->header_len[1] = 0;
+        av_free(vendor_string);
+        return AVERROR_NOMEM;
+    }
+
+    oggstream->free_header = 1;
+
+    // setup the new header
+    p = oggstream->header[1];
+    *(uint8_t*)p = 3;
+    p += sizeof(uint8_t);
+    memcpy(p, "vorbis", 6);
+    p += 6;
+
+    // write the vendor length and string
+    *(uint32_t*)p = vendor_length;
+    p += sizeof(uint32_t);
+    memcpy(p, vendor_string, vendor_length);
+    p += vendor_length;
+
+    // write the comments
+    *(uint32_t*)p = m ? m->count : 0;
+    p += sizeof(uint32_t);
+    if (m)
+        for(c = 0; c < m->count; ++c) {
+          name_size  = strlen(m->elems[c].key  );
+          value_size = strlen(m->elems[c].value);
+          *(uint32_t*)p = name_size + 1 + value_size;
+          p += sizeof(uint32_t);
+          memcpy(p, m->elems[c].key, name_size);
+          p += name_size;
+          *(char*)p = '=';
+          ++p;
+          memcpy(p, m->elems[c].value, value_size);
+          p += value_size;
+        }
+
+    // write the framing bit
+    *(uint8_t*)p = framing_bit;
+
+    // clean up and return success
+    av_free(vendor_string);
+    return 0;
+}
+
 static int ogg_build_flac_headers(AVCodecContext *avctx,
                                   OGGStreamContext *oggstream, int bitexact,
                                   AVMetadata *m)
@@ -281,7 +415,7 @@ static int ogg_build_speex_headers(AVCodecContext *avctx,
 static int ogg_write_header(AVFormatContext *s)
 {
     OGGStreamContext *oggstream;
-    int i, j;
+    int i, j, err;
 
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
@@ -319,7 +453,7 @@ static int ogg_write_header(AVFormatContext *s)
 
         st->priv_data = oggstream;
         if (st->codec->codec_id == CODEC_ID_FLAC) {
-            int err = ogg_build_flac_headers(st->codec, oggstream,
+            err = ogg_build_flac_headers(st->codec, oggstream,
                                              st->codec->flags & CODEC_FLAG_BITEXACT,
                                              s->metadata);
             if (err) {
@@ -328,7 +462,7 @@ static int ogg_write_header(AVFormatContext *s)
                 return err;
             }
         } else if (st->codec->codec_id == CODEC_ID_SPEEX) {
-            int err = ogg_build_speex_headers(st->codec, oggstream,
+            err = ogg_build_speex_headers(st->codec, oggstream,
                                               st->codec->flags & CODEC_FLAG_BITEXACT,
                                               s->metadata);
             if (err) {
@@ -352,6 +486,13 @@ static int ogg_write_header(AVFormatContext *s)
                 av_log(s, AV_LOG_DEBUG, "theora kfgshift %d, vrev %d\n",
                        oggstream->kfgshift, oggstream->vrev);
             }
+
+            err = ogg_build_comment_header(oggstream, s->metadata);
+            if (err) {
+                av_log(s, AV_LOG_ERROR, "Error writing Vorbis comment header\n");
+                av_freep(&st->priv_data);
+                return err;
+            }
         }
     }
 
@@ -441,6 +582,9 @@ static int ogg_write_trailer(AVFormatContext *s)
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
         OGGStreamContext *oggstream = st->priv_data;
+        if (oggstream->free_header)
+            av_free(oggstream->header[1]);
+        else
         if (st->codec->codec_id == CODEC_ID_FLAC ||
             st->codec->codec_id == CODEC_ID_SPEEX) {
             av_free(oggstream->header[0]);
-- 
1.7.1.1




More information about the ffmpeg-devel mailing list