• ffmpeg入门


    参考:https://blog.csdn.net/leixiaohua1020/article/details/15811977(拜雷神)

       https://blog.csdn.net/qq_36688143/article/details/79162121

       http://dranger.com/ffmpeg/ffmpeg.html

       https://blog.csdn.net/xuyankuanrong/article/details/77531316?locationNum=4&fps=1

    ffmpeg版本(3.3.x)

    一、ffmpeg基本流程介绍:

    解协议的作用,就是将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。

    解封装的作用,就是将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。

    解码的作用,就是将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。

    视音频同步的作用,就是根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。

    二、ffmpeg基本函数介绍:

      1. 注册所有容器格式和CODEC:av_register_all()

    void av_register_all ( void    )

      Initialize libavformat and register all the muxers, demuxers and protocols.

      If you do not call this function, then you can select exactly which formats you want to support.

     

      2. 打开文件:avformat_open_input()

    int avformat_open_input ( AVFormatContext **  ps,
        const char *  url,
        AVInputFormat fmt,
        AVDictionary **  options 
      )

      Open an input stream and read the header.

      The codecs are not opened. The stream must be closed with avformat_close_input().

      Parameters
    ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). May be a pointer to NULL, in which case an AVFormatContext is allocated by this function and written into ps. Note that a user-supplied AVFormatContext will be freed on failure.
    url URL of the stream to open.
    fmt If non-NULL, this parameter forces a specific input format. Otherwise the format is autodetected.
    options A dictionary filled with AVFormatContext and demuxer-private options. On return this parameter will be destroyed and replaced with a dict containing options that were not found. May be NULL.
      Returns
    0 on success, a negative AVERROR on failure.

      3. 从文件中提取流信息:avformat_find_stream_info()

    int avformat_find_stream_info ( AVFormatContext ic,
        AVDictionary **  options 
      )    

      Read packets of a media file to get stream information.

      This is useful for file formats with no headers such as MPEG. This function also computes the real framerate in case of MPEG-2 repeat frame mode. The logical file position is not changed by this function; examined packets may be buffered for later processing.

      Parameters
    ic media file handle
    options If non-NULL, an ic.nb_streams long array of pointers to dictionaries, where i-th member contains options for codec corresponding to i-th stream. On return each dictionary will be filled with options that were not found.
      Returns
    >=0 if OK, AVERROR_xxx on error
      Note
    this function isn't guaranteed to open all the codecs, so options being non-empty at return is a perfectly normal behavior.
      Todo:
    Let the user decide somehow what information is needed so that we do not waste time getting stuff the user does not need.

      4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO

      5. 查找对应的解码器:avcodec_find_decoder()

    AVCodec* avcodec_find_decoder ( enum AVCodecID  id )  

      Find a registered decoder with a matching codec ID.

      Parameters
    id AVCodecID of the requested decoder
      Returns
    A decoder if one was found, NULL otherwise. 

      6. 为上下文分配内存:avcodec_alloc_context3()

    AVCodecContext* avcodec_alloc_context3 ( const AVCodec codec )  

      Allocate an AVCodecContext and set its fields to default values.

      The resulting struct should be freed with avcodec_free_context().

      Parameters
    codec if non-NULL, allocate private data and initialize defaults for the given codec. It is illegal to then call avcodec_open2() with a different codec. If NULL, then the codec-specific defaults won't be initialized, which may result in suboptimal default settings (this is important mainly for encoders, e.g. libx264).
      Returns
    An AVCodecContext filled with default values or NULL on failure. 

      7. 将AVCodecParameters转化为AVCodecContext:avcodec_parameters_to_context()

    int avcodec_parameters_to_context ( AVCodecContext codec,
        const AVCodecParameters par 
      )    

      Fill the codec context based on the values from the supplied codec parameters.

      Any allocated fields in codec that have a corresponding field in par are freed and replaced with duplicates of the corresponding field in par. Fields in codec that do not have a counterpart in par are not touched.

      Returns
    >= 0 on success, a negative AVERROR code on failure.

      8. 解码上下文:avcodec_open2():

    int avcodec_open2 ( AVCodecContext avctx,
        const AVCodec codec,
        AVDictionary **  options 
      )    

      Initialize the AVCodecContext to use the given AVCodec.

      Prior to using this function the context has to be allocated with avcodec_alloc_context3().

      The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for retrieving a codec.

      Warning
    This function is not thread safe!
      Note
    Always call this function before using decoding routines (such as avcodec_receive_frame()).
    avcodec_register_all();
    av_dict_set(&opts, "b", "2.5M", 0);
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec)    
        exit(1);
    
    context = avcodec_alloc_context3(codec);
    
    if (avcodec_open2(context, codec, opts) < 0)
        exit(1);
      Parameters
    avctx The context to initialize.
    codec The codec to open this context for. If a non-NULL codec has been previously passed to avcodec_alloc_context3() or for this context, then this parameter MUST be either NULL or equal to the previously passed codec.
    options A dictionary filled with AVCodecContext and codec-private options. On return this object will be filled with options that were not found.
      Returns
    zero on success, a negative value on error. 

        9.开辟一块内存空间用于保存数据av_malloc():

    void* av_malloc ( size_t  size )  

      Allocate a memory block with alignment suitable for all memory accesses (including vectors if available on the CPU).

      Parameters
    size Size in bytes for the memory block to be allocated
      Returns
    Pointer to the allocated block, or NULL if the block cannot be allocated.

      10.为一帧图片分配内存av_frame_alloc():

    AVFrame* av_frame_alloc ( void    )  

      Allocate an AVFrame and set its fields to default values.

      The resulting struct must be freed using av_frame_free().

      Returns
    An AVFrame filled with default values or NULL on failure.
      Note
    this only allocates the AVFrame itself, not the data buffers. Those must be allocated through other means, e.g. with av_frame_get_buffer() or manually.

      11.计算缓冲区的大小av_image_get_buffer_size():

    int av_image_get_buffer_size ( enum AVPixelFormat  pix_fmt,
        int  width,
        int  height,
        int  align 
      )    

      Return the size in bytes of the amount of data required to store an image with the given parameters.

      Parameters
    [in] align the assumed linesize alignment 

    //参数一:缓冲区格式(需要:AV_PIX_FMT_YUV420P)

    //参数二:缓冲区宽度

    //参数三:缓冲区高度

    //参数四:字节对齐的方式(通用设置:1)

      12.指定缓冲区填充的像素数据类型av_image_fill_arrays():

    int av_image_fill_arrays ( uint8_t dst_data[4],
        int  dst_linesize[4],
        const uint8_t src,
        enum AVPixelFormat  pix_fmt,
        int  width,
        int  height,
        int  align 
      )    

      Setup the data pointers and linesizes based on the specified image parameters and the provided array.

      The fields of the given image are filled in by using the src address which points to the image data buffer. Depending on the specified pixel format, one or multiple image data pointers and line sizes will be set. If a planar format is specified, several pointers will be set pointing to the different picture planes and the line sizes of the different planes will be stored in the lines_sizes array. Call with src == NULL to get the required size for the src buffer.

      To allocate the buffer and fill in the dst_data and dst_linesize in one call, use av_image_alloc().

      Parameters
    dst_data data pointers to be filled in
    dst_linesizes linesizes for the image in dst_data to be filled in
    src buffer which will contain or contains the actual image data, can be NULL
    pix_fmt the pixel format of the image
    width the width of the image in pixels
    height the height of the image in pixels
    align the value used in src for linesize alignment
      Returns
    the size in bytes required for src, a negative error code in case of failure.
     

      13.视频像素数据格式转换上下文sws_getContext():

    struct SwsContext* sws_getContext ( int  srcW,
        int  srcH,
        enum AVPixelFormat  srcFormat,
        int  dstW,
        int  dstH,
        enum AVPixelFormat  dstFormat,
        int  flags,
        SwsFilter srcFilter,
        SwsFilter dstFilter,
        const double *  param 
      )    

      Allocate and return an SwsContext.

      You need it to perform scaling/conversion operations using sws_scale().

      Parameters
    srcW the width of the source image
    srcH the height of the source image
    srcFormat the source image format
    dstW the width of the destination image
    dstH the height of the destination image
    dstFormat the destination image format
    flags specify which algorithm and options to use for rescaling
    param extra parameters to tune the used scaler For SWS_BICUBIC param[0] and [1] tune the shape of the basis function, param[0] tunes f(1) and param[1] f´(1) For SWS_GAUSS param[0] tunes the exponent and thus cutoff frequency For SWS_LANCZOS param[0] tunes the width of the window function
      Returns
    a pointer to an allocated context, or NULL in case of error.
      Note
    this function is to be removed after a saner alternative is written. 

      14.读取帧av_read_frame():

    int av_read_frame ( AVFormatContext s,
        AVPacket pkt 
      )    

      Return the next frame of a stream.

      This function returns what is stored in the file, and does not validate that what is there are valid frames for the decoder. It will split what is stored in the file into frames and return one for each call. It will not omit invalid data between valid frames so as to give the decoder the maximum information possible for decoding.

      If pkt->buf is NULL, then the packet is valid until the next av_read_frame() or until avformat_close_input(). Otherwise the packet is valid indefinitely. In both cases the packet must be freed with av_packet_unref when it is no longer needed. For video, the packet contains exactly one frame. For audio, it contains an integer number of frames if each frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames have a variable size (e.g. MPEG audio), then it contains one frame.

      pkt->pts, pkt->dts and pkt->duration are always set to correct values in AVStream.time_base units (and guessed if the format cannot provide them). pkt->pts can be AV_NOPTS_VALUE if the video format has B-frames, so it is better to rely on pkt->dts if you do not decompress the payload.

      Returns
    0 if OK, < 0 on error or end of file.
     

      15.发送数据包(通俗解释:解压一帧视频压缩数据)avcodec_send_packet():

    int avcodec_send_packet ( AVCodecContext avctx,
        const AVPacket avpkt 
      )    

      Supply raw packet data as input to a decoder.

      Internally, this call will copy relevant AVCodecContext fields, which can influence decoding per-packet, and apply them when the packet is actually decoded. (For example AVCodecContext.skip_frame, which might direct the decoder to drop the frame contained by the packet sent with this function.)

      Warning
    The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE larger than the actual read bytes because some optimized bitstream readers read 32 or 64 bits at once and could read over the end.
    Do not mix this API with the legacy API (like avcodec_decode_video2()) on the same AVCodecContext. It will return unexpected results now or in future libavcodec versions.
      Note
    The AVCodecContext MUST have been opened with avcodec_open2() before packets may be fed to the decoder.
      Parameters
      avctx codec context
    [in] avpkt The input AVPacket. Usually, this will be a single video frame, or several complete audio frames. Ownership of the packet remains with the caller, and the decoder will not write to the packet. The decoder may create a reference to the packet data (or copy it if the packet is not reference-counted). Unlike with older APIs, the packet is always fully consumed, and if it contains multiple frames (e.g. some audio codecs), will require you to call avcodec_receive_frame() multiple times afterwards before you can send a new packet. It can be NULL (or an AVPacket with data set to NULL and size set to 0); in this case, it is considered a flush packet, which signals the end of the stream. Sending the first flush packet will return success. Subsequent ones are unnecessary and will return AVERROR_EOF. If the decoder still has frames buffered, it will return them after sending a flush packet.
      Returns
    0 on success, otherwise negative error code: AVERROR(EAGAIN): input is not accepted in the current state - user must read output with avcodec_receive_frame() (once all output is read, the packet should be resent, and the call will not fail with EAGAIN). AVERROR_EOF: the decoder has been flushed, and no new packets can be sent to it (also returned if more than 1 flush packet is sent) AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush AVERROR(ENOMEM): failed to add packet to internal queue, or similar other errors: legitimate decoding errors.
      

      16.接收一帧解码视频像素数据avcodec_receive_frame():

    int avcodec_receive_frame ( AVCodecContext avctx,
        AVFrame frame 
      )    

      Return decoded output data from a decoder.

      Parameters
    avctx codec context
    frame This will be set to a reference-counted video or audio frame (depending on the decoder type) allocated by the decoder. Note that the function will always call av_frame_unref(frame) before doing anything else.
      Returns
    0: success, a frame was returned AVERROR(EAGAIN): output is not available in this state - user must try to send new input AVERROR_EOF: the decoder has been fully flushed, and there will be no more output frames AVERROR(EINVAL): codec not opened, or it is an encoder other negative values: legitimate decoding errors.

      17.将解码后的数据进行格式转换sws_scale():

    int sws_scale ( struct SwsContext c,
        const uint8_t *const  srcSlice[],
        const int  srcStride[],
        int  srcSliceY,
        int  srcSliceH,
        uint8_t *const  dst[],
        const int  dstStride[] 
      )    

      Scale the image slice in srcSlice and put the resulting scaled slice in the image in dst.

      A slice is a sequence of consecutive rows in an image.

      Slices have to be provided in sequential order, either in top-bottom or bottom-top order. If slices are provided in non-sequential order the behavior of the function is undefined.

      Parameters
    c the scaling context previously created with sws_getContext()
    srcSlice the array containing the pointers to the planes of the source slice
    srcStride the array containing the strides for each plane of the source image
    srcSliceY the position in the source image of the slice to process, that is the number (counted starting from zero) in the image of the first row of the slice
    srcSliceH the height of the source slice, that is the number of rows in the slice
    dst the array containing the pointers to the planes of the destination image
    dstStride the array containing the strides for each plane of the destination image
      Returns
    the height of the output slice.

      Scale the image slice in srcSlice and put the resulting scaled slice in the image in dst.

      Assumes planar YUV to be in YUV order instead of YVU. 

      18.释放数据包av_packet_free():

    void av_packet_free ( AVPacket **  pkt )  

      Free the packet, if the packet is reference counted, it will be unreferenced first.

      Parameters
    packet packet to be freed. The pointer will be set to NULL.
      Note
    passing NULL is a no-op. 

      19.释放帧内存av_frame_free():

    void av_frame_free ( AVFrame **  frame )  

      Free the frame and any dynamically allocated objects in it, e.g.

      extended_data. If the frame is reference counted, it will be unreferenced first.

      Parameters
    frame frame to be freed. The pointer will be set to NULL. 

      20.关闭解码器avcodec_close():

      

    int avcodec_close ( AVCodecContext avctx )  

      Close a given AVCodecContext and free all the data associated with it (but not the AVCodecContext itself).

      Calling this function on an AVCodecContext that hasn't been opened will free the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL codec.       Subsequent calls will do nothing.

      Note
    Do not use this function. Use avcodec_free_context() to destroy a codec context (either open or closed). Opening and closing a codec context multiple times is not supported anymore – use multiple codec contexts instead. 

      21.释放文件以及所有流avformat_free_context():

    void avformat_free_context ( AVFormatContext s )  

      Free an AVFormatContext and all its streams.

      Parameters
    s context to free 

    三、ffmpeg主要结构体介绍:

    AVFormatContext
    封装格式上下文结构体,也是统称全局的结构体,保存了视频文件封装格式相关信息
    AVInputFormat
    每种封装格式(例如:FLV、MKV、MP4、AVI等)对应一个该结构体
    AVStream
    视频文件中每个视频(音频)流对应一个该结构体
    AVCodecContext
    编码器上下文结构体,保存了视频(音频)编解码相关的信息
    AVCodec
    每种视频(音频)编解码器(例如:H.264解码器)对应一个该结构体。
    AVPacket
    存储一帧压缩编码数据
    AVFrame
    存储一帧解码后像素(采样)数据。

    AVFormatContext
    iformat:输入视频的AVInputFormat
    nb_streams:输入视频的AVStream个数
    streams:输入视频的AVStream[]数组
    druation:输入视频的时长(以微秒为单位)
    bit_rate:输入视频码率
    AVInputFormat
    name:封装格式名称
    long_name:封装格式的长名称
    extensions:封装格式的扩展名
    id:封装格式的ID
    一些封装格式处理的接口函数
    AVStream
    id:序号
    codec:该流对应的AVCodecContext
    time_base:该流的时基
    r_frame_rate:该流的帧率
    AVCodecContext
    codec:编解码器的AVCodec
    width,height:图像的宽高(只针对视频)
    pix_fmt:像素格式(只针对视频)
    sample_rae:采样率(只针对音频)
    channels:声道数(只针对音频)
    sample_fmt:采样格式(只针对音频)
    AVCodec
    name:编解码器名称
    long_name:编解码器的长名称
    type:编解码器类型
    id:编解码器ID
    一些编解码的接口函数
    AVPacket
    pts:显示时间戳
    dts:解码时间戳
    data:压缩编码数据
    size:压缩编码数据大小
    stream_index:所属的AVStream
    AVFrame
    data:解码后的图像像素数据(音频采样数据)
    linesize:对视频来说是图像中一行像素的大小;对应音频来说是整个音频帧的大小
    width,height:图像的宽高(只针对视频)
    key_frame:是否为关键帧(只针对视频)
    pict_type:帧类型(值针对视频)。例如:I,P,B

    (1)  AVFormatContent:

      Format I/O context. #include <avformat.h>

      关键字段:

    unsigned int  nb_streams
      Number of elements in AVFormatContext.streams.
     
    AVStream **  streams
      A list of all streams in the file
    struct AVInputFormat iformat
      The input container format.
    int64_t  duration
      Duration of the stream, in AV_TIME_BASE fractional seconds.

    (2)  AVStream:

      Stream structure. #include <avformat.h>

      关键字段:

    AVCodecParameters codecpar

    (3)  AVCodecParameters:

      This struct describes the properties of an encoded stream. #include <avcodec.h>

      关键字段:

    enum AVMediaType  codec_type
      General type of the encoded data.
     
    enum AVCodecID  codec_id
      Specific type of the encoded data (the codec used).  
    enum AVMediaType
    Enumerator
    AVMEDIA_TYPE_UNKNOWN 

    Usually treated as AVMEDIA_TYPE_DATA.

    AVMEDIA_TYPE_VIDEO   
    AVMEDIA_TYPE_AUDIO   
    AVMEDIA_TYPE_DATA 

    Opaque data information usually continuous.

    AVMEDIA_TYPE_SUBTITLE   
    AVMEDIA_TYPE_ATTACHMENT 

    Opaque data information usually sparse.

    AVMEDIA_TYPE_NB 
    enum AVCodecID

      Identify the syntax and semantics of the bitstream.

      The principle is roughly: Two decoders with the same ID can decode the same streams. Two encoders with the same ID can encode compatible streams. There may be slight deviations from the principle due to implementation details.

      If you add a codec ID to this list, add it so that

    1. no value of an existing codec ID changes (that would break ABI),
    2. it is as close as possible to similar codecs

      After adding new codec IDs, do not forget to add an entry to the codec descriptor list and bump libavcodec minor version. 

      e.g  AV_CODEC_ID_H264 

    (4)AVCodecContext:

      main external API structure. #include <avcodec.h>

      关键字段:

    int  width
      picture width / height.
     
    int  height
    enum AVPixelFormat  pix_fmt
      Pixel format, see AV_PIX_FMT_xxx.  

    (5)AVCodec:

      AVCodec.  #include <avcodec.h>

      关键字段:

    const char *  name
      Name of the codec implementation.  

    (6)AVFrame:

      This structure describes decoded (raw) audio or video data. #include <frame.h>

      关键字段:

    uint8_t data [AV_NUM_DATA_POINTERS]
      pointer to the picture/channel planes.
     
    int  linesize [AV_NUM_DATA_POINTERS]
      For video, size in bytes of each picture line.
      #define AV_NUM_DATA_POINTERS   8

    (7)AVPacket:

      This structure stores compressed data. #include <avcodec.h>

      关键字段:

    int  stream_index

    四、ffmpeg基本操作:

    (1)首先初始化库:

    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>

    #include <stdio.h>
    int main(int argc, char *argv[]) {   av_register_all();

    (2)打开文件:

    AVFormatContext *pFormatCtx = NULL;
    
    // Open video file
    if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) < 0)
      return -1; // Couldn't openfile

    (3)查看文件中的流信息:

    // Retrieve stream information
    if(avformat_find_stream_info(pFormatCtx, NULL)<0)
      return -1; // Couldn't find stream information

    (4)遍历所有流,找到第一个视频流:

    int i;// Find the first video stream
    int videoStream=-1;
    for(i=0; i<pFormatCtx->nb_streams; i++)
      if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO) {
        videoStream=i;
        break;
      }
    if(videoStream==-1)
      return -1; // Didn't find a video stream
    
    // Get the video stream
    AVStream *stream=
    pFormatCtx->streams[videoStream];
    // Get the codec id for the video stream
    enum AVCodecID codec_id=stream->codecpar->codec_id;

    (5)找到编解码器:

    AVCodec *pCodec = avcodec_find_decoder(codec_id);
    if (!pCodec) {
        printf("Codec not found
    ");
        return -1;
    }

    (6)分配AVCodecContext空间:

    AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);   
    if (!pCodecCtx){        
        printf("Could not allocate video codec context
    ");        
        return -1;    
    }

    (7)将AVCodecParameters转化为AVCodecContext:

    avcodec_parameters_to_context(pCodecCtx, stream->codecpar);

    (8)打开解码器:

    /* open it */
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
      printf("Could not open codec ");
      exit(1);
    }

     (9)获取配置视频信息:

    //获取配置视频信息
    //文件格式、文件的宽高、解码器的名称等等......
    printf("视频文件的格式:%s",pFormatCtx->iformat->name);
    //返回的单位是:微秒
    printf("视频的时长:%ld秒",(pFormatCtx->duration)/1000000);
    //获取宽高
    printf("视频的宽高:%d x %d = ",pCodecCtx->width,pCodecCtx->height);
    //解码器的名称
    printf("解码器的名称:%s",pCodec->name);

    (10)从输入文件循环读取每一帧数据:

    //读取的帧数据缓存到那里(开辟一块内存空间用于保存)
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //缓存一帧数据(就是一张图片)
    AVFrame* in_frame_picture = av_frame_alloc();
    //定义输出一帧数据(缓冲区:YUV420p类型)
    AVFrame* out_frame_picture_YUV42P = av_frame_alloc();
    //av_image_get_buffer_size: 计算缓冲区的大小
    //参数一:缓冲区格式(需要:AV_PIX_FMT_YUV420P)
    //参数二:缓冲区宽度
    //参数三:缓冲区高度
    //参数四:字节对齐的方式(通用设置:1)
    //回忆:以前学习结构体的大小的时候,讲解过字节对齐(字节对齐目的:为了提高读取的效率或者说性能)
    int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);
    //开辟缓存空间
    uint8_t *out_buffer = (uint8_t *)av_malloc(buffer_size);
    //av_image_fill_arrays: 指定缓冲区填充的像素数据类型(要求:YUV420P视频像素数据格式)
    //参数分析
    //参数一:填充的数据
    //参数二:每一行大小
    //参数三:缓冲区
    //参数四:填充的像素数据格式(在这里我们要求:YUV420P)
    //参数五:每一帧宽度(视频宽)
    //参数六:每一帧高度(视频高)
    //参数七:字节对齐的方式(通用设置:1)
    av_image_fill_arrays(out_frame_picture_YUV42P->data,out_frame_picture_YUV42P->linesize,out_buffer
                ,AV_PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,1);
    
    int y_size = 0,u_size = 0,v_size = 0,frame_index = 0;
    
    //打开文件
    //解码之后的输出文件地址
    const char* outFilePath = NULL;

    FILE* out_file_yuv = fopen(outFilePath,"wb"); if(out_file_yuv == NULL){ printf("文件不存在!"); return -1; } //视频像素数据格式转换上下文 //参数一:输入的宽度 //参数二:输入的高度 //参数三:输入的数据 //参数四:输出的宽度 //参数五:输出的高度 //参数六:输出的数据 //参数七:视频像素数据格式转换算法类型(使用什么算法) //参数八:字节对齐类型,一般都是默认1(字节对齐类型:提高读取效率) SwsContext *sws_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_YUV420P, SWS_BICUBIC,NULL,NULL,NULL); //读取返回值 //>=0:正在读取 //<0:读取失败或者说读取完毕 while (av_read_frame(pFormatCtx,packet) >= 0){ //有视频流帧数据、音频流帧数据、字幕流...... if(packet->stream_index == videoStream){ //avcodec_send_packet: 发送数据包(通俗解释:解压一帧视频压缩数据) //avcodec_receive_frame: 接收一帧解析成功之后的视频像素数据 //第一步:avcodec_send_packet //参数一:解码器上下文 //参数二:数据包(通俗讲解:一帧视频压缩数据) //返回值: //AVERROR(EAGAIN): 当前数据不可用,你可以尝试继续重新发送一次(通俗讲解:继续解码下一帧) //AVERROR_EOF: 解码完成 //AVERROR(EINVAL): 当前你一个解码器,但是没有打开或者已经关闭了(通俗讲解:不可用) //AVERROR(ENOMEM): 当前解码一帧视频压缩数据发送了异常 avcodec_send_packet(pCodecCtx,packet);      //第二步:avcodec_receive_frame //接收一帧解码视频像素数据 //返回值和avcodec_send_packet返回值含义相同
         if(avcodec_receive_frame(pCodecCtx,in_frame_picture) == 0){ //接下来我要将解码后的数据(视频像素数据,保存为YUV420P文件) //在这个地方需要指定输出文件的类型(格式转换) //我要将AVFrame转成视频像素数YUV420P格式 //参数一:视频像素数据格式上下文(SwsContext) //参数二:输入的数据(转格式前的视频像素数据) //参数三:输入画面每一行的大小(视频像素数据转换一行一行的转) //参数四:输入画面每一行的要转码的开始位置 //参数五:输出画面数据(转格式后的视频像素数据) //参数六:输出画面每一行的大小 sws_scale(sws_ctx,(const uint8_t *const*)in_frame_picture->data, in_frame_picture->linesize,0,pCodecCtx->height, out_frame_picture_YUV42P->data,out_frame_picture_YUV42P->linesize); //普及: YUV420P格式结构 //Y代表亮度,UV代表色度(人的眼睛对亮度敏感,对色度不敏感) //再深入:计算机图像学相关 //YUV420P格式规定一:Y结构表示一个像素点(一个像素点就是一个Y) //YUV420P格式规定二:四个Y对应一个U和一个V(也就是四个像素点,对应一个U和V) //Y默认情况下:灰度 //计算Y大小:y = 宽x高 y_size = pCodecCtx->width * pCodecCtx->height; u_size = y_size / 4; v_size = y_size / 4; //写入文件 //首先写入Y,再是U,再是V fwrite(out_frame_picture_YUV42P->data[0],1,y_size,out_file_yuv); fwrite(out_frame_picture_YUV42P->data[1],1,u_size,out_file_yuv); fwrite(out_frame_picture_YUV42P->data[2],1,v_size,out_file_yuv); frame_index++; printf("当前是第%d帧",frame_index); } } }

    (11)关闭流

        av_packet_free(&packet);
        //关闭流
        fclose(out_file_yuv);
        av_frame_free(&in_frame_picture);
        av_frame_free(&out_frame_picture_YUV42P);
        avcodec_close(pCodecCtx);
        avformat_free_context(pFormatCtx);
    }

    解码后的数据为什么要经过sws_scale()函数处理?
     解码后YUV格式的视频像素数据保存在AVFrame的data[0]、data[1]、data[2]中。

      但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。以亮度Y数据为例,

      data[0]中一共包含了linesize[0]*height个数据。但是出于优化等方面的考虑,

      linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。因此需要使用sws_scale()进行转换。

      转换后去除了无效数据,width和linesize[0]取值相等

  • 相关阅读:
    正则表达式常用语法
    JDK源码分析hashmap
    追加数据
    验证域名!!!!!!!!!
    Hadoop(一)之初识大数据与Hadoop
    Hadoop(十)Hadoop IO之数据完整性
    Hadoop(九)Hadoop IO之Compression和Codecs
    Hadoop(八)Java程序访问HDFS集群中数据块与查看文件系统
    Hadoop(七)HDFS容错机制详解
    python连接数据库
  • 原文地址:https://www.cnblogs.com/lucifer1997/p/9712750.html
Copyright © 2020-2023  润新知