[FFmpeg-devel] [PATCH] Add writing of vorbis comments to flac files
Justin Ruggles
justin.ruggles
Wed Oct 21 00:17:03 CEST 2009
James Darnley wrote:
> Hello,
> I have attached a patch which adds the creation of vorbis comments
> from AVMetadata and the writing of this as a
> METADATA_BLOCK_VORBIS_COMMENT in a flac file.
Thank you for sending the patch.
> Could someone please double check libavformat/makefile and the
> #includes on the files to eliminate any redundant options or to add
> any I have missed.
Although the VorbisComment reading function should probably go into
vorbiscomment.c eventually (or now?), which might simplify the includes
and the Makefile.
> I do have a question or two.
> Is it possible to determine the length of the audio from the
> AVFormatContext struct? I tried a couple of "duration" values but
> they always seemed to be 0.
I don't think this is possible at the muxer level.
> If that is not possible, is there a value in AVFormatContext which
> could be interpreted as a header padding value, a value which might
> request N bytes of padding?
Not currently.
> I ask because there is also the possibility of writing a
> METADATA_BLOCK_PADDING to allow extra metadata or possibly a seek
> table to be added later without the need to rewrite the whole file.
An imperfect alternative might be to reserve space for a fixed number of
seek table entries and choose the interval at the end based on the final
duration. But that can be a separate patch.
> Thanks for any comments.
Please update the regression tests.
>
> Index: libavformat/oggenc.c
> ===================================================================
> --- libavformat/oggenc.c (revision 20332)
> +++ libavformat/oggenc.c (working copy)
> @@ -25,6 +25,7 @@
> #include "libavcodec/flac.h"
> #include "avformat.h"
> #include "internal.h"
> +#include "vorbiscomment.h"
>
> typedef struct {
> int64_t duration;
> @@ -357,4 +358,5 @@
> ogg_write_packet,
> ogg_write_trailer,
> .interleave_packet = ogg_interleave_per_granule,
> + .metadata_conv = ff_vorbiscomment_metadata_conv,
> };
This change would only be needed if VorbisComment writing is added to
the ogg muxer. Which leads to the question... Why is that not done?
> Index: libavformat/matroskaenc.c
> ===================================================================
> --- libavformat/matroskaenc.c (revision 20332)
> +++ libavformat/matroskaenc.c (working copy)
> @@ -462,7 +462,7 @@
> if (codec->codec_id == CODEC_ID_VORBIS || codec->codec_id == CODEC_ID_THEORA)
> ret = put_xiph_codecpriv(s, dyn_cp, codec);
> else if (codec->codec_id == CODEC_ID_FLAC)
> - ret = ff_flac_write_header(dyn_cp, codec);
> + ret = ff_flac_write_header(dyn_cp, codec, 1);
> else if (codec->codec_id == CODEC_ID_H264)
> ret = ff_isom_write_avcc(dyn_cp, codec->extradata, codec->extradata_size);
> else if (codec->extradata_size)
This looks ok.
> Index: libavformat/oggdec.c
> ===================================================================
> --- libavformat/oggdec.c (revision 20332)
> +++ libavformat/oggdec.c (working copy)
> @@ -33,6 +33,7 @@
> #include <stdio.h>
> #include "oggdec.h"
> #include "avformat.h"
> +#include "vorbiscomment.h"
>
> #define MAX_PAGE_SIZE 65307
> #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE
> Index: libavformat/oggparsevorbis.c
> ===================================================================
> --- libavformat/oggparsevorbis.c (revision 20332)
> +++ libavformat/oggparsevorbis.c (working copy)
> @@ -29,18 +29,13 @@
> #include "libavcodec/bytestream.h"
> #include "avformat.h"
> #include "oggdec.h"
> +#include "vorbiscomment.h"
why is this #include needed?
>
> /**
> * VorbisComment metadata conversion mapping.
> * from Ogg Vorbis I format specification: comment field and header specification
> * http://xiph.org/vorbis/doc/v-comment.html
> */
> -const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
> - { "ARTIST" , "author" },
> - { "DATE" , "year" },
> - { "TRACKNUMBER", "track" },
> - { 0 }
> -};
You should move the documentation along with the table.
> Index: libavformat/flacenc.c
> ===================================================================
> --- libavformat/flacenc.c (revision 20332)
> +++ libavformat/flacenc.c (working copy)
> @@ -22,15 +22,20 @@
> #include "libavcodec/flac.h"
> #include "avformat.h"
> #include "flacenc.h"
> +#include "metadata.h"
> +#include "vorbiscomment.h"
> +#include "libavcodec/avcodec.h"
you don't need to include avcodec.h explicitly.
> +#include "libavcodec/bytestream.h"
>
> -int ff_flac_write_header(ByteIOContext *pb, AVCodecContext *codec)
> +int ff_flac_write_header(ByteIOContext *pb, AVCodecContext *codec, int last_block)
> {
> - static const uint8_t header[8] = {
> - 0x66, 0x4C, 0x61, 0x43, 0x80, 0x00, 0x00, 0x22
> + uint8_t header[8] = {
> + 0x66, 0x4C, 0x61, 0x43, 0x00, 0x00, 0x00, 0x22
> };
> uint8_t *streaminfo;
> enum FLACExtradataFormat format;
>
> + header[4] = (last_block)?0x80:0x00;
> if (!ff_flac_is_extradata_valid(codec, &format, &streaminfo))
> return -1;
>
> @@ -45,9 +50,58 @@
> return 0;
> }
This change looks ok.
>
> +int flac_write_block_padding(ByteIOContext *pb, unsigned int n_padding_bytes, int last_block)
> +{
> + put_byte(pb, (last_block)?0x81:0x01);
unneeded parentheses around last_block
> + put_be24(pb, n_padding_bytes);
> + while(n_padding_bytes>0)
> + {
> + put_byte(pb, 0);
> + n_padding_bytes--;
> + }
> + return 0;
> +}
> +
> +int flac_write_block_comment(ByteIOContext *pb, AVMetadata *m, int last_block, int bitexact)
> +{
> + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
> + unsigned int len;
> + uint8_t *p, *p0;
> +
> + len = ff_vorbis_comment_length(m, vendor);
> + p0 = av_mallocz(len+4);
> + if (!p0)
> + return -1;
> + p = p0;
> +
> + bytestream_put_byte(&p, (last_block)?0x84:0x04);
unnecessary parentheses.
> + bytestream_put_be24(&p, len);
> + ff_vorbis_comment_write(&p, m, vendor);
> +
> + put_buffer(pb, p0, len+4);
> + //av_freep(p0);
is this supposed to be commented-out?
> + p = NULL;
> +
> + return 0;
> +}
> +
> static int flac_write_header(struct AVFormatContext *s)
> {
> - return ff_flac_write_header(s->pb, s->streams[0]->codec);
> + int ret;
> + unsigned int dur = ((s->duration)); // * s->streams[0]->time_base.den) / s->streams[0]->time_base.num);
> + /* seems to be zero in my testing */
> + av_log(NULL, AV_LOG_ERROR, "[flac tags] duration=%d\n", dur);
as you mentioned, this doesn't work.
> + ret = ff_flac_write_header(s->pb, s->streams[0]->codec, 0);
> + if (ret)
> + return ret;
> + if (dur>0)
> + {
> + flac_write_block_comment(s->pb, s->metadata, 0, s->streams[0]->codec->flags & CODEC_FLAG_BITEXACT);
> + flac_write_block_padding(s->pb, (dur/10)*18, 1);
The default padding for the reference flac encoder is 8192 bytes. That
would allow for a pretty large seek table if needed.
> Index: libavformat/vorbiscomment.c
> ===================================================================
> --- libavformat/vorbiscomment.c (revision 0)
> +++ libavformat/vorbiscomment.c (revision 0)
> @@ -0,0 +1,48 @@
> +#include "avformat.h"
> +#include "metadata.h"
> +#include "vorbiscomment.h"
> +#include "libavcodec/bytestream.h"
> +
> +const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
> + { "artist" , "author" },
> + { "date" , "year" },
> + { "tracknumber", "track" },
> + { 0 }
> +};
as mentioned above, move the documentation for this too. I think it
could probably just be put in the header. And don't change the case of
the key names if you're just moving the table; copy it as-is.
> +
> +int ff_vorbis_comment_length(AVMetadata *m, const char *vendor_string)
document this.
> +{
> + int len = 8;
> + int i;
> + len += strlen(vendor_string);
> + if (m)
> + {
> + len += m->count * 4;
> + for (i=0; i<m->count; i++)
> + len += strlen(m->elems[i].key) + 1 + strlen(m->elems[i].value);
> + }
> + return len;
> +}
this looks ok.
> +
> +int ff_vorbis_comment_write(uint8_t **p, AVMetadata *m, const char *vendor_string)
you should probably document this. especially the part about the amount
of data required for p.
> +{
> + unsigned int len1, len2;
> + int i;
> + bytestream_put_le32(p, strlen(vendor_string));
> + bytestream_put_buffer(p, vendor_string, strlen(vendor_string));
> + if (m)
> + {
> + bytestream_put_le32(p, m->count);
> + for (i=0; i<m->count; i++)
> + {
> + len1 = strlen(m->elems[i].key);
> + len2 = strlen(m->elems[i].value);
len1 and len2 can be declared inside the loop.
> + bytestream_put_le32(p, len1+1+len2);
> + bytestream_put_buffer(p, m->elems[i].key, len1);
> + bytestream_put_byte(p, '=');
> + bytestream_put_buffer(p, m->elems[i].value, len2);
> + }
> + } else
> + bytestream_put_le32(p, 0);
> + return 0;
> +}
> Index: libavformat/vorbiscomment.h
> ===================================================================
> --- libavformat/vorbiscomment.h (revision 0)
> +++ libavformat/vorbiscomment.h (revision 0)
> @@ -0,0 +1,7 @@
> +#include "avformat.h"
> +#include "metadata.h"
> +
> +int ff_vorbis_comment_length(AVMetadata *m, const char *vendor_string);
> +int ff_vorbis_comment_write(uint8_t **p, AVMetadata *m, const char *vendor_string);
> +
> +extern const AVMetadataConv ff_vorbiscomment_metadata_conv[];
as Diego mentioned, license header and inclusion guards.
-Justin
More information about the ffmpeg-devel
mailing list