摘要:下面是,讀取頭信息頭信息。猜測網絡部分至少在一開始就應當初始化好的,因此在的過程里面找,在中找到了。就先暫時分析到此吧。
這章要簡單分析下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. "); 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 "); 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" ", 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. "); #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://specialneedsforspecialkids.com/yun/66701.html
摘要:分別為音頻視頻和字母進行相關處理。向下跟蹤兩層,會發現,核心函數是。至此解碼算完了。整個過程真是粗略分析啊,對自己也很抱歉,暫時先這樣吧。 上文中說到在read_thread線程中有個關鍵函數:avformat_open_input(utils.c),應當是讀取視頻文件的,這個函數屬于ffmpeg層。這回進入到其中去看下: int avformat_open_input(AVForma...
摘要:基本上就是對一個數據幀的描述。我理解的是一個未解碼的壓縮數據幀。 read_thread這個最關鍵的讀取線程中,逐步跟蹤,可以明確stream_component_open---> decoder_start---> video_thread--->ffplay_video_thread。這個調用過程,在解碼開始后的異步解碼線程中,調用的是ffplay_video_thread。具體可...
摘要:我們下面先從讀取線程入手。無論這個循環前后干了什么,都是要走這一步,讀取數據幀。從開始,我理解的是計算出當前數據幀的時間戳后再計算出播放的起始時間到當前時間,然后看這個時間戳是否在此范圍內。 ijkplayer現在比較流行,因為工作關系,接觸了他,現在做個簡單的分析記錄吧。我這里直接跳過java層代碼,進入c層,因為大多數的工作都是通過jni調用到c層來完成的,java層的內容并不是主...
摘要:初始化的過程上一篇其實并未完全分析完,這回接著來。層的函數中,最后還有的調用,走的是層的。結構體如下的和,以及,其余是狀態及的內容。整個過程是個異步的過程,并不阻塞。至于的東西,都是在層創建并填充的。 初始化的過程上一篇其實并未完全分析完,這回接著來。java層的initPlayer函數中,最后還有native_setup的調用,走的是c層的IjkMediaPlayer_native_...
閱讀 2959·2021-11-11 16:55
閱讀 521·2021-09-27 13:36
閱讀 1090·2021-09-22 15:35
閱讀 2915·2019-08-30 12:46
閱讀 3131·2019-08-26 17:02
閱讀 1831·2019-08-26 11:56
閱讀 1298·2019-08-26 11:47
閱讀 429·2019-08-23 17:01