音视频-播放原理基础知识SDL

张开发
2026/4/7 22:55:38 15 分钟阅读

分享文章

音视频-播放原理基础知识SDL
基础知识常用术语复用mux/封装把不同的流按照某种容器的规则放入容器这种行为叫做复用mux。将音频流、视频流、字幕流、其他成分按一定的规则组合成视频文件(MP4/FLV等)。复用器比如mp4、flv解复用mux/解封装把不同的流从某种容器中解析出来这种行为叫做解复用(demux)。将一个视频文件(mp4/flv等)按一定的规则拆分成有音频流、视频流、字幕流、其他成分。解复用器比如mp4、flv容器/文件(Conainer/File)即特定格式的多媒体文件比如mp4、flv、mkv等。媒体流Stream表示时间轴上的段连续数据如段声音数据、段视频或一段字幕数据可以是压缩的也可以是非压缩的压缩的数据要关联特定解码器。数据帧/数据包(Frame/Packet)通常1个媒体流是由大量的数据帧组成的对于压缩数据帧对应着编解码器的最小处理单元分属于不同媒体流的数据帧交错存储了容器之中。编解码器Codec)以帧为单位实现压缩数据和原始数据之间的相互转换的编码目的压缩。各种视频编码方式都是为了让视频变得体积更小有利于存储和传输。编码的核心思想是去除冗余信息。空间冗余图像内部相邻像素之间的存在较强的相关性多造成的冗余。时间冗余视频图像序列中的不同帧之间的相关性所造成的冗余。视觉冗余是指人眼不能感知或不敏感的那部分图像信息。信息熵冗余编码冗余人们用于表达某一信息所需要的比特数总比理论上表示该信息所需的比特数要大它们之间的差距就是信息熵冗余或称编码冗余。知识冗余是指在有些图像中还包含与某些研组知识有关的信息。解码将第2张和第一张图片整合起来这个过程叫做解码。解码完获取到完整的图像数据然后就可以进行展示了。编解码目的压缩数据。比如一个1920*1080分辨率 32位 每秒30帧 的视频一秒钟需要1920*1080*32*30的大小大概为237M的数据。视频编码视频编码就是指通过特定的压缩技术将某个视频格式文件转换成另一种视频格式文件的方式。视频流传输中最重要的编解码标准有国际电联CITU-T的H.261、H.263、H.264等运动静止图像专家组由ISO国际标准化组织与IEC国际电子委员会于1988年联合成立的MPEG系列标准MPEG1、MPEG2、MPEG4、AVC等。而H.265则被称为是ITU-T.H.264/MPEG-4AVC标准的继任者又称为高效率视频编码简称HEVC。其中ITU.T.H.264/MPEG-4AVC是ITU-T与ISO/IEC联手合作指定的新标准。ITU-T方面称之为H.264。但ISO/IEC的则将这个新标准归纳于MPEG系列称为MPEG-4AVC。NALUSPS序列参数集SPS中保存了一组编码视频序列的全局参数。PPS图像参数集对应的是一个序列中某一辐图像或者某几幅图像的参数IPB帧I帧帧内编码帧关键帧。I帧可以看作一个图像经过压缩之后的产物可以单独解码出一个完整的图像。P帧前向预测编码帧记录了本帧跟之前的一个关键帧的差别解码时需要用之前缓存的画面叠加本帧的差别生成最终画面。B帧双向预测编码帧记录了本帧与前后帧的差别解码需要参考前面一个I帧或者P帧同时需要后面的P帧才能解码一张完整的图像。颜色空间RGB牛顿利用三棱镜将太阳光分解成彩色的光带。各色光因其所形成的折射角不同而彼此分离就像彩虹一样所以白光能分解成多种彩色的光。经过不断地实验发现红绿蓝(RGB)三种色光无法被分解所以称它为三原色光等量的三原色光相加会变成白光。在计算机里R、G、B也被称为基色分量。它们的取值分别从0到255一共255个等级256是2的8次方。所以任何颜色都可以用R、G、B三个值的组合表示。YUV主要运用于优化彩色视频信号的传输与RGB相比YUV只需要占用极少的频宽RGB需要三个独立的视频信号同时传输。YUV中Y代表亮度也称为灰阶值U与V表示的则是色度色调饱和度也可以记作YCbCr。如果只有Y数据那么表示的图像就是黑白的。使用YUV格式才能极大的去除冗余信息人眼对亮点信息更敏感对色彩敏感度不高也就是说可以压缩UV数据而人眼难以发现。所以压缩算法的第一步往往把RGB数据转换成YUV数据。对Y压缩一点对UV压缩一多点以平衡图像效果和压缩率。这也是为什么编码选择使用YUV而不是RGBGrayR*0.30G*0.59B*0.11Y0.299R0.587G0.114BU-0.147R0.289G-0.436BV0.615R-0.515G-0.100BRY1.140VGY-0.395U-0.581VBY2.032UNV21与1420YUV因为采样和数据排列方式的不同又分为不同的存储格式。一般Android摄像头输出为NV21格式而不是14201420格式是绝大多数编解码器输入输出格式。大小计算width*heightwidth/2*height/2width/2height/2width*heightwidth*height/4*2width*height*3/2H264码流分析H.264码流文件分2层VCL(Video Coding Layer)视频编码层负责高效的视频内容表示VCL数据即编码处理它表示被压缩编码后的视频数据序列。NAL(Network Abstraction Layer)网络提取层负责以网络所要求的恰当方式对数据进行打包和传送是传输层。不管在本地播放还是网络播放都要通过这一层来传输。如VCL就是被压缩后的原始数据在VCL数据封装到NAL单元中之后才可以用于传输或存储。片和宏块一帧图片经过H.264编码器之后就被编码为一个或多个片slice每个片包含整数个宏块至少一个宏块最多包含整个图像宏块NAL单元就是装载着这些slice的载体。Annex BAnnex B格式是一种常见的H.264/H.265HEVC视频编码标准中的字节流格式Byte Stream Format。它主要用于存储和传输视频数据尤其是在MPEG-2 TS传输流和实时流媒体传输中。通过起始码分隔NALU适合实时流媒体传输和广播系统。它与AVCC格式的主要区别在于NALU的分隔方式Annex B使用起始码而AVCC使用长度字段。一般H.264编码器的默认输出为起始码NALU。起始码为0x00000001或0x000001。每个NAL包含1个字节的NAL Header与若干整数字节的负荷数据EBSP构成。其中Nal Header占用1个字符此字节低5位表示NALU类型。Annex B格式的特点起始码Start CodeAnnex B格式使用起始码来分隔视频帧NALU网络抽象层单元。起始码是一个特殊的字节序列通常是0x000001或0x00000001用于标识一个NALU的开始。NALU结构每个NALUNetwork Abstraction Layer Unit包含一个视频帧或视频帧的一部分。NALU之间通过起始码分隔没有额外的长度字段。兼容性Annex B格式广泛应用于实时流媒体传输如RTSP、RTP和广播系统如MPEG-2 TS。许多硬件编码器和解码器如摄像头、手机默认输出或支持Annex B格式。与AVCC格式的区别Annex B使用起始码分隔NALU而AVCC格式如MP4文件中使用的格式在NALU前使用长度字段通常是4字节来标识NALU的大小。Annex B更适合流式传输而AVCC更适合文件存储。Annex B格式的示例一个Annex B格式的视频流可能如下所示复制0x00000001 0x67 ... (SPS NALU) 0x00000001 0x68 ... (PPS NALU) 0x00000001 0x65 ... (IDR帧 NALU) 0x00000001 0x41 ... (P帧 NALU) ...0x00000001是起始码。0x67、0x68、0x65等是NALU的类型标识符如SPS、PPS、IDR帧等。Annex B格式的应用场景实时流媒体如RTSP、RTP协议传输的视频流通常使用Annex B格式。常见的应用包括监控摄像头、视频会议等。广播系统MPEG-2 TS传输流中的H.264/H.265视频通常采用Annex B格式。硬件编码器输出许多硬件编码器如摄像头、手机默认输出Annex B格式的视频流。Annex B格式的转换在某些场景下Annex B格式需要与其他格式如AVCC进行转换Annex B转AVCC去掉起始码改为在NALU前添加长度字段。AVCC转Annex B去掉长度字段改为添加起始码。常用的工具和库如FFmpeg可以完成这种转换。图像采集-Camera视频采集-MediaProjectionthis.virtualDisplay this.mediaProjection.createVirtualDisplay(virtualDisplayName, width, height, density, flags, surface, (VirtualDisplay.Callback)null,null);获得MediaProjection后调用createVirtualDisplay创建虚拟显示器VirtualDisplay即会将手机屏幕镜像到虚拟显示器上在createVirtrualDisplay时需要传递一个surface。需要获取图像数据即可从这个surface中获取。音频采集-AudioRecordAudioRecord初始化需要一个相关联的声音buffer这个buffer主要是用来保存新的声音数据表明一个AudioRecord对象还没有被读取的声音数据能存放的数据里。采样率录音设备在一秒钟内对声音信号的采样次数采样频率越高声音的还原就越真实。//minBufferSize:采集到的声音需要缓冲到一个缓冲区这个是缓冲区的大小 int minBufferSize AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); //MediaRecorder.AudioSource.MIC:采集的源是麦克风。44100:采样率。AudioFormat.CHANNEL_IN_MONO:声道数单声道/双声道。 //AudioFormat.ENCODING_PCM_16BIT:量化位16bit2个字节表示每个声音数据占2个字节 new AudioRecord(MediaRecorder.AudioSource.MIC,44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,minBufferSize);编解码-MediaCodec从编码器创建一个画布画布上的图像会被编码器自动编码。Surface surface MediaCodec.createInputSurface();MediaCodec类可用于访问Android底层的多媒体编码器例如编码器、解码器组件。它是Android底层多媒体支持基础架构的一部分。Android底层多媒体模块采样的是OpenMax框架任何Android底层编解码模块的实现都必须遵循OpenMax标准。Google官方默认提供了一系列的软件编解码器而硬件编解码功能则需要芯片厂商依照OpenMax框架标准来完成。所以一般采样不同芯片型号的手机硬件编解码的实现和性能是不同的。MediaCodec流程详见MediaCodec | API reference | Android Developers网络应用层协议-RTMPReal Time Messaging Protocal实时消息传输协议基于TCP的应用层协议TCP/IP四层模型LIBRTMPc语言开源RTMP库封装了socket建立TCP通信并实现了RTMP数据的收发正如OKHTTP封装了socket完成HTTP数据的收发RTMP直播实现流程RTMP视频包数据RTMP包中封装的音视频数据流和FLV格式友好兼容封装音频和视频数据的方式是相同的所以我们只需要按照FLV格式封装音视频即可。rtmp封装AAC第一帧数据时需要音频同步包里面包含AAC的相关信息。音频同步包大小固定为 4 个字节。前两个字节被称为 [AACDecoderSpecificInfo]用于描述这个音频包应当如何被解析。后两个字节称为 [AudioSpecificConfig]更加详细的指定了音频格式。下图为前两个字节 0xAF 0x00AudioTag视频流0x67头帧分为3部分第一部分0不可用1可用1位第二部分代表重要性 2位第三部分真正的帧类型 5位FLVtags结构详见详见FLV 文件格式 - 小小程序员001 - 博客园OpenGLffmpeg使用视频播放原理直播流音频一帧数据44.1kHz 1024个采样点 需要23.2ms1024/44100 0.0232使用队列解码的好处1、缓存。防止网络抖动过程中音视频卡顿2、解耦。读数据一个线程解码音频一个线程需要队列来进行解耦ffmpeg没有api做音视频同步可以参考ffplay.c的实现播放器设计判断容器格式的方式封装格式相关函数解封装流程解封装流程图ffmpeg解码视频播放视频的流程图ffmpeg数据结构之间的关系AVPacket里面有一个index的字段SDL子系统SDL将功能分成下列数个子系统subsystemSDL_INIT_TIMER定时器SDL_INIT_AUDIO音频SDL_INIT_VIDEO视频SDL_INIT_JOYSTICK摇杆SDL_INIT_HAPTIC触摸屏SDL_INIT_GAMECONTROLLER游戏控制器SDL_INIT_EVENTS事件SDL_INIT_EVERYTHING包含上述所有选项视频显示函数介绍SDL_Init()初始化SDL系统SDL_CreateWindow()创建窗口SDL_WindowSDL_CreateRenderer()创建渲染器SDL_RendererSDL_CreateTexture()创建纹理SDL_TextureSDL_UpdateTexture()设置纹理的数据SDL_RenderCopy()将纹理的数据拷贝给渲染器SDL_RenderPresent()显示SDL_Delay()工具函数用于延时SDL_Quit()退出SDL系统SDL数据结构SDL_Window 代表了一个“窗口”SDL_Renderer 代表了一个“渲染器”SDL_Texture 代表了一个“纹理”SDL_Rect 一个简单的矩形结构存储RGB和存储纹理的区别 比如一个从左到右由红色渐变到蓝色的矩形用 存储RGB的话就需要把矩形中每个点的具体颜色 值存储下来而纹理只是一些描述信息比如记 录了矩形的大小、起始颜色、终止颜色等信息 显卡可以通过这些信息推算出矩形块的详细信息。 所以相对于存储RGB而已存储纹理占用的内存 要少的多。窗口-》渲染器-》纹理SDL事件◼ 函数SDL_WaitEvent()等待一个事件SDL_PushEvent()发送一个事件SDL_PumpEvents()将硬件设备产生的事件放入事件队列用于 读取事件在调用该函数之前必须调用SDL_PumpEvents搜集 键盘等事件SDL_PeepEvents()从事件队列提取一个事件◼ 数据结构SDL_Event代表一个事件SDL多线程SDL线程创建SDL_CreateThreadSDL线程等待SDL_WaitTheadSDL互斥锁SDL_CreateMutex/SDL_DestroyMutexSDL锁定互斥SDL_LockMutex/SDL_UnlockMutexSDL条件变量(信号量)SDL_CreateCond/SDL_DestoryCondSDL条件变量(信号量)等待/通知SDL_CondWait/SDL_CondSingalSDL打开YUVSDL打开PCM打开音频设备int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained); // desired期望的参数。 // obtained实际音频设备的参数一般情况下设置为NULL即可。SDL_AudioSpectypedef struct SDL_AudioSpec { int freq; // 音频采样率 SDL_AudioFormat format; // 音频数据格式 Uint8 channels; // 声道数: 1 单声道, 2 立体声 Uint8 silence; // 设置静音的值因为声音采样是有符号的所以0当然就是这个值 Uint16 samples; // 音频缓冲区中的采样个数要求必须是2的n次 Uint16 padding; // 考虑到兼容性的一个参数 Uint32 size; // 音频缓冲区的大小以字节为单位 SDL_AudioCallback callback; // 填充音频缓冲区的回调函数 void *userdata; // 用户自定义的数据 } SDL_AudioSpec;SDL_AudioCallback// userdataSDL_AudioSpec结构中的用户自定义数据一般情况下可以不用。 // stream该指针指向需要填充的音频缓冲区。 // len音频缓冲区的大小以字节为单位1024*2*2。 void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);播放音频数据// 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候将会 播放静音的值。 void SDLCALL SDL_PauseAudio(int pause_on)

更多文章