/**
* 最简单的基于FFmpeg的视频解码器
* Simplest FFmpeg Decoder
*
*
* 本程序实现了视频文件解码为YUV数据。它使用了libavcodec和
* libavformat。是最简单的FFmpeg视频解码方面的教程。
* 通过学习本例子可以了解FFmpeg的解码流程。
* This software is a simplest decoder based on FFmpeg.
* It decodes video to YUV pixel data.
* It uses libavcodec and libavformat.
* Suitable for beginner of FFmpeg.
*
*/
#include <stdio.h>
#include "iostream"
using namespace std;
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif
int main(int argc, char* argv[])
{
AVFormatContext* pFormatCtx;//需要avformat_alloc_context();初始化
int i, videoindex;
AVCodecContext* pCodecCtx = NULL;
AVCodec* pCodec;
AVFrame* pFrame, * pFrameYUV;//需要av_frame_alloc();初始化
unsigned char* out_buffer;
AVPacket* packet;
int y_size;
int ret, got_picture;
struct SwsContext* img_convert_ctx; //图像转换数据结构
//sws_getContext() 负责初始化
//sws_scale()主体,转换视频长宽高
//sws_freeContext()释放
char filepath[] = "test.mp4";
FILE* fp_yuv = fopen("output2.yuv", "wb+");
avformat_network_init();//加载socket库以及网络加密协议相关的库,为后续使用网络相关提供支持
pFormatCtx = avformat_alloc_context();
if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
//创建视频流,使用复用器解码得到码流,初始化AVStream
printf("Couldn't find stream information.\n");
return -1;
}
videoindex = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)//nb_streams是输入视频的AVStream 个数
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
//切换到视频流
videoindex = i;
break;
}
if (videoindex == -1) {
printf("Didn't find a video stream.\n");
return -1;//如果没有找到视频流就退出
}
/****************************************************/
/****************************************************/
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
//将AVFormatContext指定的解码器让avcodec_find_decoder()去查找
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);//查找解码器id
if (pCodec == NULL) {
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return -1;
}
/****************************************************/
pFrame = av_frame_alloc();//注册AvFrame
pFrameYUV = av_frame_alloc();
//重点说明一个参数align:此参数是设定内存对齐的对齐数,也就是按多大的字节进行内存对齐。比如设置为1,表示按1字节对齐,
//那么得到的结果就是与实际的内存大小一样。再比如设置为4,表示按4字节对齐。也就是内存的起始地址必须是4的整倍数。
out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
//申请解码堆内存,计算一帧需要的空间
//有好几个参数, 第一第二个参数就是一个frame对象里的两个成员,dst_data[4]是一个指针数组
//对申请的内存进行格式化瓜分
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
packet = (AVPacket*)av_malloc(sizeof(AVPacket));//分配一个AVPacket包的内存
//Output Info-----------------------------
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx, 0, filepath, 0);// 打印关于输入或输出格式的详细信息,例如持续时间,比特率,流,容器,程序,元数据,边数据,编解码器和时基。
printf("-------------------------------------------------\n");
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
1280, 720, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
while (av_read_frame(pFormatCtx, packet) >= 0) {
//拆包
if (packet->stream_index == videoindex) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0) {
printf("Decode Error.\n");
return -1;
}
if (got_picture) {
printf(" pts is %d \n", pFrame->pts);
// 每帧格式类型转换
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
y_size = pCodecCtx->width * pCodecCtx->height;
int y_size2 = pFrameYUV->linesize[2];
// 根据YUV420格式,Y U V 比例 4 : 1 : 1
fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); //Y
fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //U
fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); //V
printf("Succeed to decode 1 frame!\n");
printf("y_size2 = %d,y_size = %d\n", y_size2, y_size);
}
}
av_free_packet(packet);
}
/*
//flush decoder
//FIX: Flush Frames remained in Codec
while (1) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0)
break;
if (!got_picture)
break;
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);//转换长宽高,分辨率
int y_size = pCodecCtx->width*pCodecCtx->height;
fwrite(pFrame->data[0], 1, y_size, fp_yuv); //Y
fwrite(pFrame->data[1], 1, y_size / 4, fp_yuv); //U
fwrite(pFrame->data[2], 1, y_size / 4, fp_yuv); //V
printf("Flush Decoder: Succeed to decode 1 frame!\n");
}*/
printf("%dX%d", pCodecCtx->width, pCodecCtx->height);
sws_freeContext(img_convert_ctx);
fclose(fp_yuv);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
简单的基于FFmpeg的视频解码器
猜你喜欢
转载自blog.csdn.net/qq_51282224/article/details/131040793
今日推荐
周排行