[PATCH] detect where trailing metadata begins in mp3 files so =
David Byron none
dbyron
Mon Sep 27 10:29:03 CEST 2010
it's=0A=
not returned from av_read_frame=0A=
=0A=
---=0A=
Changelog | 2 +=0A=
libavcodec/mpegaudio.h | 4 +=0A=
libavcodec/mpegaudio_parser.c | 30 ++++++++-=0A=
libavformat/mp3.c | 135 =
+++++++++++++++++++++++++++++++++++++++++=0A=
4 files changed, 167 insertions(+), 4 deletions(-)=0A=
=0A=
diff --git a/Changelog b/Changelog=0A=
index a76cec1..289466d 100644=0A=
--- a/Changelog=0A=
+++ b/Changelog=0A=
@@ -4,6 +4,8 @@ releases are sorted from youngest to oldest.=0A=
=0A=
version <next>:=0A=
=0A=
+- detect where trailing metadata begins in mp3 files so it's not=0A=
+ returned from av_read_frame=0A=
- WebM support in Matroska de/muxer=0A=
- low overhead Ogg muxing=0A=
- MMS-TCP support=0A=
diff --git a/libavcodec/mpegaudio.h b/libavcodec/mpegaudio.h=0A=
index e2ad911..a838cc8 100644=0A=
--- a/libavcodec/mpegaudio.h=0A=
+++ b/libavcodec/mpegaudio.h=0A=
@@ -190,6 +190,10 @@ void ff_mpa_synth_filter_float(MPADecodeContext *s,=0A=
void ff_mpegaudiodec_init_mmx(MPADecodeContext *s);=0A=
void ff_mpegaudiodec_init_altivec(MPADecodeContext *s);=0A=
=0A=
+struct MpegAudioParseContext;=0A=
+void ff_mpegaudio_parse_set_trailing_metadata_offset(struct =
MpegAudioParseContext *s,=0A=
+ int64_t =
trailing_metadata_offset);=0A=
+=0A=
/* fast header check for resync */=0A=
static inline int ff_mpa_check_header(uint32_t header){=0A=
/* header */=0A=
diff --git a/libavcodec/mpegaudio_parser.c =
b/libavcodec/mpegaudio_parser.c=0A=
index 6d7ab8a..84a8cb8 100644=0A=
--- a/libavcodec/mpegaudio_parser.c=0A=
+++ b/libavcodec/mpegaudio_parser.c=0A=
@@ -25,12 +25,17 @@=0A=
#include "mpegaudiodecheader.h"=0A=
=0A=
=0A=
-typedef struct MpegAudioParseContext {=0A=
+struct MpegAudioParseContext {=0A=
ParseContext pc;=0A=
int frame_size;=0A=
uint32_t header;=0A=
int header_count;=0A=
-} MpegAudioParseContext;=0A=
+ /**=0A=
+ * -1 if no trailing metadata is detected, otherwise the=0A=
+ * offset where trailing metadata begins=0A=
+ */=0A=
+ int64_t trailing_metadata_offset;=0A=
+};=0A=
=0A=
#define MPA_HEADER_SIZE 4=0A=
=0A=
@@ -83,12 +88,21 @@ static int mpegaudio_parse(AVCodecParserContext *s1,=0A=
const uint8_t **poutbuf, int *poutbuf_size,=0A=
const uint8_t *buf, int buf_size)=0A=
{=0A=
- MpegAudioParseContext *s =3D s1->priv_data;=0A=
+ struct MpegAudioParseContext *s =3D s1->priv_data;=0A=
ParseContext *pc =3D &s->pc;=0A=
uint32_t state=3D pc->state;=0A=
int i;=0A=
int next=3D END_NOT_FOUND;=0A=
=0A=
+ if ((s->trailing_metadata_offset !=3D -1) &&=0A=
+ (s1->cur_offset >=3D s->trailing_metadata_offset)) {=0A=
+ av_log(avctx, AV_LOG_DEBUG, "%s: giving up at offset: %" PRId64=0A=
+ " due to trailing metadata\n", =
__FUNCTION__,s1->cur_offset);=0A=
+ *poutbuf =3D NULL;=0A=
+ *poutbuf_size =3D 0;=0A=
+ return buf_size;=0A=
+ }=0A=
+=0A=
for(i=3D0; i<buf_size; ){=0A=
if(s->frame_size){=0A=
int inc=3D FFMIN(buf_size - i, s->frame_size);=0A=
@@ -139,10 +153,18 @@ static int mpegaudio_parse(AVCodecParserContext =
*s1,=0A=
return next;=0A=
}=0A=
=0A=
+void=0A=
+ff_mpegaudio_parse_set_trailing_metadata_offset(struct =
MpegAudioParseContext *s,=0A=
+ int64_t =
trailing_metadata_offset)=0A=
+{=0A=
+ assert(s);=0A=
+ s->trailing_metadata_offset =3D trailing_metadata_offset;=0A=
+}=0A=
+=0A=
=0A=
AVCodecParser mpegaudio_parser =3D {=0A=
{ CODEC_ID_MP1, CODEC_ID_MP2, CODEC_ID_MP3 },=0A=
- sizeof(MpegAudioParseContext),=0A=
+ sizeof(struct MpegAudioParseContext),=0A=
NULL,=0A=
mpegaudio_parse,=0A=
ff_parse_close,=0A=
diff --git a/libavformat/mp3.c b/libavformat/mp3.c=0A=
index c1622a3..a2191b8 100644=0A=
--- a/libavformat/mp3.c=0A=
+++ b/libavformat/mp3.c=0A=
@@ -19,9 +19,12 @@=0A=
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA =
02110-1301 USA=0A=
*/=0A=
=0A=
+#include <assert.h>=0A=
#include <strings.h>=0A=
#include "libavutil/avstring.h"=0A=
#include "libavutil/intreadwrite.h"=0A=
+#include "libavcodec/mpegaudio.h"=0A=
+#include "apetag.h"=0A=
#include "avformat.h"=0A=
#include "id3v2.h"=0A=
#include "id3v1.h"=0A=
@@ -33,6 +36,117 @@=0A=
=0A=
/* mp3 read */=0A=
=0A=
+/**=0A=
+ * Find the offset where trailing metadata begins. If there=0A=
+ * are multiple items of trailing metadata (e.g. APE +=0A=
+ * lyrics3 + id3v1), this function finds the offset of the=0A=
+ * beginning of them all. If there is space between the=0A=
+ * items, this function doesn't look before the space.=0A=
+ *=0A=
+ * Assume that the current offset of s->pb is the first byte=0A=
+ * of audio data. We need this since the offsets we have=0A=
+ * available when parsing packets is relative to the=0A=
+ * beginning of the audio.=0A=
+ *=0A=
+ * @retval 0 successfully populated s->priv_data (an=0A=
+ * MP3Context)->trailing_metadata_offset with the offset of=0A=
+ * trailing metadata (-1 if there is none)=0A=
+ */=0A=
+static int ff_mp3_find_trailing_metadata(AVFormatContext *s,=0A=
+ AVStream *st)=0A=
+{=0A=
+ int64_t ape_offset;=0A=
+ uint64_t ape_size;=0A=
+ int64_t id3v1_offset;=0A=
+ int64_t trailing_offset;=0A=
+ int ret;=0A=
+ struct MpegAudioParseContext *mpeg_parse_ctxt;=0A=
+ int64_t audio_begin;=0A=
+=0A=
+ assert(s);=0A=
+ assert(st);=0A=
+=0A=
+ audio_begin =3D url_ftell(s->pb);=0A=
+ if (audio_begin < 0)=0A=
+ return audio_begin;=0A=
+=0A=
+ /* MpegAudioParseContext is the right type because=0A=
+ avcodec/mpegaudio_parse.c defines mpegaudio_parser=0A=
+ with codec_ids containing CODEC_ID_MP3 and=0A=
+ priv_data_size to sizeof(MpegAudioParseContext). As=0A=
+ well, mp3_read_header sets st->codec->codec_id to=0A=
+ CODEC_ID_MP3. */=0A=
+ assert(st->parser);=0A=
+ mpeg_parse_ctxt =3D st->parser->priv_data;=0A=
+ assert(mpeg_parse_ctxt);=0A=
+=0A=
+ trailing_offset =3D -1;=0A=
+=0A=
+ /* First look for the id3v1 tag at the end */=0A=
+ ret =3D ff_id3v1_offset(s,&id3v1_offset);=0A=
+ if (ret < 0)=0A=
+ goto done;=0A=
+=0A=
+ if (id3v1_offset !=3D -1)=0A=
+ av_log(s, AV_LOG_VERBOSE, "%s: \"%s\": id3v1 tag at offset %"=0A=
+ PRId64 "(0x%" PRIx64 ")\n", __FUNCTION__, s->filename,=0A=
+ id3v1_offset, id3v1_offset);=0A=
+=0A=
+ trailing_offset =3D id3v1_offset;=0A=
+=0A=
+ /* Because of the way ff_id3v1_offset works, we don't=0A=
+ need to make sure the id3v1 tag is at the very end of=0A=
+ the file. */=0A=
+=0A=
+ /* Now look for an APE tag. Pass 0 because we don't=0A=
+ care how many fields it's got. */=0A=
+ ret =3D ff_ape_offset(s,id3v1_offset,&ape_offset,&ape_size,0);=0A=
+ if (ret < 0)=0A=
+ goto done;=0A=
+=0A=
+ assert((id3v1_offset =3D=3D -1) || (ape_offset < id3v1_offset));=0A=
+=0A=
+ if (ape_offset !=3D -1) {=0A=
+ if (id3v1_offset !=3D -1) {=0A=
+ /* If the APE tag isn't immediately before the=0A=
+ id3v1 tag, we're done */=0A=
+ if ((ape_offset + ape_size) !=3D id3v1_offset) {=0A=
+ av_log(s, AV_LOG_WARNING, "%s: \"%s\": gap between end =
of "=0A=
+ "APE tag and start of id3v1 tag", __FUNCTION__,=0A=
+ s->filename);=0A=
+=0A=
+ /* Just give the offset of the id3v1 tag so=0A=
+ we don't ditch the info between the=0A=
+ tags */=0A=
+ trailing_offset =3D id3v1_offset;=0A=
+ goto done;=0A=
+ }=0A=
+ }=0A=
+=0A=
+ trailing_offset =3D ape_offset;=0A=
+ }=0A=
+=0A=
+ done:=0A=
+ if (audio_begin > trailing_offset) {=0A=
+ av_log(s, AV_LOG_ERROR, "%s: \"%s\": beginning of audio is =
after "=0A=
+ "trailing metadata offset -- something's wrong\n",=0A=
+ __FUNCTION__, s->filename);=0A=
+ return -1;=0A=
+ }=0A=
+=0A=
+ trailing_offset -=3D audio_begin;=0A=
+=0A=
+ av_log(s, AV_LOG_DEBUG, "%s: \"%s\": trailing metadata offset =
relative "=0A=
+ "to audio: %" PRId64 "\n", __FUNCTION__, s->filename,=0A=
+ trailing_offset);=0A=
+=0A=
+ ff_mpegaudio_parse_set_trailing_metadata_offset(mpeg_parse_ctxt,=0A=
+ trailing_offset);=0A=
+ url_fseek(s->pb, 0, SEEK_SET);=0A=
+=0A=
+ return ret;=0A=
+}=0A=
+=0A=
static int mp3_read_probe(AVProbeData *p)=0A=
{=0A=
int max_frames, first_frames =3D 0;=0A=
@@ -143,6 +257,7 @@ static int mp3_read_header(AVFormatContext *s,=0A=
{=0A=
AVStream *st;=0A=
int64_t off;=0A=
+ int ret;=0A=
=0A=
st =3D av_new_stream(s, 0);=0A=
if (!st)=0A=
@@ -153,6 +268,13 @@ static int mp3_read_header(AVFormatContext *s,=0A=
st->need_parsing =3D AVSTREAM_PARSE_FULL;=0A=
st->start_time =3D 0;=0A=
=0A=
+ /* Initialize the parser so we can store the offset of=0A=
+ trailing metadata there */=0A=
+ assert(!st->parser);=0A=
+ st->parser =3D av_parser_init(st->codec->codec_id);=0A=
+ if (!st->parser)=0A=
+ return -1;=0A=
+=0A=
// lcm of all mp3 sample rates=0A=
av_set_pts_info(st, 64, 1, 14112000);=0A=
=0A=
@@ -165,6 +287,18 @@ static int mp3_read_header(AVFormatContext *s,=0A=
if (mp3_parse_vbr_tags(s, st, off) < 0)=0A=
url_fseek(s->pb, off, SEEK_SET);=0A=
=0A=
+ /* Eventually we may want to parse metadata from APE or=0A=
+ lyrics3 tags, but for now find the offset of trailing=0A=
+ metadata (i.e. lyrics3/APE/id3v1 tags with id3v1 at=0A=
+ the end (if present), but the other two in either=0A=
+ order before that. If we do decide to read all the=0A=
+ trailing metadata, perhaps this function becomes=0A=
+ ff_mp3_read_trailing_metadata and supercedes=0A=
+ ff_id3v1_read. */=0A=
+ ret =3D ff_mp3_find_trailing_metadata(s,st);=0A=
+ if (ret < 0)=0A=
+ return ret;=0A=
+=0A=
/* the parameters will be extracted from the compressed bitstream */=0A=
return 0;=0A=
}=0A=
@@ -174,6 +308,7 @@ static int mp3_read_header(AVFormatContext *s,=0A=
static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt)=0A=
{=0A=
int ret, size;=0A=
+=0A=
// AVStream *st =3D s->streams[0];=0A=
=0A=
size=3D MP3_PACKET_SIZE;=0A=
-- =0A=
1.6.0.4=0A=
=0A=
------=_NextPart_000_004D_01CB5E5B.BC517140
Content-Type: text/plain;
name="iterate_frames.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="iterate_frames.c"
#include <assert.h>=0A=
#include <inttypes.h>=0A=
#include <libavcodec/avcodec.h>=0A=
#include <libavformat/avformat.h>=0A=
#include <libavformat/avio.h>=0A=
#include <stdio.h>=0A=
#include <stdint.h>=0A=
=0A=
int=0A=
main ( int argc,=0A=
char **argv )=0A=
{=0A=
AVFormatParameters ap;=0A=
static char errbuf[512];=0A=
const char *filename;=0A=
AVFormatContext *format_context;=0A=
unsigned long num_frames;=0A=
AVPacket packet;=0A=
int retval;=0A=
unsigned long total_bytes;=0A=
=0A=
if (argc < 2)=0A=
{=0A=
printf("usage: %s filename\n",argv[0]);=0A=
return 1;=0A=
}=0A=
filename =3D argv[1];=0A=
=0A=
av_log_set_level(AV_LOG_DEBUG);=0A=
=0A=
av_register_all();=0A=
=0A=
/*=0A=
** Tell the parser that we only want frame data. In=0A=
** other words, we don't care about metadata that comes=0A=
** before or after frames=0A=
*/=0A=
format_context =3D avformat_alloc_context();=0A=
if (!format_context)=0A=
{=0A=
printf("avformat_alloc_context failed\n");=0A=
return 1;=0A=
}=0A=
=0A=
// format_context->debug =3D FF_FDEBUG_TS;=0A=
=0A=
memset(&ap,0,sizeof(ap));=0A=
ap.prealloced_context =3D 1;=0A=
=0A=
retval =3D av_open_input_file(&format_context,filename,NULL,0,&ap);=0A=
if (retval !=3D 0)=0A=
{=0A=
av_strerror(retval,errbuf,sizeof(errbuf));=0A=
printf("av_open_input_file(%s) failed (%d) -- =
%s\n",filename,retval,=0A=
errbuf);=0A=
av_free(format_context);=0A=
format_context =3D NULL;=0A=
return 1;=0A=
}=0A=
assert(format_context);=0A=
=0A=
num_frames =3D 0;=0A=
total_bytes =3D 0;=0A=
=0A=
while (av_read_frame(format_context,&packet) =3D=3D 0)=0A=
{=0A=
if (packet.size > 0)=0A=
{=0A=
num_frames++;=0A=
total_bytes +=3D packet.size;=0A=
=0A=
printf("\"%s\": frame %ld: offset: %" PRId64 ", size %d "=0A=
"byte(s)\n",filename,num_frames,=0A=
url_ftell(format_context->pb),packet.size);=0A=
}=0A=
av_free_packet(&packet);=0A=
}=0A=
=0A=
av_close_input_file(format_context);=0A=
format_context =3D NULL;=0A=
=0A=
printf("\"%s\": %ld frame(s), %ld byte(s)\n",filename,num_frames,=0A=
total_bytes);=0A=
=0A=
return 0;=0A=
}=0A=
------=_NextPart_000_004D_01CB5E5B.BC517140--
More information about the ffmpeg-devel
mailing list