Persistence will dawn on you, turning effort into the light of reality.
从前面翻译ISO/IEC 11172-3 mp3编码标准中可以看出mp3文件的构成大致为:
+------------------------------------------------------+
| ID3v2 标签 (可选) |
| [ID3v2 Header] + [ID3v2 Frames] + [Padding] |
+------------------------------------------------------+
| 音频帧 1 |
| [Sync Word] + [Frame Header] + [Side Information] + |
| [Main Data (Part 1)] |
+------------------------------------------------------+
| 音频帧 2 |
| [Sync Word] + [Frame Header] + [Side Information] + |
| [Main Data (Part 2)] |
+------------------------------------------------------+
| ... |
+------------------------------------------------------+
| 音频帧 N |
| [Sync Word] + [Frame Header] + [Side Information] + |
| [Main Data (Last Part)] |
+------------------------------------------------------+
| Xing/Info 头(可选) |
| [Xing Identifier] + [Flags] + [Metadata Fields] |
+------------------------------------------------------+
下面以一个demo看看mp3 demux(mp3dec.c)的每个接口如何被调用以及如何读取到数据的. 此文看完后可以解答一下几个问题:
- mp3_read_probe函数中如何识别此文件是mp3文件?并给出多少匹配分?
- mp3_read_header中如何获取到stream info?
- mp3_read_packet每次读取多少数据,每一个avpackt中是否都是完整帧?
- mp3_seek是如何找到对应seekpoint的数据帧的?
demo code
include <libavformat/avformat.h>
int main(int argc, char *argv[]) {
AVFormatContext *fmt_ctx = NULL;
AVPacket *pkt = NULL;
int ret, stream_index;
const char *filename;
filename = "/data/ffmpeg/demo/test.mp3";
// 打开输入文件
avformat_open_input(&fmt_ctx, filename, NULL, NULL));
// 获取流信息
avformat_find_stream_info(fmt_ctx, NULL));
// 查找第一个音频流
stream_index = -1;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
stream_index = i;
break;
}
}
// 分配 AVPacket
pkt = av_packet_alloc();
printf("Reading packets from '%s' (Audio Stream Index: %d):\n", filename, stream_index);
printf("----------------------------------------\n");
// 循环读取数据包
while ((ret = av_read_frame(fmt_ctx, pkt)) >= 0) {
if (pkt->stream_index == stream_index) {
// 打印当前包的原始数据大小
printf("Packet Size: %6d bytes | PTS: %lld | DTS: %lld\n",
pkt->size,
pkt->pts,
pkt->dts);
}
av_packet_unref(pkt); // 释放包内存(保留头部)
}
if (ret == AVERROR_EOF) {
printf("----------------------------------------\n");
printf("End of file reached.\n");
} else {
char errbuf[128];
av_strerror(ret, errbuf, sizeof(errbuf));
printf("Error occurred: %s\n", errbuf);
}
// 清理资源
av_packet_free(&pkt);
avformat_close_input(&fmt_ctx);
return 0;
}
mp3_read_probe
mp3_read_probe总结先行
1.demux.c会从文件中读取2048个字节传入到AVProbeData,并把AVProbeData作为参数传入mp3_read_probe
2.mp3_read_probe首先会忽略空bit,依次从非空bit的第一个字节,第二个字节,第三个字节...开始header字段校验,看看在不同起时位置能解析到多少个正确的frame
3.如果从非空bit的第一个字节开始解析,能正确解析到7个frame,得分51
4.否则,利用解析到正确的最大帧数目和最大帧size作为条件返回得分
5.最后通过header计算出frame_size(一帧内有多少字节的数据),返回。
使用gdb运行demo: gdb mp3_demux_demo
Reading symbols from ./mp3-demux-demo...
(gdb) b mp3_read_probe //设置断点
Function "mp3_read_probe" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mp3_read_probe) pending.
(gdb) r //运行程序
Starting program: /data/ffmpeg/demo/build/mp3-demux-demo
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, mp3_read_probe (p=0x7fffffffdcd0) at libavformat/mp3dec.c:68
68 {
(gdb) bt //查看调用关系
#0 mp3_read_probe (p=0x7fffffffdcd0) at libavformat/mp3dec.c:68
#1 0x00007ffff7dd7526 in av_probe_input_format3 (pd=pd@entry=0x7fffffffdda0, is_opened=is_opened@entry=1, score_ret=score_ret@entry=0x7fffffffdd44) at libavformat/demux.h:135
#2 0x00007ffff7dd7766 in av_probe_input_format2 (pd=pd@entry=0x7fffffffdda0, is_opened=is_opened@entry=1, score_max=score_max@entry=0x7fffffffdd8c) at libavformat/format.c:238
#3 0x00007ffff7dd79bf in av_probe_input_buffer2 (pb=0x555555561d40, fmt=0x555555559048, filename=filename@entry=0x555555556008 "data/ffmpeg/demo/test.mp3", logctx=logctx@entry=0x555555559040,
offset=offset@entry=0, max_probe_size=1048576) at libavformat/format.c:312
#4 0x00007ffff7db94a2 in init_input (options=0x7fffffffde20, filename=0x555555556008 "/data/ffmpeg/demo/test.mp3", s=0x555555559040) at libavformat/demux.c:182
#5 avformat_open_input (ps=0x7fffffffdec8, filename=0x555555556008 "/home/mi/data/ffmpeg/demo/test.mp3", fmt=<optimized out>, options=0x0) at libavformat/demux.c:247
#6 0x00005555555552f8 in main (argc=1, argv=0x7fffffffe068) at /data/ffmpeg/demo/mp3_demux.c:13
从上面的backtrace开看,会从mp3文件中读取一段buffer给到mp3_read_probe进行判断当前文件是否为MP3,并给出打分。 用gdb 查看下AVProbeData的信息,可以看出读了2048个字节。 下面继续看下从这2048个字节能获取到什么信息。
(gdb) p *p
$2 = {filename = 0x555555556008 "/data/ffmpeg/demo/test.mp3", buf = 0x555555561ed0 "\377\373\320d", buf_size = 2048, mime_type = 0x0}
static int mp3_read_probe(const AVProbeData *p)
{
int max_frames, first_frames = 0;
int whole_used = 0;
int frames, ret;
int framesizes, max_framesizes;
uint32_t header;
const uint8_t *buf, *buf0, *buf2, *buf3, *end;
buf0 = p->buf;
end = p->buf + p->buf_size - sizeof(uint32_t);
//清除文件开头的空字节
while (buf0 < end && !*buf0)
buf0++;
max_frames = 0;
max_framesizes = 0;
buf = buf0;
//第0个,第1个,第2个...字节匹配MP3标准header
for (; buf < end; buf = buf2+1) {
buf2 = buf;
for (framesizes = frames = 0; buf2 < end; frames++) {
MPADecodeHeader h;
int header_emu = 0;
int available;
header = AV_RB32(buf2);//解析开头的4个字节,把结果解析到MPADecodeHeader
ret = avpriv_mpegaudio_decode_header(&h, header);
if (ret != 0)
break;
//下一段buffer的大小
available = FFMIN(h.frame_size, end - buf2);
//从上一个header后面,第0个,第1个,第2个...字节匹配MP3标准header
// 如果匹配到两个及以上个合法的header说明此文件就问题,不是一个合法的mp3文件
for (buf3 = buf2 + 4; buf3 < buf2 + available; buf3++) {
uint32_t next_sync = AV_RB32(buf3);
header_emu += (next_sync & MP3_MASK) == (header & MP3_MASK);
}
if (header_emu > 2)
break;
//正确的framesize
framesizes += h.frame_size;
if (available < h.frame_size) {
//正确的frame count
frames++;
break;
}
buf2 += h.frame_size;
}
max_frames = FFMAX(max_frames, frames);
max_framesizes = FFMAX(max_framesizes, framesizes);
if (buf == buf0) {
first_frames= frames;
if (buf2 == end + sizeof(uint32_t))
whole_used = 1;
}
}
// keep this in sync with ac3 probe, both need to avoid
// issues with MPEG-files!
//如果从prob buffer第一个字节开始匹配,匹配到7个以上的正确的frame说明高准却性,得分很高51分
if (first_frames>=7) return AVPROBE_SCORE_EXTENSION + 1;
//得分50分
else if (max_frames>200 && p->buf_size < 2*max_framesizes)return AVPROBE_SCORE_EXTENSION;
else if (max_frames>=4 && p->buf_size < 2*max_framesizes) return AVPROBE_SCORE_EXTENSION / 2;
else if (ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size)
return p->buf_size < PROBE_BUF_MAX ? AVPROBE_SCORE_EXTENSION / 4 : AVPROBE_SCORE_EXTENSION - 2;
else if (first_frames > 1 && whole_used) return 5;
else if (max_frames>=1 && p->buf_size < 10*max_framesizes) return 1;
else return 0;
//mpegps_mp3_unrecognized_format.mpg has max_frames=3
}
我们来看下下面测试MP3文件,看看能得多少分?
00000000: fffb d064 0000 c66c 6924 b32c 36b2 6f4d ...d...li$.,6.oM
00000010: 04c2 0023 0261 f1ea ba55 d780 293f 3257 ...#.a...U..)?2W
00000020: 4a80 2004 68a0 ad2c 0204 1c90 6907 2b26 J. .h..,....i.+&
00000030: 5443 0c96 b8ac 2070 e5ef 6a8d 999d c04f TC.... p..j....O
00000040: 22e7 614b f448 0403 a863 017b 9d48 dd4b ".aK.H...c.{.H.K
00000050: 9127 e59b b107 8e1a 84c3 f28a 6b12 ba09 .'..........k...
00000060: d97c 423b 0a64 ed62 0d8b 4829 ff56 6755 .|B;.d.b..H).VgU
00000070: 9815 cac5 92d9 842e 4368 8cca 85d3 2220 ........Ch...."
00000080: e84b 4c89 f948 726c 1185 0399 48f4 b670 .KL..Hrl....H..p
00000090: 9504 b671 55e5 c2a9 388a 3f8e e60d efac ...qU...8.?.....
000000a0: 3b29 1a8f 8669 16ab 590b ce2c db30 b483 ;)...i..Y..,.0..
000000b0: 2684 6e6c 1e51 11e1 640d 5a17 50f3 6616 &.nl.Q..d.Z.P.f.
000000c0: 7293 31f3 ec59 5292 040f a8b6 cb65 903c r.1..YR......e.<
000000d0: e456 8279 2b40 9993 b0ef 597d 9ce5 0c26 .V.y+@....Y}...&
000000e0: 64ae 37d2 4ab2 2927 596f d272 2b7f fd96 d.7.J.)'Yo.r+...
000000f0: a940 0016 6569 4cd2 9959 9d84 f339 93ca .@..eiL..Y...9..
00000100: 6961 25c9 e11f e733 4a6e 94ca c23b 09e1 ia%....3Jn...;..
00000110: 1cc9 e534 b092 9958 47fb ccd2 9ba5 32b9 ...4...XG.....2.
00000120: 1dc9 e13c 2278 a695 8929 a3c2 7864 f08d ...<"x...)..xd..
reakpoint 1, mp3_read_probe (p=0x7fffffffdcd0) at libavformat/mp3dec.c:68
68 {
(gdb) n
76 buf0 = p->buf;
(gdb) n
77 end = p->buf + p->buf_size - sizeof(uint32_t);
(gdb) n
78 while (buf0 < end && !*buf0)
(gdb) n
85 for (; buf < end; buf = buf2+1) {
(gdb) n
92 header = AV_RB32(buf2);
(gdb) n
93 ret = avpriv_mpegaudio_decode_header(&h, header);
(gdb) p/x header
$1 = 0xfffbd064
(gdb) n
94 if (ret != 0)
(gdb) n
97 available = FFMIN(h.frame_size, end - buf2);
(gdb) p h
$2 = {frame_size = 835, error_protection = 0, layer = 3, sample_rate = 44100, sample_rate_index = 0, bit_rate = 256000, nb_channels = 2, mode = 1, mode_ext = 2, lsf = 0}
(gdb) b mp3dec.c:120
Breakpoint 2 at 0x7ffff7e5c330: file libavformat/mp3dec.c, line 121.
(gdb) c
Continuing.
Breakpoint 2, mp3_read_probe (p=0x7fffffffdcd0) at libavformat/mp3dec.c:121
121 if (first_frames>=7) return AVPROBE_SCORE_EXTENSION + 1;
(gdb) p first_frames
$3 = 3
(gdb) p max_frames
$4 = 3
(gdb) p max_framesizes
$5 = 2507
(gdb) n
122 else if (max_frames>200 && p->buf_size < 2*max_framesizes)return AVPROBE_SCORE_EXTENSION;
(gdb) n
123 else if (max_frames>=4 && p->buf_size < 2*max_framesizes) return AVPROBE_SCORE_EXTENSION / 2;
(gdb) n
124 else if (ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size)
(gdb) n
126 else if (first_frames > 1 && whole_used) return 5;
(gdb) n
127 else if (max_frames>=1 && p->buf_size < 10*max_framesizes) return 1;
(gdb) n
av_probe_input_format3 (pd=pd@entry=0x7fffffffdda0, is_opened=is_opened@entry=1, score_ret=score_ret@entry=0x7fffffffdd44) at libavformat/format.c:195
195 if (score)
(gdb) p socre
No symbol "socre" in current context.
(gdb) p score
$6 = 1
(gdb)
从上面gdb得到的信息来看,
- 通过header 4个字节:0xfffbd064解析到了下面的信息:
$2 = {
frame_size = 835,
error_protection = 0,
layer = 3,
sample_rate = 44100,
sample_rate_index = 0,
bit_rate = 256000,
nb_channels = 2,
mode = 1,
mode_ext = 2,
lsf = 0}
- 计算framesize和framecount,最终得分1分。得分第主要是由于prob buffer数据量比较小从而解析到帧数比小于7个。
first_frames: 3
max_frames: 3
max_framesizes:2507
score:1
avpriv_mpegaudio_decode_header
下面结合代码,看看0xfffbd064为何能解析到上述那么多信息。
// 0xfffbd064二进制表示
1111 1111 1111 1011 1101 0000 0110 0100
// ISO/IEC 11172-3规定的MP3 header
//header()
//{
// syncword 12 bit bslbf
// ID 1 bit bslbf
// layer 2 bit bslbf
// protection_bit 1 bit bslbf
// bitrate_index 4 bit bslbf
// sampling_frequency 2 bit bslbf
// padding_bit 1 bit bslbf
// private_bit 1 bit bslbf
// mode 2 bit bslbf
// mode-extension 2 bit bslbf
// copyright 1 bit bslbf
// original/copy 1 bit bslbf
// emphasis 2 bit bslbf
//}
int avpriv_mpegaudio_decode_header(MPADecodeHeader *s, uint32_t header)
{
int sample_rate, frame_size, mpeg25, padding;
int sample_rate_index, bitrate_index;
int ret;
ret = ff_mpa_check_header(header);
if (ret < 0)
return ret;
//第20 bit为1,19bit为1,所以lsf为0
if (header & (1<<20)) {
s->lsf = (header & (1<<19)) ? 0 : 1;
mpeg25 = 0;
} else {
s->lsf = 1;
mpeg25 = 1;
}
//右移17位后,最低两bit为01, 与3与后为1,所以layer为3
s->layer = 4 - ((header >> 17) & 3);
//右移10位后,最低两bit为11, 所以sample_rate_index为3
sample_rate_index = (header >> 10) & 3;
if (sample_rate_index >= FF_ARRAY_ELEMS(ff_mpa_freq_tab))
sample_rate_index = 0;
// const uint16_t ff_mpa_freq_tab[3] = { 44100, 48000, 32000 };
// 3超过了ff_mpa_freq_tab的size,所以sample_rate_index被设置为0
// 由于lsf和mpeg25都是0,则sample_rate为44100
sample_rate = ff_mpa_freq_tab[sample_rate_index] >> (s->lsf + mpeg25);
sample_rate_index += 3 * (s->lsf + mpeg25);
s->sample_rate_index = sample_rate_index;
s->error_protection = ((header >> 16) & 1) ^ 1;
s->sample_rate = sample_rate;
//bitrate_index为13
bitrate_index = (header >> 12) & 0xf;
padding = (header >> 9) & 1;
//extension = (header >> 8) & 1;
s->mode = (header >> 6) & 3;
s->mode_ext = (header >> 4) & 3;
//copyright = (header >> 3) & 1;
//original = (header >> 2) & 1;
//emphasis = header & 3;
//根据mode判断声道数目
if (s->mode == MPA_MONO)
s->nb_channels = 1;
else
s->nb_channels = 2;
if (bitrate_index != 0) {
//ff_mpa_bitrate_tab这个表也是ISO/IEC 11172-3中定义的
//ff_mpa_bitrate_tab[0][2][13] = 256
frame_size = ff_mpa_bitrate_tab[s->lsf][s->layer - 1][bitrate_index];
s->bit_rate = frame_size * 1000;
switch(s->layer) {
case 1:
frame_size = (frame_size * 12000) / sample_rate;
frame_size = (frame_size + padding) * 4;
break;
case 2:
frame_size = (frame_size * 144000) / sample_rate;
frame_size += padding;
break;
default:
case 3:
// 由于MP3标准规定一帧有1152个采样点,那么在不同采样率下,这1152个采样点分辨表多长时间呢?
// frame_druation = 1152 / sample_rate
// frame_size = bitrate(kbps) *1000 * frame_duration / 8 (Byte)
// frame_size = (bitrate(kbps) * 1000 * 1152)/ (sample_rate * 8)
// frame_size = (bitrate * 1152 * 1000/8)/ sample_rate
// frame_size = (bitrate * 144000)/ sample_rate
//因此frame_size = 256 * 144000 / 44100 = 835 字节
frame_size = (frame_size * 144000) / (sample_rate << s->lsf);
frame_size += padding;
break;
}
s->frame_size = frame_size;
} else {
/* if no frame size computed, signal it */
return 1;
}
return 0;
}
mp3_read_header
首先来看下backtrace,由此可见avformat_open_input中会调用到mp3_read_header,那么再看看mp3_read_header中具体做了什么事情?
结论先行
- 创建stream
- 填充st->codecpar中的信息,如:codec_id
- 读取64K数据进行header字段检查,并检查frame_size是否合法
- 设置need_parsing = AVSTREAM_PARSE_FULL_RAW,使能ff_mpegaudio_parser进行分帧处理
- 将avio的position再重新seek back到文件头
Reading symbols from ./mp3-demux-demo...
(gdb) b mp3_read_header
Function "mp3_read_header" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mp3_read_header) pending.
(gdb) r
Starting program: /home/mi/data/ffmpeg/demo/build/mp3-demux-demo
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, mp3_read_header (s=0x555555559040) at libavformat/mp3dec.c:363
363 {
(gdb) bt
#0 mp3_read_header (s=0x555555559040) at libavformat/mp3dec.c:363
#1 0x00007ffff7db9129 in avformat_open_input (ps=0x7fffffffdec8, filename=0x555555556008 "/data/ffmpeg/demo/test.mp3", fmt=<optimized out>, options=0x0) at libavformat/demux.h:135
#2 0x00005555555552f8 in main (argc=1, argv=0x7fffffffe068) at /data/ffmpeg/demo/mp3_demux.c:13
源码分析:
static int mp3_read_header(AVFormatContext *s)
{
FFFormatContext *const si = ffformatcontext(s);
MP3DecContext *mp3 = s->priv_data;
AVStream *st;
FFStream *sti;
int64_t off;
int ret;
int i;
s->metadata = si->id3v2_meta;
si->id3v2_meta = NULL;
//创建stream
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
sti = ffstream(st);
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->codec_id = AV_CODEC_ID_MP3;
//此flag非常重要,设置后会demux.c中启动ff_mpegaudio_parser进行分帧处理
sti->need_parsing = AVSTREAM_PARSE_FULL_RAW;
st->start_time = 0;
// lcm of all mp3 sample rates
avpriv_set_pts_info(st, 64, 1, 14112000);
ffiocontext(s->pb)->maxsize = -1;
off = avio_tell(s->pb);
if (!av_dict_count(s->metadata))
ff_id3v1_read(s);
//获取文件大小
if (s->pb->seekable & AVIO_SEEKABLE_NORMAL)
mp3->filesize = avio_size(s->pb);
//处理非标准附加的信息
if (mp3_parse_vbr_tags(s, st, off) < 0)
avio_seek(s->pb, off, SEEK_SET);
ret = ff_replaygain_export(st, s->metadata);
if (ret < 0)
return ret;
ret = ffio_ensure_seekback(s->pb, 64 * 1024 + MPA_MAX_CODED_FRAME_SIZE + 4);
if (ret < 0)
return ret;
off = avio_tell(s->pb);
//获取frame size
for (i = 0; i < 64 * 1024; i++) {
uint32_t header, header2;
int frame_size;
frame_size = check(s->pb, off + i, &header);
if (frame_size > 0) {
ret = check(s->pb, off + i + frame_size, &header2);
if (ret >= 0 && (header & MP3_MASK) == (header2 & MP3_MASK))
break;
} else if (frame_size == CHECK_SEEK_FAILED) {
av_log(s, AV_LOG_ERROR, "Failed to find two consecutive MPEG audio frames.\n");
return AVERROR_INVALIDDATA;
}
}
if (i == 64 * 1024) {
off = avio_seek(s->pb, off, SEEK_SET);
} else {
av_log(s, i > 0 ? AV_LOG_INFO : AV_LOG_VERBOSE, "Skipping %d bytes of junk at %"PRId64".\n", i, off);
off = avio_seek(s->pb, off + i, SEEK_SET);
}
if (off < 0)
return off;
// the seek index is relative to the end of the xing vbr headers
for (int i = 0; i < sti->nb_index_entries; i++)
sti->index_entries[i].pos += off;
/* the parameters will be extracted from the compressed bitstream */
return 0;
}
mp3_read_packet
从mp3_read_packet函数中可以看到每次从avio中读取1024字节。
(gdb) p *pkt
$3 = {buf = 0x555555572640, pts = -9223372036854775808, dts = -9223372036854775808,
data = 0x55555555bd40 "...", 'size = 1024', stream_index = 0, flags = 0, side_data = 0x0, side_data_elems = 0, duration = 0, pos = 46080,
opaque = 0x0, opaque_ref = 0x0, time_base = {num = 0, den = 1}}
但是从av_read_frame中得到的avpackt.size是836:
Packet Size: 835 bytes | PTS: 0 | DTS: 0
Packet Size: 836 bytes | PTS: 368640 | DTS: 368640
Packet Size: 836 bytes | PTS: 737280 | DTS: 737280
Packet Size: 836 bytes | PTS: 1105920 | DTS: 1105920
Packet Size: 836 bytes | PTS: 1474560 | DTS: 1474560
Packet Size: 836 bytes | PTS: 1843200 | DTS: 1843200
Packet Size: 836 bytes | PTS: 2211840 | DTS: 2211840
Packet Size: 836 bytes | PTS: 2580480 | DTS: 2580480
Packet Size: 836 bytes | PTS: 2949120 | DTS: 2949120
Packet Size: 836 bytes | PTS: 3317760 | DTS: 3317760
Packet Size: 836 bytes | PTS: 3686400 | DTS: 3686400
Packet Size: 836 bytes | PTS: 4055040 | DTS: 4055040
Packet Size: 836 bytes | PTS: 4423680 | DTS: 4423680
原因是在ff_mpegaudio_parser中会进行分帧处理,因为在mp3_read_header中设置了
sti->need_parsing = AVSTREAM_PARSE_FULL_RAW;
原因是在ff_mpegaudio_parser中会进行分帧处理流程如下:
输入数据:
- 包1:
[帧1头...部分数据] - 包2:
[剩余数据...帧2头]
解析步骤:
- 处理包1:发现帧1头,但数据不足,返回
-1,数据暂存到pc->buffer。 - 处理包2:合并
pc->buffer和包2,找到完整帧1,返回帧数据;残留数据(如帧2头)暂存到pc->buffer。
#define MP3_PACKET_SIZE 1024
static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt)
{
MP3DecContext *mp3 = s->priv_data;
int ret, size;
int64_t pos;
size = MP3_PACKET_SIZE;//每次读取1024字节数据
pos = avio_tell(s->pb);
if (mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize)
size= FFMIN(size, mp3->filesize - pos);
ret = av_get_packet(s->pb, pkt, size);
if (ret <= 0) {
if(ret<0)
return ret;
return AVERROR_EOF;
}
pkt->flags &= ~AV_PKT_FLAG_CORRUPT;
pkt->stream_index = 0;
return ret;
}
mp3_seek
此函数随长,只需要弄懂一句话即可:
ie->pos = av_rescale(timestamp, filesize, st->duration) + si->data_offset;
也就是: seek_pos = (seek_timestamp / st->duration) * filesize; 即:文件seek位置 = (seek时间戳/文件总时长)*文件总字节数
算出seek_pos后直接调用avio_seek,seek到想要快进的位置,当下次再avio_read就可以读取到seek后的新数据了。
static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags)
{
FFFormatContext *const si = ffformatcontext(s);
MP3DecContext *mp3 = s->priv_data;
AVIndexEntry *ie, ie1;
AVStream *st = s->streams[0];
FFStream *const sti = ffstream(st);
int64_t best_pos;
int fast_seek = s->flags & AVFMT_FLAG_FAST_SEEK;
int64_t filesize = mp3->header_filesize;
if (filesize <= 0) {
int64_t size = avio_size(s->pb);
if (size > 0 && size > si->data_offset)
filesize = size - si->data_offset;
}
if (mp3->xing_toc && (mp3->usetoc || (fast_seek && !mp3->is_cbr))) {
int64_t ret = av_index_search_timestamp(st, timestamp, flags);
// NOTE: The MP3 TOC is not a precise lookup table. Accuracy is worse
// for bigger files.
av_log(s, AV_LOG_WARNING, "Using MP3 TOC to seek; may be imprecise.\n");
if (ret < 0)
return ret;
ie = &sti->index_entries[ret];
} else if (fast_seek && st->duration > 0 && filesize > 0) {
if (!mp3->is_cbr)
av_log(s, AV_LOG_WARNING, "Using scaling to seek VBR MP3; may be imprecise.\n");
ie = &ie1;
timestamp = av_clip64(timestamp, 0, st->duration);
ie->timestamp = timestamp;
ie->pos = av_rescale(timestamp, filesize, st->duration) + si->data_offset;
} else {
return -1; // generic index code
}
best_pos = mp3_sync(s, ie->pos, flags);
if (best_pos < 0)
return best_pos;
if (mp3->is_cbr && ie == &ie1 && mp3->frames) {
int frame_duration = av_rescale(st->duration, 1, mp3->frames);
ie1.timestamp = frame_duration * av_rescale(best_pos - si->data_offset, mp3->frames, mp3->header_filesize);
}
avpriv_update_cur_dts(s, st, ie->timestamp);
return 0;
}