多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### 基础:显示过程 1) 设定显示区域,//video mode 2) Yuv覆盖 // 3) 显示图像//rgb格式转yuv,操作力实际操作用 yv12来填充yuv420, 4) 绘制图像//位置,高度,宽度,缩放大小的矩阵参数。 一届理解:桌子,白底桌布,原始花纹,变化的花纹。 【待补充 SDL 显示过程 csdn】 ### 关于播放声音的小结 1) 声音的回调函数,尤其那个参数的设置过程(get decode...) 2) Event事件,联系到消费者、生产者问题。**其他处理比如为了预防死循环,自己sleep让系统调度,以方便做其他事 **3) 一个数据结构,audioq,队列。 4) 声音播放对应的函数。 ### 关于播放图像的小结 1) 程序的调用框架 Video_thread  ----call--->     可能  queue_picture(分配空间 等)...............         send event............收到事件,执行 alloc_picture;                           --->(空间分配好后的事件处理)         【待补充】 Main timer--->      schedule_refresh ----> send event......................... 收到事件,执行 video_refresh_timer--->播放函数video_play 待更新一个函数调用框架 2) 大量的使用事件机制 > a) 刷新事件(细心的话,你会发现schedule_refresh触发了timer事件,而后者又调用了schedule_refresh,所谓的形成回路;在此我推断这里的定时器一般情形只能执行一次) b) 退出事件 c) 处理 生产者 消费者 问题(细心的话,**会发现针对 video_q有两处上锁,有两处解锁,**具体的情景后续分析) 3) 图像的处理过程在这里 “形散而神不散”,说句俗话,就是跟tutorial 2不一样,这里 buffer 申请,yuv覆盖申请,拷贝buffer 到yuv覆盖 等等都独立成函数; 4) 有一个全局的数据结构,videoState ,所谓的大数据结构,这个结构涵盖大部分的信息; 5) 在使用音频队列的基础上,同时普及视频队列; ### 场景分析 简要交互过程 Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3197C video_thread,【wait】while (!vp->allocated && !is->quit) Function: alloc_picture(void *), Thread: 0x31844 主线程,【signal】,分配ok Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3197C video_thread,验证一下,写到满? Function: video_display(VideoState *), Thread: 0x31844 主线程,play core,SDL_DisplayYUVOverlay Function: video_refresh_timer(void *), Thread: 0x31844 主线程, size=1,验证 是否会 消费到底 Function: video_refresh_timer(void *), Thread: 0x31844 主线程,【signal】timer中 队列数目减少 Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3197C video_thread,【wait】while (!vp->allocated && !is->quit) Function: alloc_picture(void *), Thread: 0x31844 主线程,【signal】,分配ok Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3197C video_thread,验证一下,写到满? **【线程调度,再次发现没有分配,下一条记录可以发现】** //Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3197C video_thread,【wait】while (!vp->allocated && !is->quit) Function: video_display(VideoState *), Thread: 0x31844 主线程,play core,SDL_DisplayYUVOverlay Function: video_refresh_timer(void *), Thread: 0x31844 主线程, size=1,验证 是否会 消费到底 Function: video_refresh_timer(void *), Thread: 0x31844 主线程,【signal】timer中 队列数目减少 我在下面的图片,对每一帧的过程 进行了标示,细心甚至会发现第4帧出现时  有一个申请内存的小插曲。 ### ![](https://box.kancloud.cn/2016-02-22_56cae4b7e4165.jpg) 场景分析二:一个完整的交互过程 直接上图。 ![](https://box.kancloud.cn/2016-02-22_56cae4b80e031.jpg) //分析如上,不同,主要就是timer 还没有触发,所以就执行下一次的wait. //其实场景切换,不过与vedio_thread,主线程的分配,还有timer的播放; . ps:如果wait满足的话,那么下一次还是在原来线程里执行; 鉴定真实的wait就是下一次执行不在这个线程里; 一句话,这里的wait表示执行到这块代码,并不表示一定会wait. **队列的数目,从来没有超过1,这就是作者说的有了就要用,没有就要取;想起这里使用的同步量是mutex;** 其他:程序中有2个wait, 但下面这个wait从来没有执行过,后续可以增加分析 ~~~ while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { SDL_CondWait(is->pictq_cond, is->pictq_mutex); } ~~~ 唯一长期执行的wait是如下代码: ~~~ /* wait until we have a picture allocated */ SDL_LockMutex(is->pictq_mutex); while (!vp->allocated && !is->quit) { SDL_CondWait(is->pictq_cond, is->pictq_mutex); } SDL_UnlockMutex(is->pictq_mutex); ~~~ ### 最后一帧的处理 那就是当所有的12帧图像播放完毕后,代码的行为。以下是log 经过分析,这是图像的最后一帧,当前的vedio picture已经分配,所以不会出现分配的情景。 Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3AD14 video_thread, 队列增加 size=1 Function: video_display(VideoState *), Thread: 0x3ABEC 主线程,play core,SDL_DisplayYUVOverlay Function: video_refresh_timer(void *), Thread: 0x3ABEC 主线程, size=1,验证 是否会 消费到底 Function: video_refresh_timer(void *), Thread: 0x3ABEC 主线程,【signal】timer中 队列数目减少 Function: queue_picture(VideoState *, AVFrame *), Thread: 0x3AD14 video_thread, 队列增加 size=1 Function: video_display(VideoState *), Thread: 0x3ABEC 主线程,play core,SDL_DisplayYUVOverlay Function: video_refresh_timer(void *), Thread: 0x3ABEC 主线程, size=1,验证 是否会 消费到底 Function: video_refresh_timer(void *), Thread: 0x3ABEC 主线程,【signal】timer中 队列数目减少 ### 其他问题 (有同事提出,可能哪个锁没有打开,此时待后续分析) 具体说,就是上锁的位置就是那个vp 没有分配的情形, 解锁就是alloc 完毕,或者播放完毕的情形;至于播放完毕会导致锁解开的情形待分析。 解决方案:就是在上锁的位置打log,看上锁代码的log和后续的关键区log 是否会相继出现。\ 如果是相继出现,就证明queue_picture里没有出现 锁mutex等待的情形;//mutex要么0要么1 视频刷新的时间 可能会打乱播放的顺序。待考证 解决方案:那就是修改播放刷新的时间, ### 小结: 这是第一次使用日志的方式处理异步调试 针对关键区域的断点放置,是个学问,针对读取变量或者锁的轮询有不同的方案; 一个视频流完整的对应过程(忽略细节不同),如下代码:,一句话,找到开头和结尾。 Function: queue_picture(VideoState *, AVFrame *), Thread: 0x48CB0 video_thread,【wait】while (!vp->allocated && !is->quit) ...忽略 Function: video_refresh_timer(void *), Thread: 0x48AEC 主线程,【signal】timer中 队列数目减少 附件:完整的调试log(待补充,将会在csdn网盘补充)