首页 > 其他分享 >视频解码基础讲解

视频解码基础讲解

时间:2024-07-28 12:39:34浏览次数:12  
标签:视频 outputFile codecContext 解码 packet 解码器 frame 讲解 avcodec

视频解码流程

视频解码的具体步骤如下:

查找指定的解码器 avcodec_find_decoder
根据指定的解码器ID初始化相应裸流的解析器 av_parser_init
分配解码器上下文 avcodec_alloc_context3
打开解码器和关联解码器上下文 avcodec_open2
读取原始裸流 fread
解析出一个完整的数据包 av_parser_parse2
发送给解码器 avcodec_send_packet
接收解码后的帧 avcodec_receive_frame
循环读取帧并将YUV数据写入文件 fwrite
文件读取完毕后冲刷解码器,以读取出缓存在解码器的数据帧 flush decoder
结束

关键函数和示例代码

查找指定的解码器

AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
    std::cerr << "Codec not found" << std::endl;
    return -1;
}

初始化解析器

AVCodecParserContext* parser = av_parser_init(codec->id);
if (!parser) {
    std::cerr << "Parser not found" << std::endl;
    return -1;
}

分配解码器上下文

AVCodecContext* codecContext = avcodec_alloc_context3(codec);
if (!codecContext) {
    std::cerr << "Could not allocate video codec context" << std::endl;
    return -1;
}

打开解码器

if (avcodec_open2(codecContext, codec, NULL) < 0) {
    std::cerr << "Could not open codec" << std::endl;
    return -1;
}

读取原始裸流

FILE* inputFile = fopen(inputFileName, "rb");
if (!inputFile) {
    std::cerr << "Could not open input file" << std::endl;
    return -1;
}

解析数据包并发送到解码器

uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();

while (!feof(inputFile)) {
    size_t dataSize = fread(inbuf, 1, INBUF_SIZE, inputFile);
    if (dataSize <= 0)
        break;

    uint8_t* data = inbuf;
    while (dataSize > 0) {
        int ret = av_parser_parse2(parser, codecContext, &packet->data, &packet->size, data, dataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
            std::cerr << "Error while parsing" << std::endl;
            return -1;
        }
        data += ret;
        dataSize -= ret;

        if (packet->size)
            decode(codecContext, packet, frame, outputFile);
    }
}

解码函数

void decode(AVCodecContext* codecContext, AVPacket* packet, AVFrame* frame, FILE* outputFile) {
    int ret = avcodec_send_packet(codecContext, packet);
    if (ret < 0) {
        std::cerr << "Error sending a packet for decoding" << std::endl;
        return;
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(codecContext, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            std::cerr << "Error during decoding" << std::endl;
            return;
        }

        // 将YUV数据写入文件
        for (int i = 0; i < frame->height; i++) {
            fwrite(frame->data[0] + i * frame->linesize[0], 1, frame->width, outputFile);
        }
        for (int i = 0; i < frame->height / 2; i++) {
            fwrite(frame->data[1] + i * frame->linesize[1], 1, frame->width / 2, outputFile);
        }
        for (int i = 0; i < frame->height / 2; i++) {
            fwrite(frame->data[2] + i * frame->linesize[2], 1, frame->width / 2, outputFile);
        }
    }
}

冲刷解码器

avcodec_send_packet(codecContext, NULL);
while (avcodec_receive_frame(codecContext, frame) == 0) {
    for (int i = 0; i < frame->height; i++) {
        fwrite(frame->data[0] + i * frame->linesize[0], 1, frame->width, outputFile);
    }
    for (int i = 0; i < frame->height / 2; i++) {
        fwrite(frame->data[1] + i * frame->linesize[1], 1, frame->width / 2, outputFile);
    }
    for (int i = 0; i < frame->height / 2; i++) {
        fwrite(frame->data[2] + i * frame->linesize[2], 1, frame->width / 2, outputFile);
    }
}

关闭文件和释放资源

fclose(inputFile);
fclose(outputFile);
av_parser_close(parser);
avcodec_free_context(&codecContext);
av_frame_free(&frame);
av_packet_free(&packet);

总结

通过以上步骤和代码示例,你可以了解如何使用FFmpeg进行视频解码,从而将H.264等格式的视频文件解码为YUV数据。整个流程包括了查找解码器、初始化解析器、分配和打开解码器上下文、读取和解析数据包、发送数据包到解码器、接收解码后的帧并写入文件,以及最后的资源释放。

标签:视频,outputFile,codecContext,解码,packet,解码器,frame,讲解,avcodec
From: https://www.cnblogs.com/mxh010211/p/18328099

相关文章

  • FFmpeg开发笔记(四十二)使用ZLMediaKit开启SRT视频直播服务
    ​《FFmpeg开发实战:从零基础到短视频上线》一书在第10章介绍了轻量级流媒体服务器MediaMTX,通过该工具可以测试RTSP/RTMP等流媒体协议的推拉流。不过MediaMTX的功能实在是太简单了,无法应用于真实直播的生产环境,真正能用于生产环境的流媒体服务器还要看SRS或者ZLMediaKit。ZLMedia......
  • 音频解码基础讲解
    音频解码流程音频解码的总体流程如下:输入音频格式(例如AAC)通过音频解码器进行解码得到PCM数据FFmpeg解码流程音频解码的具体步骤如下:查找指定的解码器avcodec_find_decoder根据指定的解码器ID初始化相应裸流的解析器av_parser_init分配解码器上下文avcodec_alloc_conte......
  • 【C++进阶学习】第九弹——哈希的原理与实现——开放寻址法的讲解
    前言:在前面,我们已经学习了很多存储机构,包括线性存储、树性存储等,并学习了多种拓展结构,效率也越来越高,但是是否有一种存储结构可以在大部分问题中都一次找到目标值呢?哈希可能能实现目录一、哈希的概念二、哈希冲突三、哈希冲突解决3.1开放寻址法节点结构插入操作查......
  • Diffutoon下载介绍:真人视频转动漫工具,轻松获得上千点赞
    最近在刷短视频的时候,偶尔能看到一些真人转动漫风的作品,看起来给人一种新鲜感,流量都还不错,简简单单跳个舞,就能获得上千个点赞~ 那么,这种视频是怎么制作的?本期给大家介绍一款AI转绘工具Diffutoon,可以将逼真的视频转换成动画风格,不仅能够处理高分辨率和快速运动的视频,还能确保......
  • 【Spring Cloud应用框架的讲解】
    ......
  • 基于SpringBoot+Vue的二手手机交易平台的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • 基于SpringBoot+Vue的短视频广告发布系统的详细设计和实现(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示项目运行截图技术框架后端采用SpringBoot框架前端框架Vue可行性分析系统测试系统测试的目的系统功能测试数据库表设计代码参考数据库脚本为什么选择我?获取源码前言......
  • C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
    写在最前,一篇文章学会C语言指针顶级重要,学习C语言最重要的一部分-------指针第八章指针超详细讲解_指针变量_二维数组指针_指向字符串指针文章目录写在最前,一篇文章学会C语言指针第八章指针超详细讲解_指针变量_二维数组指针_指向字符串指针1.指针变量1.1指针变......
  • JS异步中async、await讲解
    目录1async、await1.1微任务队列&宏任务队列1.2问题引入1.3async函数返回值1.3.1示例1.3.2面试示例1.4await右值类型区别1.4.1非thenable1.4.2thenable类型1.4.3Promise类型1.4.3.1没有两个then等待1.4.3.2循环交叉输出1.5await+sync示例说明1.5.1返回和无返回1......
  • 【项目实战】解码软件工程:一文读懂DO/PO/BO/AO/DTO/DAO/POJO/VO的奥秘
    文章目录一文读懂DO/PO/BO/AO/DTO/DAO/POJO/VO的奥秘不同领域作用POJO(PlainOldJavaObject)VO(ValueObject)VO(ViewObject)的特点:实体类(Entity)数据传输对象(DTO)领域对象(DomainObject)持久化对象(PersistentObject)业务对象(BusinessObject)应用对象(ApplicationObject)......