解码AVC(h264)裸流为yuv420P写入文件

简介: 本文介绍了如何使用FFmpeg库解码AVC(H.264)裸流为YUV420P格式并写入文件的过程。

使用AVC裸流解析器解析AVC裸流文件,然后解码为yuv420P格式写入文件

// 解码h264
#include "myLog.h"
#include <iostream>

extern "C"
{
#include "libavcodec\avcodec.h"
}

#define VIDEO_INBUF_SIZE 20480        // 最初读取数据大小
#define VIDEO_REFILL_THRESH 4096

static char* av_get_err(int errnum)
{
    char err_buf[128] = { 0 };
    av_strerror(errnum, err_buf, 128);
    return err_buf;
}

static void print_video_format(const AVFrame *frame)
{
    printf("width: %u\n", frame->width);
    printf("height: %u\n", frame->height);
    printf("format: %u\n", frame->format);
}

static void decode_video(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame, FILE* out_fp)
{
    int nRet = avcodec_send_packet(dec_ctx, pkt);
    if (nRet == AVERROR(EAGAIN))
    {
        LOG_WARNING("need more pkt\n");
        return;
    }
    else if (nRet < 0)
    {
        LOG_WARNING("Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
            av_get_err(nRet), pkt->size);
        return;
    }

    // 一个pkt可能会有多个frame
    while (nRet >= 0)
    {
        nRet = avcodec_receive_frame(dec_ctx, frame);
        if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF)
        {
            return;
        }
        else if (nRet < 0)
        {
            LOG_WARNING("Error during decoding\n");
            return;
        }

        // 打印一次信息
        static bool is_print = false;
        if (!is_print)
        {
            is_print = !is_print;
            print_video_format(frame);
        }


        // 一般H264默认为 AV_PIX_FMT_YUV420P
        // linesize[i]代表每行的字节数量,所以每行的偏移是linesize[i]
        // 写入YUV420P格式数据(每次写入一行数据)
        for (int i = 0; i < frame->height; i++)    // Y
        {
            fwrite(frame->data[0] + i * frame->linesize[0], 1, frame->width, out_fp);
        }
        for (int i = 0; i < frame->height / 2; i++)    // U
        {
            fwrite(frame->data[1] + i * frame->linesize[1], 1, frame->width / 2, out_fp);
        }
        for (int i = 0; i < frame->height / 2; i++)    // V
        {
            fwrite(frame->data[2] + i * frame->linesize[2], 1, frame->width / 2, out_fp);
        }
    }
}

// ffplay  -f rawvideo -pixel_format yuv420p -video_size 852x480 -i HTA_10s_420P.yuv
int main_video_decodec()
{
    const char* in_file = "./HTA_10s.h264";
    const char* out_file = "./HTA_10s_420P.yuv";

    // 1. 直接查找h264解码器
    AVCodec* video_dec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (video_dec == nullptr)
    {
        LOG_WARNING("avcodec_find_decoder error\n");
        return -1;
    }

    // 2. 创建解码器上下文
    AVCodecContext* dec_ctx = avcodec_alloc_context3(video_dec);
    if (dec_ctx == nullptr)
    {
        LOG_WARNING("avcodec_alloc_context3 error\n");
        return -2;
    }

    // 3. 初始化(h264)裸流解析器
    AVCodecParserContext* parser_ctx = av_parser_init(dec_ctx->codec_id);
    if (parser_ctx == nullptr)
    {
        LOG_WARNING("av_parser_init error\n");
        return -3;
    }

    // 4. 将解码器与上下文关联
    int nRet = avcodec_open2(dec_ctx, video_dec, NULL);
    if (nRet < 0)
    {
        LOG_WARNING("avcodec_open2 error\n");
        return -4;
    }

    // 5. 打开输入输出文件
    FILE* in_fp = fopen(in_file, "rb");
    if (nullptr == in_fp)
    {
        LOG_WARNING("open in_file error\n");
        return -5;
    }

    FILE* out_fp = fopen(out_file, "wb");
    if (nullptr == out_fp)
    {
        LOG_WARNING("open out_file error\n");
        return -6;
    }

    // 读取文件开始解码
    uint8_t inbuf[VIDEO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data = inbuf;
    size_t   data_size = 0;
    data_size = fread(inbuf, 1, VIDEO_INBUF_SIZE, in_fp);

    AVPacket* pkt = av_packet_alloc();
    AVFrame* frame = av_frame_alloc();
    while (data_size > 0)
    {
        if (frame == nullptr)
        {
            LOG_WARNING("frame is nullptr\n");
            return -7;
        }

        nRet = av_parser_parse2(parser_ctx, dec_ctx, &pkt->data, &pkt->size,
            data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (nRet < 0)
        {

            LOG_WARNING("av_parser_parse2 error\n");
            return -8;
        }
        data += nRet;
        data_size -= nRet;

        if (pkt->size)
        {
            // 解码
            decode_video(dec_ctx, pkt, frame, out_fp);
        }

        if (data_size < VIDEO_REFILL_THRESH)
        {
            memmove(inbuf, data, data_size);
            data = inbuf;
            int len = fread(inbuf + data_size, 1, VIDEO_INBUF_SIZE - data_size, in_fp);
            if (len > 0)
            {
                data_size += len;
            }
        }
    }

    // 冲刷解码器
    pkt->data = NULL;
    pkt->size = 0;
    decode_video(dec_ctx, pkt, frame, out_fp);

    // 释放资源
    fclose(in_fp);
    fclose(out_fp);

    avcodec_free_context(&dec_ctx);
    av_parser_close(parser_ctx);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    getchar();
    return 0;
}
相关文章
|
11月前
SDL事件处理以及线程使用(2)
SDL库中事件处理和多线程编程的基本概念和示例代码,包括如何使用SDL事件循环来处理键盘和鼠标事件,以及如何创建和管理线程、互斥锁和条件变量。
149 1
SDL事件处理以及线程使用(2)
|
11月前
|
C++
SDL基础使用02(加载bmp图片、纹理和渲染)
这篇文章介绍了如何使用SDL库在C++中加载和显示BMP图片,以及如何使用纹理和渲染器进行更高级的图形处理。
158 2
|
11月前
|
API
(4)Qt中的位置和尺寸
本文介绍了Qt中用于处理位置和尺寸的四个核心类:QPoint、QLine、QSize和QRect,包括它们的常用API和一些基本使用示例。
180 0
(4)Qt中的位置和尺寸
|
机器学习/深度学习 自然语言处理 测试技术
社区供稿 | RWKV-6-World 14B正式开源发布,迄今最强的稠密纯RNN大语言模型
2024年7月19日,RWKV 开源基金会宣布正式向全球开源 RWKV-6-World 14B 模型。
|
SQL 监控 关系型数据库
mysql统计数据库大小
通过这些方法,数据库管理员可以有效地监控和规划MySQL数据库的存储需求,确保数据库的稳定运行。
191 2
|
网络协议 视频直播 C语言
C语言 网络编程(三)UDP 协议
UDP(用户数据报协议)是一种无需建立连接的通信协议,适用于高效率的数据传输,但不保证数据的可靠性。其特点是无连接、尽力交付且面向报文,具备较高的实时性。UDP广泛应用于视频会议、实时多媒体通信、直播及DNS查询等场景,并被许多即时通讯软件和服务(如MSN/QQ/Skype、流媒体、VoIP等)采用进行实时数据传输。UDP报文由首部和数据部分组成,首部包含源端口、目的端口、长度和校验和字段。相比TCP,UDP具有更高的传输效率和更低的资源消耗。
|
JavaScript Java 测试技术
基于springboot+vue.js的电商平台附带文章和源代码设计说明文档ppt
基于springboot+vue.js的电商平台附带文章和源代码设计说明文档ppt
129 1
基于springboot+vue.js的电商平台附带文章和源代码设计说明文档ppt
|
JSON 数据格式
关于nlohmann::json的简单使用
关于nlohmann::json的简单使用
446 0
关于nlohmann::json的简单使用
|
流计算
电力系统潮流【牛顿-拉夫逊法】(4节点、5节点、6节点、9节点)(Matlab代码实现)
电力系统潮流【牛顿-拉夫逊法】(4节点、5节点、6节点、9节点)(Matlab代码实现)
781 0
|
NoSQL Java 数据库
Seata常见问题之xa模式下插入一条数据再更新这条数据会报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
315 2