本文主要是介绍解码AVC(h264)裸流为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 4096static 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可能会有多个framewhile (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;
}
这篇关于解码AVC(h264)裸流为yuv420P写入文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!