1.音视频流媒体开发系列(45)GLSurfaceView源码解析&EGL环境
2.SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
3.ffplay视频播放原理分析
4.每日开源:一个巨硬的流媒流媒产品级嵌入式流媒体库
5.SRS流媒体服务器——单机环境搭建和源码目录介绍
音视频流媒体开发系列(45)GLSurfaceView源码解析&EGL环境
查看源码的原则:以常用的API为入口,依据地图、体播体播带着问题、放器放器沿着主线来寻找答案 从事「音视频领域」开发工作有前途吗? GLSurfaceView在使用时,源码源码我们调用的流媒流媒两个主要方法是setEGLContextClientVersion和setRenderer。具体操作在渲染回调中执行,体播体播apk拦截马源码包括onSurfaceCreated、放器放器onSurfaceChanged和onDrawFrame。源码源码 我们的流媒流媒焦点是EGL和GLThread。1.1. setRenderer的体播体播实现:检查GLThread的状态,确保只有一个GLThread存在。放器放器
1.2. GLThread实现:这是源码源码一个Thread的子类,关键逻辑在guardedRun方法中。流媒流媒
1.3. guardedRun(渲染核心逻辑):创建EGLSurface,体播体播获取GL对象,放器放器并在EGLContext和EGLSurface生成并绑定后执行渲染。渲染数据通过eglSwapBuffers显示。
1.4. EglHelper:提供创建EGLSurface、获取GL对象和交换Framebuffer的方法。
音视频免费学习资源:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发 整理了一些面试题、学习资料、教学视频和学习路线图共享在群文件,资料涵盖C/C++、Linux、FFmpeg、WebRTC、RTMP等,免费分享,有需要的可以加入群自取。TextureView +EGL+ GLThread绘制图形
将GLSurfaceView内容简化,剔除SurfaceView继承,保留GL环境,使用GLEnvironment进行渲染。借鉴了[GLSurfaceView的融职商城源码简单分析及巧妙借用]的思路,避免了从头开始实现GL环境的复杂过程。 通过实践,了解了GLSurfaceView内部机制、EGLThread的实现和EGL上下文的意义。在TextureView基础上创建EGL上下文和GLThread以实现OpenGL的绘制。 感谢阅读。SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play
本章内容梳理了SRS在接收到RTMP信息后如何进行转发的过程。在此过程中,首先进行代码梳理,作者也在源码熟悉阶段,可能尚未完全梳理完接受到RTMP后信息如何处理、缓存以及转发给直播用户等内容。
SRS源码中的Play流程如下:
1. 进入play流程:本章内容直接从SrsRtmpConn::stream_service_cycle()方法开始梳理。
2. 在接受流程中,客户类型为SrsRtmpConnFMLEPublish “fmle publish”,而在转发流程中,客户类型为SrsRtmpConnPlay。
3. 在ponent_open创建对应流的解码线程。
3.6解封装处理
接下来是一个for(;;)循环:
(1)响应中断停止、暂停/继续、Seek操作;
(2)判断PacketQueue队列是否满了,如果满了就休眠ms,继续循环;
(3)调用av_read_frame从码流中读取若干个音频帧或一个视频帧;
(4)从输入文件中读取一个AVPacket,判断当前AVPacket是否在播放时间范围内,如果是则调用packet_queue_put函数,根据类型将其放在音频/视频/字幕的PacketQueue中。
四、stream_component_open函数3.5小节讲到,stream_component_open函数负责创建不同流的解码线程。那么它是如何创建解码线程的呢?
4.1创建AVCodecContext
AVCodecContext是编解码器上下文,保存音视频编解码相关的信息。使用avcodec_alloc_context3函数分配空间,使用avcodec_free_context函数释放空间。网店运营教程源码
4.2查找解码器
根据解码器的id,调用avcodec_find_decoder函数,查找对应的解码器。与之类似的一个函数是avcodec_find_encoder,用于查找FFmpeg的编码器。两个函数返回的结构体都是AVCodec。
如果指定了解码器名称,则需要调用avcodec_find_decoder_by_name函数查找解码器。
不管是哪种方式查找解码器,如果没有找到解码器,都会抛异常退出流程。
4.3解码器初始化
找到解码器后,需要打开解码器,并对解码器初始化,对应的函数是avcodec_open2,该函数也支持编码器的初始化。
4.4创建解码线程
判断解码类型,创建不同的解码线程。
switch(avctx->codec_type){ caseAVMEDIA_TYPE_AUDIO://音频...if((ret=decoder_init(&is->auddec,avctx,&is->audioq,is->continue_read_thread))<0)gotofail;...if((ret=decoder_start(&is->auddec,audio_thread,"audio_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_VIDEO://视频...if((ret=decoder_init(&is->viddec,avctx,&is->videoq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->viddec,video_thread,"video_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_SUBTITLE://字幕...if((ret=decoder_init(&is->subdec,avctx,&is->subtitleq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->subdec,subtitle_thread,"subtitle_decoder",is))<0)gotoout;...}线程创建在decoder_start函数中,依然使用SDL创建线程的方式,调用SDL_CreateThread函数。
五、video_thread函数视频解码线程从视频的PacketQueue中不断读取AVPacket,解码完成后将AVFrame放入视频FrameQueue。音频的解码实现和视频类似,这里仅介绍视频的解码过程。
5.1创建AVFrame
AVFrame描述解码后的原始音频数据或视频数据,通过av_frame_alloc函数分配内存,通过av_frame_free函数释放内存。
5.2视频解码
开启for(;;)循环,不断调用get_video_frame函数解码一个视频帧。该函数主要调用了decoder_decode_frame函数解码,decoder_decode_frame函数对音频、面相软件源码下载视频、字幕都进行了处理,主要依靠FFmpeg的avcodec_receive_frame函数获取解码器解码输出的数据。
拿到解码后的视频帧后,会根据音视频同步的方式和命令行的-framedrop选项,判断是否需要丢弃失去同步的视频帧。
命令行带-framedrop选项,无论哪种音视频同步机制,都会丢弃失去同步的视频帧。
命令行带-noframedrop选项,无论哪种音视频同步机制,都不会丢弃失去同步的视频帧。
命令行不带-framedrop或-noframedrop选项,若音视频同步机制为同步到视频,则不丢弃失去同步的视频帧,否则会丢弃失去同步的视频帧。
5.3放入FrameQueue
调用queue_picture函数,将AVFrame放入FrameQueue。该函数内部调用了frame_queue_push函数,采用了环形缓冲区的处理方式,对写指针windex累加。
staticvoidframe_queue_push(FrameQueue*f){ if(++f->windex==f->max_size)f->windex=0;SDL_LockMutex(f->mutex);f->size++;SDL_CondSignal(f->cond);SDL_UnlockMutex(f->mutex);}六、音视频同步ffplay默认采用将视频同步到音频的方式,分以下三种情况:
如果视频和音频进度一致,不需要同步;
如果视频落后音频,则丢弃当前帧直接播放下一帧,人眼感觉跳帧了;
如果视频超前音频,则重复显示上一帧,等待音频,人眼感觉视频画面停止了,但是有声音在播放;
ffplay视频同步到音频的逻辑在视频播放函数video_refresh中实现。该函数的调用链是:main()->event_loop()->refresh_loop_wait_event()->video_refresh。
6.1判断播放完成
调用frame_queue_nb_remaing函数计算剩余没有显示的码头桩基指标源码帧数是否等于0,如果是,则不需要走剩下的步骤。计算过程比较简单,用FrameQueue的size-rindex_shown,size是FrameQueue的大小,rindex_shown表示rindex指向的节点是否已经显示,如果已经显示则为1,否则为0。
6.2播放序列匹配
****分别调用frame_queue_peek_last和frame_queue_peek函数从FrameQueue中获取上一帧和当前帧,上一帧是上次已经显示的帧,当前帧是当前待显示的帧。
(1)比较当前帧和当前PacketQueue的播放序列serial是否相等:
如果不等,重试视频播放的逻辑;
如果相等,则进入(2)流程判断;
注:serial是用来区分是不是连续的数据,如果发生了seek,会开始一个新的播放序列,
(2)比较上一帧和当前帧的播放序列serial是否相等:
如果不相等,则将frame_timer更新为当前时间;
如果相等,不处理并进入下一流程
6.3判断是否重复上一帧
(1)将上一帧lastvp和当前帧vp传入vp_duration函数,通过vp->pts-lastvp->pts计算上一帧的播放时长。
注:pts全称是PresentationTimeStamp,显示时间戳,表示解码后得到的帧的显示时间。
(2)在compute_target_delay函数中,调用get_clock函数获取视频时钟,调用get_master_clock函数获取同步时钟,计算两个时钟的差值,根据差值计算需要delay的时间。
(3)如果当前帧播放时刻(is->frame_timer+delay)大于当前时刻(time),表示当前帧的播放时间还没有到,相当于当前视频超前音频了,则需要将上一帧再播放一遍。
last_duration=vp_duration(is,lastvp,vp);delay=compute_target_delay(last_duration,is);time=av_gettime_relative()/.0;if(time<is->frame_timer+delay){ *remaining_time=FFMIN(is->frame_timer+delay-time,*remaining_time);gotodisplay;}6.4判断是否丢弃未播放的帧
如果当前队列中的帧数大于1,则需要考虑丢帧,只有一帧的时候不考虑丢帧。
(1)调用frame_queue_peek_next函数获取下一帧(下一个待显示的帧),根据当前帧和下一帧计算当前帧的播放时长,计算过程和6.3相同。
(2)满足以下条件时,开始丢帧:
当前播放模式不是步进模式;
丢帧策略生效:framedrop>0,或者当前音视频同步策略不是音频到视频。
当前帧vp还没有来得及播放,但是下一帧的播放时刻(is->frame_timer+duration)已经小于当前系统时刻(time)了。
(3)丢帧时,将is->frame_drops_late++,并调用frame_queue_next函数将上一帧删除,更新FrameQueue的读指针rindex和size。
if(frame_queue_nb_remaining(&is->pictq)>1){ Frame*nextvp=frame_queue_peek_next(&is->pictq);duration=vp_duration(is,vp,nextvp);if(!is->step&&(framedrop>0||(framedrop&&get_master_sync_type(is)!=AV_SYNC_VIDEO_MASTER))&&time>is->frame_timer+duration){ is->frame_drops_late++;frame_queue_next(&is->pictq);gotoretry;}}七、渲染ffplay最终的图像渲染是由SDL完成的,在video_display中调用了SDL_RenderPresent(render)函数,其中render参数是最开始在main函数中创建的。在渲染之前,需要将解码得到的视频帧数据转换为SDL支持的图像格式。转换过程在upload_texture函数中实现,细节不在此处分析。
音频类似,如果解码得到的音频不能被SDL支持,需要对音频进行重采样,将音频帧格式转换为SDL支持的格式。
八、小结本文从整体播放流程出发,介绍了ffplay播放器播放媒体文件的主要流程,不深陷于代码细节。同时,对FFmpeg的一些常用函数有了一些了解,对我们自己手写一个简单的播放器有很大的帮助。
----------END----------每日开源:一个巨硬的产品级嵌入式流媒体库
哈喽,我是老吴。
今天分享一个比较复杂的开源项目:live 是一个开源的流媒体库,用于实现实时流媒体的传输和处理。它提供了一套跨平台的 C++ 类库,帮助快速构建高效、可靠的流媒体服务器和客户端应用程序。
live的代码量庞大,约9w行代码。如果专注于核心逻辑,代码量缩减到约8K行。使用live,你可以收获高效可靠的流媒体库,了解产品级的C++项目设计,掌握音视频基础知识,甚至获得基于select()的C++事件循环库。live在媒体播放器、流媒体服务器、视频监控系统等领域应用广泛,如VLC、FFmpeg、GStreamer均使用live实现流媒体的接收和播放。
live基于C++,语法相对简单,适合专注于学习C++类设计和编写专业的C++软件。为了理解源码,需要补充多媒体、流媒体的理论知识。通过阅读和运行相关应用,加深对理论知识的理解。
编译live库后,会生成4个静态库:libBasicUsageEnvironment.a和libUsageEnvironment.a用于实现事件循环、上下文管理、任务管理等;libliveMedia.a负责多媒体流化,包括音视频编解码、流媒体协议实现;libgroupsock.a负责网络IO功能,核心是TCP、UDP的读写。简单示例是RTP传输MP3音频,涉及server和client两个程序。
server程序的核心逻辑包括准备运行环境、设置数据来源、设置数据目的地。TaskScheduler用于任务管理,基于select()实现事件循环。BasicUsageEnvironment用于上下文管理。数据流化本质是网络传输,Source和Sink分别表示数据源和目的地,本例中Source是MP3FileSource,Sink是MPEG1or2AudioRTPSink。client端程序同样初始化Source和Sink。
RTP协议简介,RTP(Real-time Transport Protocol)是一种用于实时传输音频和视频数据的网络传输协议,基于UDP,用于在IP网络上传输实时媒体数据。RTP协议设计目标是提供低延迟、高效率的传输,以满足实时应用需求。主要特点包括时间戳、序列号、负载类型、NACK反馈和RTCP(Real-time Transport Control Protocol)等。
关键问题是如何实现数据一帧帧流化?关注点不是具体音视频格式解析或特定协议实现,而是live对音视频流化的整体框架。通过示例分析,live本质上将音视频数据逐帧解码,通过RTP协议经网络发送。live封装了多种数据Source和Sink,但无需详细了解每个概念。仍以RTP传输MP3数据为例,分析live的工作流程。
首先,需要对相关类的关系有大概概念:MediaSource是所有Source的父类,各种具体音视频Source基于其派生;MediaSink是所有Sink的父类,派生出FileSink、RTPSink等众多Sink类。Sink类最关键的成员函数是startPlaying(),用于使用Source对象获取帧数据,然后发送至网络。
RTP传输MP3的主要逻辑包括准备就绪后调用MediaSink::startPlaying()启动数据流化,在packFrame()调用Source对象的getNextFrame()。getNextFrame()最终调用MP3FileSource的doGetNextFrame(),负责MP3音频解码,解码完成后,回调afterGettingFrame(),正常时调用sendPacketIfNecessary()发送数据,并添加至事件循环调度器中。一段时间后,MultiFramedRTPSink的sendNext()被调用,推动新一帧数据传输,直到Source中的所有帧数据被消费。
live如何创建RTSP服务器?通常RTP协议与RTSP协议结合使用,对外提供RTSP服务器服务。RTSP提供控制实时流媒体传输和播放的标准化方式,可以控制播放、暂停、停止、快进、后退等功能。添加几行代码即可创建RTSP服务器。RTSP服务器封装实现RTSP服务,类似HTTP协议,是文本协议。服务器包括接受客户端连接、读取客户端数据、解析和处理数据的操作。
总结,live是一个开源的多媒体流媒体库,支持常见流媒体协议,提供高效可靠的流媒体传输功能,适用于构建流媒体服务器和客户端应用程序。使用live需要熟悉C++编程和网络编程知识,官方提供丰富示例代码,帮助快速熟悉库的使用方法。
SRS流媒体服务器——单机环境搭建和源码目录介绍
启动srs
2. 显示日志信息
3. 确认srs是否正常启动
4. 安全退出正在运行的srs
5. 默认后台启动,调试需修改配置文件为前台
相关视频推荐
SRS-RTMP-WebRTC流媒体服务器入门
全球Star第一的流媒体服务器SRS4.0 WebRTC音视频通话分析
SRS流媒体服务器架构设计及源码分析
免费FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发免费学习地址
纯干货免费分享C++音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 加群免费领取哦~
源码目录介绍
1. trunk目录
2. src下的源码
3. app
4. core
5. kernel 音视频格式相关
6. libs
7. main
8. protocol 流媒体协议相关
9. service
. utest
. 八个目录,二百零三个文件