[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