[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=
 libavformat/mp3.c |  122 =
++++++++++++++++++++++++++++++++++++++++++++++++++++-=0A=
 2 files changed, 123 insertions(+), 1 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/libavformat/mp3.c b/libavformat/mp3.c=0A=
index c1622a3..b8f3734 100644=0A=
--- a/libavformat/mp3.c=0A=
+++ b/libavformat/mp3.c=0A=
@@ -19,9 +19,11 @@=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 "apetag.h"=0A=
 #include "avformat.h"=0A=
 #include "id3v2.h"=0A=
 #include "id3v1.h"=0A=
@@ -31,8 +33,93 @@=0A=
 #include "libavcodec/mpegaudio.h"=0A=
 #include "libavcodec/mpegaudiodecheader.h"=0A=
 =0A=
+/**=0A=
+ * Information used to demux mp3 files=0A=
+ */=0A=
+typedef struct {=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=
+} MP3Context;=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=
+ * @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=
+{=0A=
+    int64_t ape_offset;=0A=
+    uint64_t ape_size;=0A=
+    int64_t id3v1_offset;=0A=
+    int64_t trailing_offset;=0A=
+    int ret;=0A=
+    MP3Context *mp3 =3D s->priv_data;=0A=
+=0A=
+    mp3->trailing_metadata_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=
+                mp3->trailing_metadata_offset =3D id3v1_offset;=0A=
+                goto done;=0A=
+            }=0A=
+        }=0A=
+=0A=
+        trailing_offset =3D ape_offset;=0A=
+    }=0A=
+=0A=
+    mp3->trailing_metadata_offset =3D trailing_offset;=0A=
+=0A=
+ done:=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 +230,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=
@@ -162,6 +250,18 @@ static int mp3_read_header(AVFormatContext *s,=0A=
     if (!av_metadata_get(s->metadata, "", NULL, =
AV_METADATA_IGNORE_SUFFIX))=0A=
         ff_id3v1_read(s);=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);=0A=
+    if (ret < 0)=0A=
+        return ret;=0A=
+=0A=
     if (mp3_parse_vbr_tags(s, st, off) < 0)=0A=
         url_fseek(s->pb, off, SEEK_SET);=0A=
 =0A=
@@ -174,8 +274,28 @@ static int mp3_read_header(AVFormatContext *s,=0A=
 static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt)=0A=
 {=0A=
     int ret, size;=0A=
+    MP3Context *mp3 =3D s->priv_data;=0A=
+    int64_t pos;=0A=
+=0A=
     //    AVStream *st =3D s->streams[0];=0A=
 =0A=
+    /* If we're into the trailing metadata, don't look for a packet */=0A=
+    if (mp3->trailing_metadata_offset !=3D -1) {=0A=
+        pos =3D url_ftell(s->pb);=0A=
+        av_log(s, AV_LOG_DEBUG, "%s: \"%s\": current offset: %" PRId64 =
", "=0A=
+               "trailing_metadata_offset: %" PRId64 "\n", __FUNCTION__,=0A=
+               s->filename, pos, mp3->trailing_metadata_offset);=0A=
+=0A=
+        if (pos < 0)=0A=
+            return pos;=0A=
+        if (pos >=3D mp3->trailing_metadata_offset) {=0A=
+            av_log(s, AV_LOG_DEBUG, "%s: \"%s\": giving up at offset: %"=0A=
+                   PRId64 " due to trailing metadata\n", __FUNCTION__,=0A=
+                   s->filename, pos);=0A=
+            return AVERROR_EOF;=0A=
+        }=0A=
+    }=0A=
+=0A=
     size=3D MP3_PACKET_SIZE;=0A=
 =0A=
     ret=3D av_get_packet(s->pb, pkt, size);=0A=
@@ -193,7 +313,7 @@ static int mp3_read_packet(AVFormatContext *s, =
AVPacket *pkt)=0A=
 AVInputFormat mp3_demuxer =3D {=0A=
     "mp3",=0A=
     NULL_IF_CONFIG_SMALL("MPEG audio layer 2/3"),=0A=
-    0,=0A=
+    sizeof(MP3Context),=0A=
     mp3_read_probe,=0A=
     mp3_read_header,=0A=
     mp3_read_packet,=0A=
-- =0A=
1.6.0.4=0A=
=0A=

------=_NextPart_000_016F_01CB5E3E.615FC240
Content-Type: audio/mpeg;
	name="ape_and_id3v1_with_audio_5.mp3"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
	filename="ape_and_id3v1_with_audio_5.mp3"

//uwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAB9xAE0CSAADBggL
DRASFRcaHB8hJCYpKy4wMzU4Oz5AQ0VISk1PUlRXWVxeYWNmaGttcHN2eHt9gIOFiIqNj5KUl5mc
nqGjpqisrrGztri7vcDCxcfKzM/R1NbZ297g4+bp6+7w8/X4+v0AAAA6TEFNRTMuOTIgAbsAAAAA
AAAAAALAJAgERQAAAABNAkhRnzMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/
+7AEAAAEF4i3himgAIRRFuDFNAAQkej4HGMAAgu8n4eQYADxqBP4NYXfgUxD+F7HP+bjDj3/ymJm
MOPP/wtYmZTBXxg//1AUcliUC5js//x4EMcA8ECQKH//5uSBQIYngyBwBdBgP///BaAvA8AKIMAs
JwWJhzG/////YOYaQvBYbhVCKMsbA5Yyx3iN/////////48xGxMymJmPc3HuMOUxGxGx5+PRP8eA
WeC4BZ4Xgcn7GIXf/GWO8LuMv/x4HTcxIn/4lA7x5lwS8D3//w3wDDIaYiwSQNz//8TAcAwAjZDJ
QYMdg8P///AdAThECYA7DMkgvCKxx/////jzEwKx5gOgbRgxOBLyGBMw3zARsS//////////HmRh
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

------=_NextPart_000_016F_01CB5E3E.615FC240
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_016F_01CB5E3E.615FC240--




More information about the ffmpeg-devel mailing list