7.基于FFMPEG将video解码为YUV

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011003120/article/details/81979671

参考资料:
1.雷博博客

继续ffmpeg学习之路。。。

前面写了将音频解码为PCM并通过SDL2进行播放的代码,接下来则是需要对视频进行解码为YUV并通过SDL2进行播放,然后再音视频同步播放,有余力的话,通过QT制作一个简单的音视频播放器,计划好多,一步一步来。。。

视频的解码和音频的解码流程一致,不同的地方也就是解码接口不一样,还有转换部分不一样。

1)解码流程

整个解码流程采用伪代码大致如下:
初始化复用器和解复用器—>获取输入文件的一些信息—->查找解码器并打开—->读出视频数据并解码—>将解码后的数据写入文件中—>结束,做去初始化工作

2)转换

音频是采用SwrContext结构体对音频数据进行重采样,而视频则是因为解码后的YUV数据中会存储一些无效像素,因此每行像素的长度linesize并不等于实际的宽度width,所以需要用sws_scale()进行转换,去除无用数据,使 width和linesize取值相等。

转换使用的是FFMPEG中的libswsscale库,libswscale是一个主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作。

在这份代码中,主要使用了三个函数:

sws_getContext/sws_scale/sws_freeContext

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);

初始化SwsContext结构体,里面的参数分为src 和 dst两部分,根据我们的需要进行填充

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[]);

处理图像数据,转换像素。

sws_freeContext:
函数原型:

void sws_freeContext(struct SwsContext *swsContext);

释放SwsContext结构体。

3)linux下编译命令

gcc test.cpp -o test -I /usr/local/include -L /usr/local/lib -lavformat -lavcodec -lavutil -lswscale

4)代码

#include <stdio.h>

#define __STDC_CONSTANT_MACROS
extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
};

#define debug_msg(fmt, args ...) printf("--->[%s,%d]  " fmt "\n\n", __FUNCTION__, __LINE__, ##args)

int main(int argc, char* argv[])
{
    AVFormatContext *pFormatCtx;  
    AVCodecContext  *pCodecCtx;  
    AVCodec         *pCodec;     
    AVPacket *packet; 
    AVFrame *pFrame,*pFrameYUV;

    struct SwsContext *img_convert_ctx;

    uint8_t *out_buffer;
    int ret, got_picture;
    int i = 0;
    int videoindex = -1;
    char filepath[]="Titanic.ts";

    FILE *fd_yuv = fopen("output.yuv", "wb+");

    av_register_all(); 

    pFormatCtx = avformat_alloc_context();

    if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL) != 0)
    {
        debug_msg("Couldn't open input stream.\n");
        return -1;
    }

    if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
    {
        debug_msg("Couldn't find stream information.\n");
        return -1;
    }

    for(i = 0; i < pFormatCtx->nb_streams; i++) 
    {
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex=i;
            break;
        }
    }
    if(videoindex == -1)
    {
        debug_msg("Didn't find a video stream.\n");
        return -1;
    }

    pCodecCtx = pFormatCtx->streams[videoindex]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec == NULL)
    {
        debug_msg("Codec not found.\n");
        return -1;
    }

    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
    { 
        debug_msg("Could not open codec.\n");
        return -1;
    }

        packet=(AVPacket *)av_malloc(sizeof(AVPacket));

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();
    out_buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); 

    debug_msg("----------->width is %d, height is %d, size is %d,\n", 
        pCodecCtx->width, pCodecCtx->height,
        avpicture_get_size( pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height));

    //av_dump_format(pFormatCtx,0,filepath,0); 

    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 
        pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); 

    while(av_read_frame(pFormatCtx, packet)>=0)
    {
        if(packet->stream_index==videoindex)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
            if(ret < 0)
            {
                debug_msg("Decode Error.\n");
                return -1;
            }

            if(got_picture)
            {
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, 
                    pFrameYUV->data, pFrameYUV->linesize);

                fwrite(pFrameYUV->data[0],1, (pCodecCtx->width*pCodecCtx->height),fd_yuv);
                fwrite(pFrameYUV->data[1],1, (pCodecCtx->width*pCodecCtx->height)/4,fd_yuv);  
                fwrite(pFrameYUV->data[2],1, (pCodecCtx->width*pCodecCtx->height)/4,fd_yuv);
                //fwrite(out_buffer,1, (pCodecCtx->width*pCodecCtx->height)*3/2,fd_yuv);
            }
        }
        av_free_packet(packet);
    }

    fclose(fd_yuv);
    sws_freeContext(img_convert_ctx);

    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/u011003120/article/details/81979671