ffmpeg视频解码

前言

在学习ffmpeg中, 视频解码是一个常用而基础的功能, 本文结合自己的学习和理解, 实现一个简单的解码器。

流程图

在这里插入图片描述
若干说明:

  1. 其实第一步, avformat_alloc_context 是可以不要的, 因为在open_input 的时候, 如果检测到还没有分配。
  2. 会自动分配的, 所以读者无需多虑, 在这里写出来只是为了初学者知道有这么回事 经过sws_scale得到的frame是经过了变换的frame, 就可以送到显卡来显示了。
  3. sws_scale这一分支, 可以干的事情很多,分辨率转化,采样格式转化之类的都可以搞定。

代码实现

#include <iostream>

#ifdef __cplusplus
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
#else
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#endif


#define VEDIO_FILE1  "D:\\ZLJ\\stuff\\Vedio\\111.yuv"
#define VEDIO_FILE  "D:\\ZLJ\\stuff\\Vedio\\111.mkv"
#undef main

using namespace std;
int main()
{
    AVFormatContext         *pFormatCtx = nullptr;
    AVCodecContext          *pCodecCtx  = nullptr;
    AVCodec                 *pCodec     = nullptr;
    AVCodecParameters       *pCodecParm = nullptr;
    AVPacket                *pPacket    = nullptr;
    AVFrame                 *pFrame     = nullptr;
    AVFrame                 *pFrameYuv  = nullptr;
    SwsContext              *pSwsCtx    = nullptr;
    size_t yuvSize;
    int                     rst = 0, vedioIndex = -1;
    int                     windowW, windowH;
    int                     quit = 1;
    unsigned char           *outBuffer  = nullptr;
    FILE                    *file       = fopen(VEDIO_FILE1, "wb+");

    av_log_set_level(AV_LOG_INFO);

    rst = avformat_open_input(&pFormatCtx, VEDIO_FILE, nullptr, nullptr);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "open input failed\n");
        goto _EIXT2;
    }

    rst = avformat_find_stream_info(pFormatCtx, nullptr);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "find stream info failed\n");
        goto _EIXT2;
    }

    rst = vedioIndex = av_find_best_stream(pFormatCtx,  AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "find best stream failed\n");
        goto _EIXT2;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "the vedioIndex = %d\n", vedioIndex);
    }

    pCodecParm = pFormatCtx->streams[vedioIndex]->codecpar;
    pCodec = avcodec_find_decoder(pCodecParm->codec_id);
    if (pCodec == nullptr) {
        av_log(nullptr, AV_LOG_ERROR, "find decoder failed \n");
        goto _EIXT2;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "width = %d, height = %d\n", pCodecParm->width, pCodecParm->height);
    }
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (pCodecCtx == nullptr) {
        av_log(nullptr, AV_LOG_ERROR, "alloc avcodec failed \n");
        goto _EIXT2;
    }
    rst = avcodec_parameters_to_context(pCodecCtx, pCodecParm);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "avcodec_parameters_to_context failed\n");
        goto _EIXT3;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "from codecCtx width = %d, height = %d\n", pCodecCtx->width, pCodecCtx->height);
    }

    rst = avcodec_open2(pCodecCtx, pCodec, nullptr);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "avcodec_open2 failed\n");
        goto _EIXT4;
    }

    pPacket = av_packet_alloc();
    if (pPacket == nullptr) {
        av_log(nullptr, AV_LOG_ERROR, "av_packet_alloc failed\n");
        goto _EIXT5;
    }
    av_init_packet(pPacket);
    pFrame      = av_frame_alloc();
    pFrameYuv   = av_frame_alloc();

    windowH     = pCodecParm->height;
    windowW     = pCodecParm->width;
    windowH     = (windowH >> 4) << 4;
    windowW     = (windowW >> 4) << 4;

    outBuffer = static_cast<unsigned char *>(av_malloc(static_cast<size_t>(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, windowW, windowH, 1))));
    if (outBuffer == nullptr) {
        av_log(nullptr, AV_LOG_ERROR, "malloc out buff failed\n");
        goto _EIXT5;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "malloc out buff for yuv frame successfully\n");
    }
    rst = av_image_fill_arrays(pFrameYuv->data, pFrameYuv->linesize, outBuffer, AV_PIX_FMT_YUV420P, windowW, windowH, 1);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "fill array failed\n");
        goto _EIXT6;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "av_image_fill_array successfully\n");
    }
    pSwsCtx = sws_alloc_context();
    rst = sws_init_context(pSwsCtx, nullptr, nullptr);
    if (rst < 0) {
        av_log(nullptr, AV_LOG_ERROR, "init sws_context failed \n");
        goto _EIXT6;
    }
    pSwsCtx = sws_getContext(pCodecParm->width, pCodecParm->height,  pCodecCtx->pix_fmt,
                             windowW, windowH, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr);
    if (pSwsCtx == nullptr) {
        av_log(nullptr, AV_LOG_ERROR, "get sws context failed\n");
        goto _EXIT7;
    }
    else {
        av_log(nullptr, AV_LOG_INFO, "sws_getContext implement successfully\n");
    }
    yuvSize = static_cast<size_t>(windowH * windowW);

    while (quit) {
        while (1) {
            if (av_read_frame(pFormatCtx, pPacket) < 0) {
                quit = 0;
                break;
            }
            if (pPacket->stream_index == vedioIndex) {
                rst = avcodec_send_packet(pCodecCtx, pPacket);
                if (rst < 0) {
                    av_log(nullptr, AV_LOG_ERROR, "send packet faild\n");
                    continue;
                } else {
                    break;
                }
            }
        }
        while (rst >= 0) {
            rst = avcodec_receive_frame(pCodecCtx, pFrame);
            if (rst == AVERROR(EAGAIN) || rst == AVERROR_EOF)
                break;
            if (rst == 0) {
                rst = sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pFrame->height, pFrameYuv->data, pFrameYuv->linesize);
                if (rst < 0) {
                    av_log(nullptr, AV_LOG_ERROR, "sws_scale implement failed\n");
                    break;
                }
                fwrite(pFrameYuv->data[0], 1, yuvSize, file);
                fwrite(pFrameYuv->data[1], 1, yuvSize/4, file);
                fwrite(pFrameYuv->data[2], 1, yuvSize/4, file);
            }
        }
        av_packet_unref(pPacket);
    }

    //flush decoder
    {
        rst = avcodec_send_packet(pCodecCtx, nullptr);
        while (rst >= 0) {
            rst = avcodec_receive_frame(pCodecCtx, pFrame);
            if (rst == AVERROR(EAGAIN) || rst == AVERROR_EOF)
                break;
            if (rst == 0) {
                rst = sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pFrame->height, pFrameYuv->data, pFrameYuv->linesize);
                if (rst < 0) {
                    av_log(nullptr, AV_LOG_ERROR, "sws_scale implement failed\n");
                    break;
                }
                fwrite(pFrameYuv->data[0], 1, yuvSize, file);
                fwrite(pFrameYuv->data[1], 1, yuvSize/4, file);
                fwrite(pFrameYuv->data[2], 1, yuvSize/4, file);
            }
        }
    }
    fclose(file);
    avcodec_send_packet(pCodecCtx, pPacket);
    rst = avcodec_receive_frame(pCodecCtx, pFrame);

_EXIT7:
    sws_freeContext(pSwsCtx);
_EIXT6:
    av_free(outBuffer);
_EIXT5:
    avcodec_close(pCodecCtx);
_EIXT4:
    avcodec_parameters_free(&pCodecParm);
_EIXT3:
    avcodec_free_context(&pCodecCtx);
_EIXT2:
    avformat_close_input(&pFormatCtx);
    return 0;
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值