这章要简单分析下ijkplayer是如何从文件或网络读取数据源的。还是read_thread函数中的关键点avformat_open_input函数:
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
......
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
......
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
......
}
avformat_alloc_context创建AVFormatContext结构实例,然后走init_input根据文件名称初始化。有必要看看这个init_input:
static int init_input(AVFormatContext *s, const char *filename,
AVDictionary **options)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
int score = AVPROBE_SCORE_RETRY;
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if (!s->iformat)
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
else if (s->iformat->flags & AVFMT_NOFILE)
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.\n");
return 0;
}
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||
(!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))
return score;
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
return ret;
if (s->iformat)
return 0;
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
s, 0, s->format_probesize);
}
这里的重点只有一个,就是av_probe_input_buffer2。那么他在干什么呢?代码就不贴了,关键点就是avio_read。
int avio_read(AVIOContext *s, unsigned char *buf, int size)
{
int len, size1;
size1 = size;
while (size > 0) {
len = FFMIN(s->buf_end - s->buf_ptr, size);
if (len == 0 || s->write_flag) {
if((s->direct || size > s->buffer_size) && !s->update_checksum) {
// bypass the buffer and read data directly into buf
if(s->read_packet)
len = s->read_packet(s->opaque, buf, size);
if (len <= 0) {
/* do not modify buffer if EOF reached so that a seek back can
be done without rereading data */
s->eof_reached = 1;
if(len<0)
s->error= len;
break;
} else {
s->pos += len;
s->bytes_read += len;
size -= len;
buf += len;
// reset the buffer
s->buf_ptr = s->buffer;
s->buf_end = s->buffer/* + len*/;
}
} else {
fill_buffer(s);
len = s->buf_end - s->buf_ptr;
if (len == 0)
break;
}
} else {
memcpy(buf, s->buf_ptr, len);
buf += len;
s->buf_ptr += len;
size -= len;
}
}
if (size1 == size) {
if (s->error) return s->error;
if (avio_feof(s)) return AVERROR_EOF;
}
return size1 - size;
}
根据传递的要读取的大小循环读取内容,前面的都是如果缓存里面有,先从缓存读取,后面的else里面的fill_buffer是个关键点。
static void fill_buffer(AVIOContext *s)
{
int max_buffer_size = s->max_packet_size ?
s->max_packet_size : IO_BUFFER_SIZE;
uint8_t *dst = s->buf_end - s->buffer + max_buffer_size < s->buffer_size ?
s->buf_end : s->buffer;
int len = s->buffer_size - (dst - s->buffer);
/* can't fill the buffer without read_packet, just set EOF if appropriate */
if (!s->read_packet && s->buf_ptr >= s->buf_end)
s->eof_reached = 1;
/* no need to do anything if EOF already reached */
if (s->eof_reached)
return;
if (s->update_checksum && dst == s->buffer) {
if (s->buf_end > s->checksum_ptr)
s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
s->buf_end - s->checksum_ptr);
s->checksum_ptr = s->buffer;
}
/* make buffer smaller in case it ended up large after probing */
if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size) {
if (dst == s->buffer) {
int ret = ffio_set_buf_size(s, s->orig_buffer_size);
if (ret < 0)
av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");
s->checksum_ptr = dst = s->buffer;
}
av_assert0(len >= s->orig_buffer_size);
len = s->orig_buffer_size;
}
if (s->read_packet)
len = s->read_packet(s->opaque, dst, len);
else
len = 0;
if (len <= 0) {
/* do not modify buffer if EOF reached so that a seek back can
be done without rereading data */
s->eof_reached = 1;
if (len < 0)
s->error = len;
} else {
s->pos += len;
s->buf_ptr = dst;
s->buf_end = dst + len;
s->bytes_read += len;
}
}
通过read_packet去读取数据包。
好了,回到avformat_open_input里继续看:
if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
if (!s->protocol_whitelist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
if (!s->protocol_blacklist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
ret = AVERROR(EINVAL);
goto fail;
}
......
if (s->pb)
ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0);
......
if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto fail;
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
update_stream_avctx(s);
......
这一块我认为是在判断协议和格式的黑白名单,是否在支持的范围内。下面是ff_id3v2_read,读取id3v2头信息头信息。这个查了下是mp3的头信息,因此不再具体看了。下面就是avformat_queue_attached_pictures和update_stream_avctx了,将数据包加入缓存并更新队列。然后基本上就没有了,那么到底网络的读取在什么地方呢?我们只是看到都是从流读取的,好吧,需要查下网络的部分了。
猜测网络部分至少在一开始就应当初始化好的,因此在init的过程里面找,在ffp_global_init中找到了avformat_network_init。
int avformat_network_init(void)
{
#if CONFIG_NETWORK
int ret;
ff_network_inited_globally = 1;
if ((ret = ff_network_init()) < 0)
return ret;
if ((ret = ff_tls_init()) < 0)
return ret;
#endif
return 0;
}
ff_network_init和ff_tls_init,逐个看。
int ff_network_init(void)
{
#if HAVE_WINSOCK2_H
WSADATA wsaData;
#endif
if (!ff_network_inited_globally)
av_log(NULL, AV_LOG_WARNING, "Using network protocols without global "
"network initialization. Please use "
"avformat_network_init(), this will "
"become mandatory later.\n");
#if HAVE_WINSOCK2_H
if (WSAStartup(MAKEWORD(1,1), &wsaData))
return 0;
#endif
return 1;
}
看到了吧,除了ff_network_inited_globally全局变量用来标记是否已经初始化过之外,就是标准的socket了,没什么可看的了。
int ff_tls_init(void)
{
#if CONFIG_TLS_OPENSSL_PROTOCOL
int ret;
if ((ret = ff_openssl_init()) < 0)
return ret;
#endif
#if CONFIG_TLS_GNUTLS_PROTOCOL
ff_gnutls_init();
#endif
return 0;
}
初始化openssl等。
就先暂时分析到此吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。