From 66061a1220bc91b216e2a59f6f779c7140a1d198 Mon Sep 17 00:00:00 2001 From: James Darnley Date: Sat, 20 Mar 2010 13:36:43 +0000 Subject: [PATCH] Add VorbisComment writing to FLAC files. Patch by James Darnley . Originally committed as revision 22605 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/Makefile | 8 +++-- libavformat/flacdec.c | 1 + libavformat/flacenc.c | 68 ++++++++++++++++++++++++++++++++++++++--- libavformat/flacenc.h | 3 +- libavformat/matroskaenc.c | 2 +- libavformat/oggdec.c | 1 + libavformat/oggdec.h | 2 -- libavformat/oggparsevorbis.c | 11 ------- libavformat/vorbiscomment.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ libavformat/vorbiscomment.h | 57 ++++++++++++++++++++++++++++++++++ tests/ref/acodec/flac | 4 +-- tests/ref/seek/flac.flac.ref | 2 +- 12 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 libavformat/vorbiscomment.c create mode 100644 libavformat/vorbiscomment.h diff --git a/libavformat/Makefile b/libavformat/Makefile index 9715908..d75f7fa 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -69,8 +69,9 @@ OBJS-$(CONFIG_FFM_MUXER) += ffmenc.o OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o raw.o id3v1.o \ - id3v2.o oggparsevorbis.o -OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o + id3v2.o \ + vorbiscomment.o +OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o vorbiscomment.o OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o @@ -148,7 +149,8 @@ OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ oggparsespeex.o \ oggparsetheora.o \ oggparsevorbis.o \ - riff.o + riff.o \ + vorbiscomment.o OBJS-$(CONFIG_OGG_MUXER) += oggenc.o OBJS-$(CONFIG_OMA_DEMUXER) += oma.o raw.o OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += raw.o diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index e8c9c87..a1bb51b 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -24,6 +24,7 @@ #include "raw.h" #include "id3v2.h" #include "oggdec.h" +#include "vorbiscomment.h" static int flac_read_header(AVFormatContext *s, AVFormatParameters *ap) diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index 81844cc..ff90731 100644 --- a/libavformat/flacenc.c +++ b/libavformat/flacenc.c @@ -22,15 +22,20 @@ #include "libavcodec/flac.h" #include "avformat.h" #include "flacenc.h" +#include "metadata.h" +#include "vorbiscomment.h" +#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,63 @@ int ff_flac_write_header(ByteIOContext *pb, AVCodecContext *codec) return 0; } +static int flac_write_block_padding(ByteIOContext *pb, unsigned int n_padding_bytes, + int last_block) +{ + put_byte(pb, last_block ? 0x81 : 0x01); + put_be24(pb, n_padding_bytes); + while (n_padding_bytes > 0) { + put_byte(pb, 0); + n_padding_bytes--; + } + return 0; +} + +static int flac_write_block_comment(ByteIOContext *pb, AVMetadata *m, + int last_block, int bitexact) +{ + const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; + unsigned int len, count; + uint8_t *p, *p0; + + len = ff_vorbiscomment_length(m, vendor, &count); + p0 = av_malloc(len+4); + if (!p0) + return AVERROR(ENOMEM); + p = p0; + + bytestream_put_byte(&p, last_block ? 0x84 : 0x04); + bytestream_put_be24(&p, len); + ff_vorbiscomment_write(&p, m, vendor, count); + + put_buffer(pb, p0, len+4); + av_freep(&p0); + 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; + AVCodecContext *codec = s->streams[0]->codec; + + ret = ff_flac_write_header(s->pb, codec, 0); + if (ret) + return ret; + + ret = flac_write_block_comment(s->pb, s->metadata, 0, + codec->flags & CODEC_FLAG_BITEXACT); + if (ret) + return ret; + + /* The command line flac encoder defaults to placing a seekpoint + * every 10s. So one might add padding to allow that later + * but there seems to be no simple way to get the duration here. + * So let's try the flac default of 8192 bytes */ + flac_write_block_padding(s->pb, 8192, 1); + + return ret; } static int flac_write_trailer(struct AVFormatContext *s) @@ -92,4 +151,5 @@ AVOutputFormat flac_muxer = { flac_write_packet, flac_write_trailer, .flags= AVFMT_NOTIMESTAMPS, + .metadata_conv = ff_vorbiscomment_metadata_conv, }; diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h index a9b7f62..748c68e 100644 --- a/libavformat/flacenc.h +++ b/libavformat/flacenc.h @@ -24,6 +24,7 @@ #include "avformat.h" -int ff_flac_write_header(ByteIOContext *pb, AVCodecContext *codec); +int ff_flac_write_header(ByteIOContext *pb, AVCodecContext *codec, + int last_block); #endif /* AVFORMAT_FLACENC_H */ diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 4263079..5204e90 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -470,7 +470,7 @@ static int mkv_write_codecprivate(AVFormatContext *s, ByteIOContext *pb, AVCodec 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) diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index d2bc085..435044f 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -33,6 +33,7 @@ #include #include "oggdec.h" #include "avformat.h" +#include "vorbiscomment.h" #define MAX_PAGE_SIZE 65307 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index 1aecd83..80ebfeb 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -112,8 +112,6 @@ extern const struct ogg_codec ff_speex_codec; extern const struct ogg_codec ff_theora_codec; extern const struct ogg_codec ff_vorbis_codec; -extern const AVMetadataConv ff_vorbiscomment_metadata_conv[]; - int ff_vorbis_comment(AVFormatContext *ms, AVMetadata **m, const uint8_t *buf, int size); static inline int diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index b032893..3bd6924 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -30,17 +30,6 @@ #include "avformat.h" #include "oggdec.h" -/** - * 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[] = { - { "ALBUMARTIST", "album_artist"}, - { "TRACKNUMBER", "track" }, - { 0 } -}; - static int ogm_chapter(AVFormatContext *as, uint8_t *key, uint8_t *val) { int i, cnum, h, m, s, ms, keylen = strlen(key); diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c new file mode 100644 index 0000000..d23c66d --- /dev/null +++ b/libavformat/vorbiscomment.c @@ -0,0 +1,73 @@ +/* + * VorbisComment writer + * Copyright (c) 2009 James Darnley + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "metadata.h" +#include "vorbiscomment.h" +#include "libavcodec/bytestream.h" + +/** + * 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[] = { + { "ALBUMARTIST", "album_artist"}, + { "TRACKNUMBER", "track" }, + { 0 } +}; + +int ff_vorbiscomment_length(AVMetadata *m, const char *vendor_string, + unsigned *count) +{ + int len = 8; + len += strlen(vendor_string); + *count = 0; + if (m) { + AVMetadataTag *tag = NULL; + while ( (tag = av_metadata_get(m, "", tag, AV_METADATA_IGNORE_SUFFIX) ) ) { + len += 4 +strlen(tag->key) + 1 + strlen(tag->value); + (*count)++; + } + } + return len; +} + +int ff_vorbiscomment_write(uint8_t **p, AVMetadata *m, + const char *vendor_string, const unsigned count) +{ + bytestream_put_le32(p, strlen(vendor_string)); + bytestream_put_buffer(p, vendor_string, strlen(vendor_string)); + if (m) { + AVMetadataTag *tag = NULL; + bytestream_put_le32(p, count); + while ( (tag = av_metadata_get(m, "", tag, AV_METADATA_IGNORE_SUFFIX) ) ) { + unsigned int len1 = strlen(tag->key); + unsigned int len2 = strlen(tag->value); + bytestream_put_le32(p, len1+1+len2); + bytestream_put_buffer(p, tag->key, len1); + bytestream_put_byte(p, '='); + bytestream_put_buffer(p, tag->value, len2); + } + } else + bytestream_put_le32(p, 0); + return 0; +} diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h new file mode 100644 index 0000000..714f1f2 --- /dev/null +++ b/libavformat/vorbiscomment.h @@ -0,0 +1,57 @@ +/* + * VorbisComment writer + * Copyright (c) 2009 James Darnley + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_VORBISCOMMENT_H +#define AVFORMAT_VORBISCOMMENT_H + +#include "avformat.h" +#include "metadata.h" + +/** + * Calculates the length in bytes of a VorbisComment. This is the minimum + * size required by ff_vorbiscomment_write(). + * + * @param m The metadata structure to be parsed. For no metadata, set to NULL. + * @param vendor_string The vendor string to be added into the VorbisComment. + * For no string, set to an empty string. + * @param count Pointer to store the number of tags in m because m->count is "not allowed" + * @return The length in bytes. + */ +int ff_vorbiscomment_length(AVMetadata *m, const char *vendor_string, + unsigned *count); + +/** + * Writes a VorbisComment into a buffer. The buffer, p, must have enough + * data to hold the whole VorbisComment. The minimum size required can be + * obtained by passing the same AVMetadata and vendor_string to + * ff_vorbiscomment_length() + * + * @param p The buffer in which to write. + * @param m The metadata struct to write. + * @param vendor_string The vendor string to write. + * @param count The number of tags in m because m->count is "not allowed" + */ +int ff_vorbiscomment_write(uint8_t **p, AVMetadata *m, + const char *vendor_string, const unsigned count); + +extern const AVMetadataConv ff_vorbiscomment_metadata_conv[]; + +#endif /* AVFORMAT_VORBISCOMMENT_H */ diff --git a/tests/ref/acodec/flac b/tests/ref/acodec/flac index ef7d394..b1e4174 100644 --- a/tests/ref/acodec/flac +++ b/tests/ref/acodec/flac @@ -1,4 +1,4 @@ -7781a016edfc242a39e4d65af02d861a *./tests/data/acodec/flac.flac -353368 ./tests/data/acodec/flac.flac +151eef9097f944726968bec48649f00a *./tests/data/acodec/flac.flac +361582 ./tests/data/acodec/flac.flac 95e54b261530a1bcf6de6fe3b21dc5f6 *./tests/data/flac.acodec.out.wav stddev: 0.00 PSNR:999.99 bytes: 1058444/ 1058444 diff --git a/tests/ref/seek/flac.flac.ref b/tests/ref/seek/flac.flac.ref index d860870..c9d6a7d 100644 --- a/tests/ref/seek/flac.flac.ref +++ b/tests/ref/seek/flac.flac.ref @@ -1,4 +1,4 @@ -ret: 0 st: 0 flags:1 dts: NOPTS pts: NOPTS pos: 42 size: 1024 +ret: 0 st: 0 flags:1 dts: NOPTS pts: NOPTS pos: 8256 size: 1024 ret:-1 st:-1 flags:0 ts:-1.000000 ret:-1 st:-1 flags:1 ts: 1.894167 ret:-1 st: 0 flags:0 ts: 0.788345 -- 2.7.4