利用av

发布时间:2024-11-10 16:23

1、解码

原始的图像数据是非常庞大的,本示例的视频20s,分辨率1280*720,原始的yuv420p数据大概在1.4G,如果是2个小时的电影、分辨率为1920*1080,原始的yuv数据可想而知会有多大,所以无论是音频还是视频数据都会经过编码,以降低多媒体mp4、mov等视频的容量。

利用av_parser_parser2解码,输入必须是只包含视频编码数据“裸流”(例如H.264、HEVC码流文件),而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)。

2、解码流程

懒的画图了,直接手写了一下

同样在windows上和linux上都实现了解码的代码,如果要获取整个工程,直接访问我的github,希望大家给个星星,感谢。

visual studio环境:https://github.com/liupengh3c/goffmpeg

linux环境:https://github.com/liupengh3c/myffmpeg

linux的代码,直接在build目录下,执行:sh build.sh即可编译

3、代码

linux环境下代码:

/*

本函数以解码h264文件为例,解码为yuv420p

没有使用av_read_frame函数,输入必须是只包含视频编码数据“裸流”(例如h264、HEVC码流文件)

而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)。

*/

#define __STDC_CONSTANT_MACROS

#include "iostream"

extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

#define INBUF_SIZE 4096

int decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, FILE *f)

{

int ret = 0;

ret = avcodec_send_packet(dec_ctx, pkt);

if (ret < 0)

{

std::cout << "error sending a packet for decoding." << std::endl;

return -1;

}

while (ret >= 0)

{

ret = avcodec_receive_frame(dec_ctx, frame);

if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)

{

return 0;

}

else if (ret < 0)

{

return -2;

}

// 写Y分量

for (size_t i = 0; i < frame->height; i++)

{

fwrite(frame->data[0] + frame->linesize[0] * i, 1, frame->width, f);

}

// 写U分量

for (size_t i = 0; i < frame->height / 2; i++)

{

fwrite(frame->data[1] + frame->linesize[1] * i, 1, frame->width / 2, f);

}

// 写V分量

for (size_t i = 0; i < frame->height / 2; i++)

{

fwrite(frame->data[2] + frame->linesize[2] * i, 1, frame->width / 2, f);

}

}

return 0;

}

int decode_video(std::string input_filename, std::string output_filename)

{

int ret = 0;

AVCodec* codec = NULL;

AVCodecContext* avcodec_ctx = NULL;

AVPacket *pkt = NULL;

AVFrame* frame = NULL;

// 解析器上下文

AVCodecParserContext* parser = NULL;

FILE* f_in = NULL;

FILE* f_out = NULL;

uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];

uint8_t* data;

size_t data_size;

// alloc an avpacket && frame

pkt = av_packet_alloc();

frame = av_frame_alloc();

f_out = fopen(output_filename.data(), "wb+");

// 1、找解码器

codec = avcodec_find_decoder(AV_CODEC_ID_H264);

if (!codec)

{

std::cout << "codec not found." << std::endl;

return -1;

}

// 2、初始化parser

parser = av_parser_init(codec->id);

if (!parser)

{

std::cout << "parser not found." << std::endl;

return -2;

}

// 3、分配解码上下文 alloc codec context

avcodec_ctx = avcodec_alloc_context3(codec);

if (!avcodec_ctx)

{

std::cout << "could not allocate avcodec_ctx." << std::endl;

return -3;

}

// 4、打开解码器

ret = avcodec_open2(avcodec_ctx, codec, NULL);

if (ret < 0)

{

std::cout << "could not open codec,ret=" << ret << std::endl;

return -4;

}

// 5、打开h264文件

f_in = fopen(input_filename.data(), "rb");

// 6、开始解码

while (!feof(f_in))

{

data_size = fread(inbuf, 1, INBUF_SIZE, f_in);

if (data_size <= 0)

{

break;

}

data = inbuf;

while (data_size > 0)

{

// 输入必须是只包含视频编码数据“裸流”(例如H.264、HEVC码流文件),而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)。

ret = av_parser_parse2(parser, avcodec_ctx, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);

std::cout << "ret=" << ret << std::endl;

data += ret;

data_size -= ret;

std::cout << "pkt_size=" << pkt->size << std::endl;

if (pkt->size)

{

decode(avcodec_ctx, frame, pkt, f_out);

}

}

}

/* flush the decoder */

decode(avcodec_ctx, frame, pkt, f_out);

fclose(f_in);

fclose(f_out);

av_parser_close(parser);

avcodec_free_context(&avcodec_ctx);

av_frame_free(&frame);

av_packet_free(&pkt);

return 0;

}

}

visual studio环境代码:

#pragma warning(disable : 4996);

#include "DecodeVideo.h"

extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

}

#define INBUF_SIZE 4096

int DecodeVideo::decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, FILE *f)

{

int ret = 0;

ret = avcodec_send_packet(dec_ctx, pkt);

if (ret < 0)

{

std::cout << "error sending a packet for decoding." << std::endl;

return -1;

}

while (ret >= 0)

{

ret = avcodec_receive_frame(dec_ctx, frame);

if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)

{

return 0;

}

else if (ret < 0)

{

return -2;

}

for (size_t i = 0; i < frame->height; i++)

{

fwrite(frame->data[0] + frame->linesize[0] * i, 1, frame->width, f);

}

for (size_t i = 0; i < frame->height / 2; i++)

{

fwrite(frame->data[1] + frame->linesize[1] * i, 1, frame->width / 2, f);

}

for (size_t i = 0; i < frame->height / 2; i++)

{

fwrite(frame->data[2] + frame->linesize[2] * i, 1, frame->width / 2, f);

}

}

return 0;

}

int DecodeVideo::decode_video(std::string input_filename, std::string output_filename)

{

int ret = 0;

AVCodec* codec = NULL;

AVCodecContext* avcodec_ctx = NULL;

AVPacket *pkt = NULL;

AVFrame* frame = NULL;

AVCodecParserContext* parser = NULL;

FILE* f_in = NULL;

FILE* f_out = NULL;

uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];

uint8_t* data;

size_t data_size;

pkt = av_packet_alloc();

frame = av_frame_alloc();

f_out = fopen(output_filename.data(), "wb+");

codec = avcodec_find_decoder(AV_CODEC_ID_H264);

if (!codec)

{

std::cout << "codec not found." << std::endl;

return -1;

}

parser = av_parser_init(codec->id);

if (!parser)

{

std::cout << "parser not found." << std::endl;

return -2;

}

avcodec_ctx = avcodec_alloc_context3(codec);

if (!avcodec_ctx)

{

std::cout << "could not allocate avcodec_ctx." << std::endl;

return -3;

}

ret = avcodec_open2(avcodec_ctx, codec, NULL);

if (ret < 0)

{

std::cout << "could not open codec,ret=" << ret << std::endl;

return -4;

}

f_in = fopen(input_filename.data(), "rb");

while (!feof(f_in))

{

data_size = fread(inbuf, 1, INBUF_SIZE, f_in);

if (data_size <= 0)

{

break;

}

data = inbuf;

while (data_size > 0)

{

ret = av_parser_parse2(parser, avcodec_ctx, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);

std::cout << "ret=" << ret << std::endl;

data += ret;

data_size -= ret;

std::cout << "pkt_size=" << pkt->size << std::endl;

if (pkt->size)

{

decode(avcodec_ctx, frame, pkt, f_out);

}

}

}

decode(avcodec_ctx, frame, pkt, f_out);

fclose(f_in);

fclose(f_out);

av_parser_close(parser);

avcodec_free_context(&avcodec_ctx);

av_frame_free(&frame);

av_packet_free(&pkt);

return 0;

}

4、执行

以linux环境为例说明吧,windows下的很简单,直接运行即可,

在build目录,运行sh build.sh进行编译,运行可执行文件,会有如下打印

h264的文件可以通过第2步解码代码中toy3.mp4来获的h264。

输入3来解码文件获得yuv420p的数据。

之后直接回车即可。

5、播放

该视频文件分辨率为1280*720,使用ffmplay可以进行播放,播放命令为(我的windows环境):

./study/ffmpeg-20200617-0b3bd00-win64-static/bin/ffplay.exe -f rawvideo -video_size 1280*720 study/goffmpeg/goffmpeg/toy3.yuv

觉着这篇文章对自己有益的土豪朋友可以扫描屏幕下方二维码金额随意,感谢大家支持,增加写作动力。

网址:利用av https://www.yuejiaxmz.com/news/view/25402

下一篇:HSO3

相关内容

维修常用到的一些小妙招总结
史上最全家电使用小窍门!(速度收藏)
家电大讲堂:如何使用家用电器更省电?
有哪些实用的家电常识
家庭生活个人保健用品介绍.PDF
【生活锦囊】各种常用家电省电窍门大全
节约的小窍门
生活节约小窍门
20120727家政女皇生活小妙招:家庭省电小窍门
如何实现家居空间的高效利用?这种利用如何提升生活便利性?

随便看看