ffmpeg视频解码

一、视频解码流程

使用ffmpeg解码视频帧主要可分为两大步骤:初始化解码器解码视频帧,以下代码以mjpeg为例

1. 初始化解码器

初始化解码器主要有以下步骤:

(1)查找解码器

cpp 复制代码
// 查找MJPEG解码器
    pCodec = avcodec_find_decoder_by_name(videoCodecName);
    if (pCodec == nullptr) {
        release();
        return false;
    }
    // 分配解码器上下文
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx) {
        release();
        return false;
    }

(2)设置解码器参数

cpp 复制代码
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->width = mVideoSrcWidth; // 视频宽度
    pCodecCtx->height = mVideoSrcHeight; // 视频高度
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ422P; // 或者其他适合的格式
    pCodecCtx->time_base = { 1, mVideoSrcFps }; // 帧率
    pCodecCtx->thread_count = 2;

(3)打开解码器

cpp 复制代码
 // 打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
       
        release();
        return false;
    }

2. 解码视频帧数据

解码视频帧数据主要有以下步骤:

(1)将编码数据送往解码器

cpp 复制代码
   AVPacket* packet = av_packet_alloc();
   if (!packet) {
       release();
       return false;
   }
   
   packet->data = data; // 待解码数据地址
   packet->size = size; // 待解码数据大小
   
   // 发送数据到解码器
   int ret = avcodec_send_packet(pCodecCtx, packet);
   if (ret < 0) {
       release();
       return false;
   }

(2)接收解码数据

cpp 复制代码
ret = avcodec_receive_frame(pCodecCtx, pFrame);

二、使用ffmpeg实现对内存中的视频帧数据解码

以下代码中InitDecoder为初始化解码器接口,DecodeVideoFrame为解码视频帧接口

需注意

(1)解码的色彩空间pCodecCtx->pix_fmt不可随意指定,调用avcodec_send_packet后可能会变化,这与视频帧的编码方式有关

(2)每次接收完解码数据要调用av_frame_unref进行释放,否则会有内存泄漏问题

cpp 复制代码
extern "C" { // ffmpeg为使用C语言库,因此要声明为C语言的方式链接
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavutil/error.h>
}

char videoCodecName[] = "mjpeg";
FILE* out_file = nullptr;

AVFormatContext* pFormatCtx = nullptr;

AVCodecContext* pCodecCtx = nullptr;
const AVCodec* pCodec = nullptr;
AVFrame* pFrame = nullptr;
AVFrame* pFrameYUYV = nullptr;
int yuyv_size = 0;
uint8_t* buffer = nullptr;
SwsContext* sws_ctx = nullptr;

int      mVideoSrcWidth = 1920;
int      mVideoSrcHeight = 1080;
int      mVideoSrcFps = 30;

bool InitDecoder() {
    fopen_s(&out_file, "test_yuv.yuv", "wb");
    
    // 查找MJPEG解码器
    pCodec = avcodec_find_decoder_by_name(videoCodecName);
    if (pCodec == nullptr) {
        release();
        return false;
    }
    // 分配解码器上下文
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (!pCodecCtx) {
        release();
        return false;
    }
    pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecCtx->width = mVideoSrcWidth; // 视频宽度
    pCodecCtx->height = mVideoSrcHeight; // 视频高度
    pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ422P; // 或者其他适合的格式
    pCodecCtx->time_base = { 1, mVideoSrcFps }; // 帧率
    pCodecCtx->thread_count = 2;
    
    // 打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
       
        release();
        return false;
    }

    // 分配帧
    pFrame = av_frame_alloc();
    pFrameYUYV = av_frame_alloc();
    if (!pFrame || !pFrameYUYV) {
        release();
        return false;
    }

    // 分配YUYV帧的缓冲区
    yuyv_size = av_image_get_buffer_size(AV_PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, 1);
    buffer = (uint8_t*)av_malloc(yuyv_size * sizeof(uint8_t));
    if (!buffer) {
        release();
        return false;
    }

    av_image_fill_arrays(pFrameYUYV->data, pFrameYUYV->linesize, buffer, AV_PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, 1);

    // 创建图像转换上下文
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
        pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUYV422,
        SWS_BILINEAR, nullptr, nullptr, nullptr);
    if (!sws_ctx) {
        release();
        return false;
    }
}

bool DecodeVideoFrame(uint8_t* data, int size)
{
    AVPacket* packet = av_packet_alloc();
    if (!packet) {
        release();
        return false;
    }
    
    packet->data = data;
    packet->size = size;
    
    // 发送数据到解码器
    int ret = avcodec_send_packet(pCodecCtx, packet);
    if (ret < 0) {
        release();
        return false;
    }

    // 循环接收解码后的帧
    while (ret >= 0) {
        ret = avcodec_receive_frame(pCodecCtx, pFrame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        }
        else if (ret < 0) {
            release();
            return false;
        }
        /******** 将解码后数据进行处理 ********/
        // 转换为YUYV格式
        if (sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUYV->data, pFrameYUYV->linesize) < 0) {
            return false;
        }
        fwrite(pFrameYUYV->data[0], 1, yuyv_size, out_file);
        /******** 将解码后数据进行处理 ********/
       
        av_frame_unref(pFrame); // 将每次接收的解码帧释放掉,否则会内存泄露
        
    }
    av_packet_unref(packet);
    av_packet_free(&packet);
   
    return true;
}

void release()
{
    if (buffer) {
        av_free(buffer);
        buffer = nullptr;
    }

    if (pCodecCtx) {
        avcodec_free_context(&pCodecCtx);
        pCodecCtx = nullptr;
    }
    if (pFormatCtx) {
        avformat_close_input(&pFormatCtx);
        pFormatCtx = nullptr;
    }
    if (pFrame) {
        av_frame_free(&pFrame);
        pFrame = nullptr;
    }
    if (pFrameYUYV) {
        av_frame_free(&pFrameYUYV);
        pFrameYUYV = nullptr;
    }

    if (out_file) {
        fclose(out_file);
    }
    
}
相关推荐
tech讯息1 小时前
商业化内容生产适用企业级视频生成模型云平台推荐|从模型生成至审核分发全链路 AWS 选型方案
人工智能·音视频·aws
杀生丸学AI17 小时前
【稀疏重建】StructSplat:基于非校准稀疏视图的可泛化3DGS
深度学习·3d·音视频·transformer·三维重建·空间智能
小柯南敲键盘17 小时前
跨境电商图片翻译工具推荐:批量AI翻译+视频字幕+智能抠图
人工智能·python·音视频
DogDaoDao20 小时前
【H266/VVC提案解读】ITU-T H.274 (V4) 规范深度解读 — 视频编码 SEI 消息的全面演进
音视频·实时音视频·视频编解码·流媒体·h266·vvc·视频编解码标准
程序员老陆21 小时前
FFmpeg RTSP 解复用器(rtsp demuxer)完全解析:从「能播」到「稳播」的参数圣经
ffmpeg·音视频·拉流·rtsp
hhzz1 天前
Tiger AI 平台「手势识别」功能全解析:从数字手势 0–9,到中国手语字母,再到本地视频批量识别——一条链路,三种输入,双模型可同开;双手比划,机器秒懂
人工智能·python·深度学习·aigc·音视频
martindelophy1 天前
DeepSeek Harness 插件开发实战:用自然语言检查、修改并渲染 Timeline Studio 视频工程
音视频
平原20181 天前
LTX-2.5 22B 本地部署:ModelScope 下载、8 步推理与图生视频命令
音视频
淡淡的香烟1 天前
Android视频直播播放器简单封装
android·物联网·音视频
AI创界者1 天前
MiniMax-H3 AI 视频整合包:音频驱动/文图生视频/全能参考,免环境解压即用
人工智能·aigc·音视频