美文网首页编程开发
音视频知识梳理

音视频知识梳理

作者: Johnny_Wu | 来源:发表于2021-02-28 14:49 被阅读0次

    一、整体播放策略

    (1)iOS自带的播放控件
    - AVPlayer、AVQueuePlayer
    只能通过路径播放,可以播放本地视频与云视频
    - AVSampleBufferDisplayLayer
    可以实现按帧播放
    (2)第三方
    - ijkPlayer
    默认只支持按路径播放,可以通过IO管道的方式,改为按帧播放方式
    (3)组合方式
    系统硬解VideoToolBox/ffmpeg软解 + OpenGL显示

    二、VideoToolBox硬解

    CMSampleBuffer = CMTime + FormatDesc(sps,pps) + CMBlockBuffer
    CMBlockBuffer:编码的图像数据结构
    CVPixelBuffer:解码后的图像数据结构

    VTDecompressionSessionRef decodeSession

    (CMSampleBuffer+decodeSession)—>VTDecompressionSessionDecodeFrame—>CVPixelBuffer

    三、ffmpeg解码

    AVPacket—>AVFrame
    1、创建AVCodec codec = avcodec_find_decoder(AV_CODEC_ID_H264)
    2、创建AVCodecContext codecCtx = avcodec_alloc_context3(codec);
    3、avcodec_open2(codecCtx, codec, NULL)
    4、视频帧组装成 AVPacket
    5、avcodec_send_packet(codecCtx, &packet)
    6、avcodec_receive_frame(codecCtx, AVFrame)

    (1)AVFrame中取到YUV数据(这里的是非交叉的)
    Y:
    AVFrame->data[0] 存Y数据的buffer
    AVFrame->linesize[0] Y数据一段的长度,一般等于像素width。有几段呢,就得看height

    AVFrame->data[1] 存U数据的buffer
    AVFrame->linesize[1] U数据一段的长度,一般等于像素width/2。有几段呢,就得看height/2

    AVFrame->data[2] 存V数据的buffer
    AVFrame->linesize[2] V数据一段的长度,一般等于像素width/2。有几段呢,就得看height/2

    比如存到NDData里面:
    width = MIN(linesize, width);
    NSMutableData *md = [NSMutableData dataWithLength: width * height];
    Byte *dst = (Byte *)md.mutableBytes;
    for (NSUInteger i = 0; i < height; ++i) {
    memcpy(dst, src, width);
    dst += width;
    src += linesize;
    }
    return md;

    AVFrame->data只能以linesize作为取值累积量,这样才不会出现越界或者取错数据

    (2)IOS硬解得到的CVImageBufferRef,其中的YUV是交叉存储的
    CVImageBufferRef imageBuffer =CMSampleBufferGetImageBuffer(sampleBuffer);

    四、ffmpeg其他一些重要信息

    (1)主要结构体
    AVFormatContext:文件信息上下文,其中就包含了流信息结构AVStream

    AVStream:流信息,其中就包含具体的音视频格式信息上下文AVCodecContext

    AVCodecContext:具体的音视频格式信息。要解码音视频,需要从这里拿到相关信息,比如AVCodecID

    AVPacket:未解码的音视频内容。比如视频的每一帧数据,就体现在一个packet中

    AVFrame:解码后的音视频内容

    (2)H264的两种格式 Annex-B与AVCC
    - Annex-B
    0000000167(SPS)+0000000168(PPS)+0000000165(IDR)+其他帧

    - AVCC
    extradata+NAL长度+NAL+NAL长度+NAL。。。
    NAL长度所占字节、SPS与PPS等包含在extradata中
    

    (3)extradata
    解码AVCC需要与视频合成都需要extradata,如何构造extradata
    - extradata(Annex-B)
    - extradata(AVCC)
    - extradata(Annex-B) 转换为 extradata(AVCC)

    (4)timebase
    ffmpeg中的一种时间单位,包含了与秒的对应关系,都可以转换为秒。
    - pts,dts,duration
    pts * timebase = 以秒为单位的值

    (5)解码流程
    - 解Annex-B的H264
    - 解AVCC的H264

    (6)编码流程
    - 生成AVCodecContext,要设置分辨率,pix_fmt等参数
    - avcodec_open2(codecCtx, codec, NULL)
    - avcodec_send_frame(codecCtx, frame);
    - avcodec_receive_packet(codecCtx, pkt);

    (7)H264 合成mp4流程
    - avformat_alloc_output_context2 创建输出的文件
    - avformat_new_stream 创建视频流
    - 构造AVCodecContext,赋值给AVStream->codecpar
    - 设置timebase,AVStream->time_base
    - avio_open打开输出文件
    - avformat_write_header写入头
    - av_interleaved_write_frame 写入packet
    - av_write_trailer 写入结尾

    五、OpenGL

    简单说就是直接操作GPU进行图片的渲染。OpenGL有自己的渲染管线,提供给外部修改的有顶点处理和片元处理。
    顶点处理:我们可以为OpenGL提供顶点,相当于构建了图片的外部框架(顶点坐标)
    片元处理:提供图片纹理填充刚才构建的外部框架的内容 (通过纹理坐标可以控制图片的方向)
    然后OpenGL就帮我们写入到帧缓冲区:glDraw

    帧缓冲区不能直接显示,还得关联一个渲染缓冲区,渲染缓冲区又与某个CAEAGLLayer对应。这样就可以显示出来:presentRenderbuffer

    传值:CPU的值,先传到GPU,GPU再传给OpenGL绘制

    OpenGL是通过shader语言操作的,通过shader语音来操控顶点与片元

    六、ijkPlayer的整体流程

    三个线程 读线程+解码线程+显示线程
    读线程:不断从视频文件通过ffmpeg读取到AVPacket,存到videoq队列里面
    解码线程:从videoq拿出AVPacket,通过ffmpeg解码得到AVFrame,然后转化为Frame结构体,存到picq队列
    显示线程:不断从picq取出Frame,然后通过OpenGL渲染

    七、音频相关

    每个采样都得用一个数据类型来存储,可以是float、uint16等。这个会决定音频的大小
    1、音频解码

    • ffmpeg软解
      需要参数:sample_rate(采样率)、channels(声道数)、sample_fmt(AV_SAMPLE_FMT_S16,存储采样的数据格式)、channel_layout(类似channels,两个常量:单声道、立体声)

    nb_samples:一包(帧)acc的一个channel有多少采样。解码成pcm之后,就是通过这些采样来计算buf大小,用来存解码后的pcm数据(av_samples_get_buffer_size)注:一般1024个pcm数据作为ACC的一帧,一个(一帧frame)pcm就是一个采样(非交错存储)

    一般解码后的pcm可能需要重采样,比如sample_fmt是16或32等,或者采样,声道不一致,需要通过swr_convert转换

    非交错的存储方式:NonInterleaved,一个frame只存一个channel,所以frame的大小等于一个channel的采样大小
    交错的存储方式:Interleaved,一个frame存多个channel的数据,所以frame = sampleSize * channel

    • AudioToolBox硬解
      inPutDescription(aac)—>outPutDescription(pcm)
      解码器描述符(Description),告诉系统使用哪个解码器
      AudioConverterFillComplexBuffer 进行解码,input和output的数据都是通过AudioBufferList进行传递
    • 直接用aac库

    2、音频编码

    • ffmpeg软编:软解的逆过程

    • AudioToolBox硬编码:硬解码的逆过程

    3、音频播放

    • AudioSession
    • AudioQueue
    • AudioUnit

    相关文章

      网友评论

        本文标题:音视频知识梳理

        本文链接:https://www.haomeiwen.com/subject/twygfltx.html