使用 ffmpeg将MP4转成yuv并转成jpg图像

所需头文件和 main 文件

#include <iostream>
extern "C"
{
    
    
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
};
#include <afxwin.h>
#pragma warning(disable:4996)
using namespace std;

int main()
{
    
    
	Parser();
	return 0;
}

函数定义

typedef struct
{
    
    
	int         startpos;
	int         endpos;
	int         len;
	int         code;
	int         startlen;
}FrameAna;

unsigned char   m_outbuf[1024 * 1024];

typedef struct
{
    
    
	uint8_t     m_Head[4];
	int         m_len;
}FrameHead_t;

//yuv转为jpg
int tojpg(AVPacket *packet, AVFrame* pFrame, unsigned int stream_index, int width, int height)
{
    
    
	char out_file[128] = "";
	sprintf_s(out_file, sizeof(out_file), "D:\\jpg\\%03d.jpg", stream_index);//jpg图像输出路径
	// 分配AVFormatContext对象  
	AVFormatContext* pFormatCtx = avformat_alloc_context();

	// 设置输出文件格式  
	pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);

	// 创建并初始化一个和该url相关的AVIOContext  
	if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0)
	{
    
    
		printf("Couldn't open output file");
		return -1;
	}

	// 构建一个新stream  
	AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);
	if (pAVStream == NULL)
	{
    
    
		printf("Frame2JPG::avformat_new_stream error.");
		return -1;
	}

	// 设置该stream的信息  
	AVCodecContext* pCodecCtx = pAVStream->codec;

	pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
	pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
	pCodecCtx->width = width;
	pCodecCtx->height = height;
	pCodecCtx->time_base.num = 1;
	pCodecCtx->time_base.den = 25;

	// 查找解码器  
	AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec)
	{
    
    
		printf("avcodec_find_encoder() error.");
		return -1;
	}
	// 设置pCodecCtx的解码器为pCodec  
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
	{
    
    
		printf("Could not open codec.");
		return -1;
	}

	//Write Header  
	int ret = avformat_write_header(pFormatCtx, NULL);
	if (ret < 0)
	{
    
    
		printf("avformat_write_header() error.\n");
		return -1;
	}

	int y_size = pCodecCtx->width * pCodecCtx->height;

	//Encode  
	// 给AVPacket分配足够大的空间  
	AVPacket pkt;
	av_new_packet(&pkt, y_size * 3);

	int got_picture = 0;
	ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
	if (ret < 0)
	{
    
    
		printf("avcodec_encode_video2() error.\n");
		return -1;
	}

	if (got_picture == 1)
	{
    
    
		ret = av_write_frame(pFormatCtx, &pkt);
	}

	av_free_packet(&pkt);

	//Write Trailer  
	av_write_trailer(pFormatCtx);

	if (pAVStream)
	{
    
    
		avcodec_close(pAVStream->codec);
	}

	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);

	return 0;
}


将mp4文件转换为h264文件

//mp4中获取yuv
void Parser()
{
    
    
	AVFormatContext		*pFormatCtx;
	int					videoindex;
	AVCodecContext		*pCodecCtx;
	AVCodec				*pCodec;
	AVFrame				*pFrame;
	uint8_t				*out_buffer;
	AVPacket			*packet;
	int                 ret, got_picture;
	struct SwsContext   *img_convert_ctx;

	char filepath[] = "d:\\test.mp4";//要解析的mp4文件
	FILE				*fp_h264 = NULL;
	FrameAna			m_FrameInfo[1024];
	int					m_FrameIndex = 0;

	fopen_s(&fp_h264, "d:\\output.h264", "wb+");//MP4输出成.h264文件
	/* 初始化一个AVFormatContext ,并打开文件 */
	pFormatCtx = avformat_alloc_context();
	avformat_open_input(&pFormatCtx, filepath, NULL, NULL);
	/*  获取视频文件信息 */
	avformat_find_stream_info(pFormatCtx, NULL);
	/* 查找视频的编号 */
	videoindex = -1;
	for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)
	{
    
    
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
    
    
			videoindex = (int)i;
			break;
		}
		if (videoindex == -1)
		{
    
    
			AfxMessageBox(L"can't find video,error!");
			return;
		}
	}

	/* 查找解码器 */
	pCodecCtx = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	/* 打开解码器 */
	avcodec_open2(pCodecCtx, pCodec, NULL);
	pFrame = av_frame_alloc();
	out_buffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
	packet = (AVPacket*)av_malloc(sizeof(AVPacket));
	img_convert_ctx= sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
	/* 获取流解析对象 */
	unsigned char   *h264_buf;
	int             h264_len;
	AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");

	int     iNeedIFrame = 1;
	int		iDecoderCnt = 0;
	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
    
        /* 读取一帧压缩数据 */
		if (packet->stream_index == videoindex)
		{
    
    
			/* 数据解码,可获取到YUV解码后数据 */
			ret = avcodec_send_packet(pCodecCtx, packet);
			if (ret < 0)
			{
    
    
				AfxMessageBox(L"Decoder one frame , Error !");
				return;
			}

			got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
			if (0 == got_picture)
			{
    
    
				iDecoderCnt++;
				if (nullptr != fp_h264)
				{
    
    
					fwrite(pFrame->data[0], 1, pFrame->width * pFrame->height, fp_h264);
					fwrite(pFrame->data[1], 1, pFrame->width * pFrame->height / 4, fp_h264);
					fwrite(pFrame->data[2], 1, pFrame->width * pFrame->height / 4, fp_h264);
					//每一帧都调用tojpg函数转换为jpg图像
					tojpg(packet,pFrame,(unsigned int)iDecoderCnt,pFrame->width,pFrame->height);
				}
			}
		}
		av_packet_unref(packet);
	}
	/* flush decoder */
	/*当av_read_frame()循环退出的时候,实际上解码器中可能还包含剩余的几帧数据。因此需要通过“flush_decoder”将这几帧数据输出。“flush_decoder”功能简而言之即直接调用avcodec_decode_video2()获得AVFrame,而不再向解码器传递AVPacket。*/
	while (1)
	{
    
    
		got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
		if (0 != got_picture)
			break;

		iDecoderCnt++;
	}

	sws_freeContext(img_convert_ctx);

	/* 释放流解析对象 */
	av_bitstream_filter_close(h264bsfc);

	/* 关闭文件以及释放内存 */
	fclose(fp_h264);
	av_frame_free(&pFrame);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);

	CString		m_str;
	m_str.Format(L"Get FrameCnt = %d Success !!\n", iDecoderCnt);
	AfxMessageBox(m_str);
	return;
}

猜你喜欢

转载自blog.csdn.net/Fengfgg/article/details/112827988