版本是ffmpeg4.3(高几个版本或者低几个版本都可以)
结果 :把H264转成yuv420p
使用的H264文件是D:/videos/264.dat(dat后缀名无所谓)
264.dat下载,不用积分
可以使用ffplay播放
ffplay D:/videos/264.dat
最终生成文件yuv420p_1920_1080_264.yuv
可以使用ffplay播放
ffplay -pix_fmt yuv420p -s 1920x1080 D:/output/yuv420p_1920_1080_264.yuv
方法一:主要函数:av_read_frame()
方法二:主要函数:av_parser_parse2()
方法一和方法二的区别:
方法一是通过查找流的信息找到解码器ID。
方法二不需要查找流的信息,直接读取二进制文件,自己设置解码器ID
方法一:
主要函数:av_read_frame()
源码
#define YUVFORMAT_YUV420P AV_PIX_FMT_YUV420P
void startTransform2YUV420p()
{
int nRet = 0;
const char *pInFileName = "C:/Users/zhou/Desktop/Mpeg2decoder/out.h264";
const char *pOutFileName = "C:/Users/zhou/Desktop/Mpeg2decoder/out.yuv";
AVDictionary *pDic = nullptr;
AVFormatContext *pInFmtCtx = nullptr;
nRet = avformat_open_input(&pInFmtCtx,pInFileName,nullptr,&pDic);
if( nRet < 0)
{
printf("Could not open input file.");
return;
}
avformat_find_stream_info(pInFmtCtx, nullptr);
printf("===========Input Information==========\n");
av_dump_format(pInFmtCtx, 0, pInFileName, 0);
printf("======================================\n");
//查找解码器 和下边的查找解码器都可用
// int vudioStreamIndex = av_find_best_stream(pInFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
// if(-1 == vudioStreamIndex)
// {
// printf("Error: can't find a video stream!\n");
// return;
// }
//查找解码器
int vudioStreamIndex = -1;
for(int i = 0; i < pInFmtCtx->nb_streams; ++i)
{
if(AVMEDIA_TYPE_VIDEO == pInFmtCtx->streams[i]->codecpar->codec_type)
{
vudioStreamIndex = i;
break;
}
}
AVStream * in_stream = pInFmtCtx->streams[vudioStreamIndex];
AVCodec *pInCodec = avcodec_find_decoder(in_stream->codecpar->codec_id);
if(nullptr == pInCodec)
{
printf("avcodec_find_decoder fail.");
return;
}
AVCodecContext* pInCodecCtx = avcodec_alloc_context3(pInCodec); //??????????????
nRet = avcodec_parameters_to_context(pInCodecCtx, in_stream->codecpar);
if(nRet < 0)
{
printf("avcodec_parameters_to_context fail.");
return;
}
//打开解码器
if(avcodec_open2(pInCodecCtx, pInCodec, nullptr) < 0)
{
printf("Error: Can't open codec!\n");
return ;
}
printf("width = %d\n", pInCodecCtx->width);
printf("height = %d\n", pInCodecCtx->height);
AVPacket *packet = av_packet_alloc();
av_init_packet(packet);
FILE *fp = fopen(pOutFileName, "wb+");
size_t y_size = 0;
int got_picture = 0;
int nCount = 0;
AVFrame *pFrame = av_frame_alloc();
while(av_read_frame(pInFmtCtx, packet) >= 0)
{
if( vudioStreamIndex == packet->stream_index)
{
//avcodec_send_packet送原始数据给编码器进行编码
//avcodec_receive_frame
if(avcodec_send_packet(pInCodecCtx, packet)<0 || (got_picture =avcodec_receive_frame(pInCodecCtx, pFrame))<0)
{
continue;
}
if(!got_picture)//
{
int picSize = pFrame->height *pFrame->width;
int newSize = picSize * 1.5;
//申请内存
unsigned char *buf = new unsigned char[newSize];
int a=0,i;
for (i=0; i<pFrame->height; i++)
{
memcpy(buf+a,pFrame->data[0] + i * pFrame->linesize[0], pFrame->width);
a+=pFrame->width;
}
for (i=0; i<pFrame->height/2; i++)
{
memcpy(buf+a,pFrame->data[1] + i * pFrame->linesize[1], pFrame->width/2);
a+=pFrame->width/2;
}
for (i=0; i<pFrame->height/2; i++)
{
memcpy(buf+a,pFrame->data[2] + i * pFrame->linesize[2], pFrame->width/2);
a+=pFrame->width/2;
}
fwrite(buf, 1, newSize, fp);
printf("Succeed to decode %d Frame!\n",nCount);
nCount++;
}
av_packet_unref(packet);
}
}
fflush(fp);
//flush decoder
//当av_read_frame 退出循环的时候,实际上解码器中可能还包含
//剩余的几帧数据。直接调用avcodec_decode_video2获得AVFrame ,
//而不再向解码器传递AVPacket
while(1)
{
if(avcodec_send_packet(pInCodecCtx, packet)<0 || (got_picture =avcodec_receive_frame(pInCodecCtx, pFrame))<0)
{
std::cout<<"h264toyuv420p end";
goto __end;
}
if(!got_picture)
{
break;
}
int picSize = pFrame->height *pFrame->width;
int newSize = picSize * 1.5;
//申请内存
unsigned char *buf = new unsigned char[newSize];
int a=0,i;
for (i=0; i<pFrame->height; i++)
{
memcpy(buf+a,pFrame->data[0] + i * pFrame->linesize[0], pFrame->width);
a+=pFrame->width;
}
for (i=0; i<pFrame->height/2; i++)
{
memcpy(buf+a,pFrame->data[1] + i * pFrame->linesize[1], pFrame->width/2);
a+=pFrame->width/2;
}
for (i=0; i<pFrame->height/2; i++)
{
memcpy(buf+a,pFrame->data[2] + i * pFrame->linesize[2], pFrame->width/2);
a+=pFrame->width/2;
}
fwrite(buf, 1, newSize, fp);
printf("flush Succeed to decode %d Frame!\n",nCount);
nCount++;
}
__end:
fflush(fp);
fclose(fp);
av_frame_free(&pFrame);
avcodec_close(pInCodecCtx);
avformat_close_input(&pInFmtCtx);
std::cout<<"h264toyuv420p end";
}
int main()
{
startTransform2YUV420p();
}
方法二:
主要函数为:
结构体AVCodecParser:用于解析输入的数据流并把它分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据。
av_parser_init(): 初始化AVCodecParserContext。
av_parser_parse2():解析获得一个Packet。相当于av_read_frame。
static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
FILE *fo)
{
char buf[1024];
int ret;
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
size_t y_size = 1920*1080;
fwrite(frame->data[0], 1, y_size, fo);
fwrite(frame->data[1], 1, y_size/4, fo);
fwrite(frame->data[2], 1, y_size/4, fo);
}
}
static int test(const char* filename,const char* outfilename)
{
const AVCodec *codec;
AVCodecParserContext *parser;
AVCodecContext *c= NULL;
FILE *f;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
int ret;
AVPacket *pkt;
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
/* find the MPEG-1 video decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
parser = av_parser_init(codec->id);
if (!parser) {
fprintf(stderr, "parser not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
FILE *fo;
fo = fopen(outfilename,"wb+");
while (!feof(f)) {
/* read raw data from the input file */
data_size = fread(inbuf, 1, INBUF_SIZE, f);
if (!data_size)
break;
/* use the parser to split the data into frames */
data = inbuf;
while (data_size > 0) {
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;
if (pkt->size)
decode(c, frame, pkt, fo);
}
}
/* flush the decoder */
decode(c, frame, NULL, fo);
fclose(f);
fclose(fo);
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
void main()
{
test("D:/videos/264.dat","D:/output/decode_video_h264.yuv");
std::cout<<"decode end";
}
如果出现条纹状或者阶梯状,请参考
https://blog.csdn.net/asdasfdgdhh/article/details/112831581
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓