热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ijkplayer系列9:流程分析准备工作

准备工作准备工作流程如下:前面几个方法没啥好讲解的,无非就是调用下一个方法或者加把锁,没有实质内容,我们直接跳到ijkmp

准备工作
准备工作流程如下:
在这里插入图片描述

前面几个方法没啥好讲解的,无非就是调用下一个方法或者加把锁,没有实质内容,我们直接跳到ijkmp_prepare_async_l():

static int ijkmp_prepare_async_l(IjkMediaPlayer *mp) {assert(mp);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_IDLE);// MPST_RET_IF_EQ(mp->mp_state, MP_STATE_INITIALIZED);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ASYNC_PREPARING);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PREPARED);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STARTED);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PAUSED);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_COMPLETED);// MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STOPPED);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ERROR);MPST_RET_IF_EQ(mp->mp_state, MP_STATE_END);// setDataSource操作必须在prepare之前&#xff0c;setDataSource之后状态变更为MP_STATE_INITIALIZEDassert(mp->data_source);// 状态变更为MP_STATE_ASYNC_PREPARING并通知外部ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING);// 启用消息队列msg_queue_start(&mp->ffplayer->msg_queue);// released in msg_loopijkmp_inc_ref(mp);// 创建消息队列模型中的消费者线程mp->msg_thread &#61; SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");// msg_thread is detached inside msg_loop// TODO: 9 release weak_thiz if pthread_create() failed;int retval &#61; ffp_prepare_async_l(mp->ffplayer, mp->data_source);if (retval <0) {ijkmp_change_state_l(mp, MP_STATE_ERROR);return retval;}return 0;
}

前面一堆代码是一如既往的合法性验证&#xff0c;如果当前状态不是INITIALIZED或者STOPPED则验证不通过&#xff0c;这说明了两点&#xff1a;

  1. prepare要在setDataSource之后调用。
  2. stop之后可以再次调用prepare重新进入正常流程。
    合法性验证过了后&#xff0c;将当前状态置为MP_STATE_ASYNC_PREPARING&#xff0c;并作为一条消息添加到消息队列中&#xff0c;然后启用消息队列。接着创建了消息队列模型中的消费者线程&#xff0c;这个线程的运行最终会指向创建IjkMediaPlayer时传入的message_loop方法。
    最后调用了ffp_prepare_async_l()方法&#xff0c;我们来看下这个方法的代码&#xff1a;

int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name) {assert(ffp);assert(!ffp->is);assert(file_name);if (av_stristart(file_name, "rtmp", NULL) ||av_stristart(file_name, "rtsp", NULL)) {// There is total different meaning for &#39;timeout&#39; option in rtmpav_log(ffp, AV_LOG_WARNING, "remove &#39;timeout&#39; option for rtmp.\n");av_dict_set(&ffp->format_opts, "timeout", NULL, 0);}/* there is a length limit in avformat */if (strlen(file_name) &#43; 1 > 1024) {av_log(ffp, AV_LOG_ERROR, "%s too long url\n", __func__);if (avio_find_protocol_name("ijklongurl:")) {av_dict_set(&ffp->format_opts, "ijklongurl-url", file_name, 0);file_name &#61; "ijklongurl:";}}av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61; versions &#61;&#61;&#61;&#61;&#61;\n");ffp_show_version_str(ffp, "ijkplayer", ijk_version_info());ffp_show_version_str(ffp, "FFmpeg", av_version_info());ffp_show_version_int(ffp, "libavutil", avutil_version());ffp_show_version_int(ffp, "libavcodec", avcodec_version());ffp_show_version_int(ffp, "libavformat", avformat_version());ffp_show_version_int(ffp, "libswscale", swscale_version());ffp_show_version_int(ffp, "libswresample", swresample_version());av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61; options &#61;&#61;&#61;&#61;&#61;\n");ffp_show_dict(ffp, "player-opts", ffp->player_opts);ffp_show_dict(ffp, "format-opts", ffp->format_opts);ffp_show_dict(ffp, "codec-opts ", ffp->codec_opts);ffp_show_dict(ffp, "sws-opts ", ffp->sws_dict);ffp_show_dict(ffp, "swr-opts ", ffp->swr_opts);av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;\n");av_opt_set_dict(ffp, &ffp->player_opts);if (!ffp->aout) {ffp->aout &#61; ffpipeline_open_audio_output(ffp->pipeline, ffp);if (!ffp->aout)return -1;}#if CONFIG_AVFILTERif (ffp->vfilter0) {GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);ffp->vfilters_list[ffp->nb_vfilters - 1] &#61; ffp->vfilter0;}
#endif// 开启流VideoState *is &#61; stream_open(ffp, file_name, NULL);if (!is) {av_log(NULL, AV_LOG_WARNING, "ffp_prepare_async_l: stream_open failed OOM");return EIJK_OUT_OF_MEMORY;}ffp->is &#61; is;ffp->input_filename &#61; av_strdup(file_name);return 0;
}

这个方法主要做了以下三件事&#xff1a;

  1. 将外部配置的信息赋给ffp的相关字段。
  2. 开启Audio Output&#xff0c;对应的Android就是AudioTrack或者opensles。
  3. 开启流。
    从这里我们可以看到&#xff0c;流其实在prepare阶段已经开启&#xff0c;而非start阶段。
    下面我们来看下stream_open()&#xff1a;

static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat) {assert(!ffp->is);VideoState *is;is &#61; av_mallocz(sizeof(VideoState));if (!is)return NULL;is->filename &#61; av_strdup(filename);if (!is->filename)goto fail;is->iformat &#61; iformat;is->ytop &#61; 0;is->xleft &#61; 0;// 初始化音视频解码数据包队列if (frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1) <0)goto fail;if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) <0)goto fail;if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) <0)goto fail;// 初始化音视频原始数据包队列if (packet_queue_init(&is->videoq) <0 ||packet_queue_init(&is->audioq) <0 ||packet_queue_init(&is->subtitleq) <0)goto fail;if (!(is->continue_read_thread &#61; SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());goto fail;}// 初始化时间点init_clock(&is->vidclk, &is->videoq.serial);init_clock(&is->audclk, &is->audioq.serial);init_clock(&is->extclk, &is->extclk.serial);is->audio_clock_serial &#61; -1;is->audio_volume &#61; SDL_MIX_MAXVOLUME;is->muted &#61; 0;is->av_sync_type &#61; ffp->av_sync_type;is->play_mutex &#61; SDL_CreateMutex();ffp->is &#61; is;is->pause_req &#61; !ffp->start_on_prepared;// 启动视频渲染线程is->video_refresh_tid &#61; SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");if (!is->video_refresh_tid) {av_freep(&ffp->is);return NULL;}// 启动流读取线程is->read_tid &#61; SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");if (!is->read_tid) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
fail:is->abort_request &#61; true;if (is->video_refresh_tid)SDL_WaitThread(is->video_refresh_tid, NULL);stream_close(ffp);return NULL;}return is;
}

这个方法最主要的工作如下&#xff1a;

  1. 初始化jitter buffer
  2. 初始化时间点
  3. 启动了两个线程&#xff0c;read_thread处理流的读取&#xff0c;video_refresh_thread处理视频渲染。这两个线程均是ijkplayer的核心线程之一&#xff0c;我们会在其他文章中单

推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 本文介绍了SPOJ2829题目的解法及优化方法。题目要求找出满足一定条件的数列,并对结果取模。文章详细解释了解题思路和算法实现,并提出了使用FMT优化的方法。最后,对于第三个限制条件,作者给出了处理方法。文章最后给出了代码实现。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • [翻译]PyCairo指南裁剪和masking
    裁剪和masking在PyCairo指南的这个部分,我么将讨论裁剪和masking操作。裁剪裁剪就是将图形的绘制限定在一定的区域内。这样做有一些效率的因素࿰ ... [详细]
author-avatar
勇敢的刺鸟宝贝4164053383
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有