rgb pcm 转 mp4

视频编码
1.读取RGB文件转换为yuv
2.压缩为h264
3.封装为MP4

YUV
1.“Y"表示明亮度,也就是灰度值
2.而"U” 和 “V” 则表示色度

音视频类的封装
1.对外使用接口类,对象由内部创建
2.隐藏ffmpeg,调用者不需要引用ffmpeg头文件

通过ffmpeg工具抽取数据
1.ffmpeg -i test.mp4 -f s16le test.pcm
2.ffmpeg -i test.mp4 -pix_fmt bgra test.rgb

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)
project(ffmpeg_pro VERSION 0.1.0 LANGUAGES CXX C)
##配置 预编译库 如ffmpeg库 路径
 
# find_library(AVCODEC_LIBRARY avcodec)
# find_library(AVFORMAT_LIBRARY avformat)
# find_library(AVUTIL_LIBRARY avutil)
# find_library(AVDEVICE_LIBRARY avdevice)
##加入预编译库的头文件 如ffmpeg库 的头文件
#include_directories(include)

include_directories(./)
add_executable(ffmpeg_pro rgb_pcm_to_mp4.cpp XVideoWriter.cpp)
message(STATUS ${AVCODEC_LIBRARY})

target_link_libraries(
	ffmpeg_pro  
	pthread 
	swresample 
	m 
	swscale 
	avformat	
	avcodec 
	avutil 
	avfilter 
	avdevice 
	postproc 
	z 
	lzma  
	rt)

XVideoWriter.h

#pragma once
#include <string>

class AVPacket;
enum XSAMPLEFMT
{
	X_S16 = 1,
	X_FLATP = 8
};

class XVideoWriter
{
public:
	//视频输入参数
	int inWidth = 848;
	int inHeight = 480;
	int inPixFmt = 28;//30 //AV_PIX_FMT_BGRA

	//音频的输入参数
	int inSampleRate = 44100;
	int inChannels = 2;
	XSAMPLEFMT inSampleFmt = X_S16;

	//视频输出参数
	int vBitrate = 4000000;
	int outWidth = 848;
	int outHeight = 480;
	int outFPS = 25;

	//音频输出参数
	int aBitrate = 64000;
	int outChannels = 2;
	int outSampleRate = 44100;
	XSAMPLEFMT outSampleFmt = X_FLATP;

	int nb_sample = 1024; //输入输出的每帧数据每通道样本数量

	virtual bool Init(const char* file) = 0;
	virtual void Close() = 0;
	virtual bool AddVideoStream() = 0;
	virtual bool AddAudioStream() = 0;
	//把转yuv和编码放在一起做
	virtual AVPacket *EncodeVideo(const unsigned char *rgb) = 0;
	virtual AVPacket *EncodeAudio(const unsigned char *pcm) = 0;
	virtual bool WriteHead() = 0;
	//尾部文件没有写会视频不能拖动,时长不对

	//会释放pkt的空间
	virtual bool WriteFrame(AVPacket *pkt) = 0;

	virtual bool WriteEnd() = 0;

	virtual bool IsVideoBefore() = 0;


	static XVideoWriter *Get(unsigned short index = 0);
	~XVideoWriter();
	std::string filename;
protected:
	XVideoWriter();	
};

XVideoWriter.cpp

#include "XVideoWriter.h"

extern "C"
{
	#include <libavformat/avformat.h>
	#include <libswscale/swscale.h>
	#include <libavutil/opt.h>
	#include <libswresample/swresample.h>
}

#include <iostream>
using namespace std;

class CXVideoWriter : public XVideoWriter
{
public:
	AVFormatContext *ic = NULL; //封装mp4输出上下文
	AVCodecContext *vc = NULL; //视频编码器上下文
	AVCodecContext *ac = NULL; //音频编码器山下文
	AVStream *vs =  NULL; //视频流
	AVStream *as = NULL; //音频流
	SwsContext *vsc = NULL; //像素转换的上下文
	SwrContext *asc = NULL; //音频重采样上下文

	AVFrame *yuv = NULL; //输出yuv
	AVFrame *pcm = NULL; //重采样后输出的pcm

	int vpts = 0; //视频的pts;
	int apts = 0; //音频的pts;

	void Close()
	{
		if(ic) avformat_close_input(&ic);
		
		if(vc) 
		{
			avcodec_close(vc);
			avcodec_free_context(&vc);
		}
		if(ac)
		{
			avcodec_close(ac);
			avcodec_free_context(&ac);
		}
		if(vsc)
		{
			sws_freeContext(vsc);
			vsc = NULL;
		}

		if(yuv){ av_frame_free(&yuv);}
		if(pcm){ av_frame_free(&pcm);}

		if(asc)
		{
			swr_free(&asc);
		}

	}
	bool Init(const char* file)
	{
		Close();
		//封装文件的上限文
		avformat_alloc_output_context2(&ic,NULL,NULL,file);
		if(!ic)
		{
			cerr<<"avformat_alloc_output_context2 failed!"<<endl;
			return false;
		}
		filename = file;
		return true;
 	}
	bool AddVideoStream()
	{

		if(!ic) return false;
		//1.视频编码器创建
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
		if(!codec)
		{
			cerr<<"avcodec_find_encoder AV_CODEC_ID_H264 failed!"<<endl;
			return false;
		}
		vc = avcodec_alloc_context3(codec);
		if(!vc)
		{
			cerr<<"avcodec_alloc_context3 failed!"<<endl;
			return false;
		}
		//比特率,压缩后每秒大小
		vc->bit_rate = vBitrate;
		vc->width = outWidth;
		vc->height = outHeight;

		//时间基数
		vc->time_base = {1,outFPS};
		vc->framerate = {outFPS,1};
		
		//画面组大小,多少帧一个关键帧
		vc->gop_size = 50;
		//两个非b帧之间的b帧数量
		vc->max_b_frames = 0;
		//像素格式
		vc->pix_fmt = AV_PIX_FMT_YUV420P;
		//编码id
		vc->codec_id = AV_CODEC_ID_H264;
		//选项的设置
		av_opt_set(vc->priv_data,"preset","superfast",0);
		//统一的封装头
		vc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		//打开编码器
		int ret = avcodec_open2(vc,codec,NULL);
		if(ret != 0)
		{
			cerr<<"avcodec_open2 failed!"<<endl;
			return false;
		}
		cout<<"avcodec_open2 success!"<<endl;

		//添加视频流到上下文
		vs = avformat_new_stream(ic,NULL);
		//不包含编码信息
		vs->codecpar->codec_tag = 0;
		//把编码器信息拷贝到封装流里面
		avcodec_parameters_from_context(vs->codecpar,vc);
		av_dump_format(ic,0,filename.c_str(),1);


		//像素(尺寸)转换的上下文 rgb to yuv
		vsc = sws_getCachedContext(vsc,
			inWidth,inHeight,(AVPixelFormat)inPixFmt,//输入参数
			outWidth,outHeight,AV_PIX_FMT_YUV420P,
			SWS_BICUBIC,
			NULL,NULL,NULL
			);
		if(!vsc)
		{
			cerr<<"sws_getCachedContext failed!"<<endl;
			return false;
		}

		if(!yuv)
		{
			//对对象成员初始化
			yuv = av_frame_alloc();
			yuv->format = AV_PIX_FMT_YUV420P;
			yuv->width = outWidth;
			yuv->height = outHeight;
			//需要指定pts 每一帧加1
			yuv->pts = 0;

			//创建yuv 空间
			int ret = av_frame_get_buffer(yuv,32);
			if(ret != 0)
			{
				cerr<<"sws_getCachedContext failed!"<<endl;
				return false;
			}
		}

		return true;		
	}
	AVPacket *EncodeVideo(const unsigned char *rgb)
	{
		AVPacket *p = NULL;
		uint8_t *inData[AV_NUM_DATA_POINTERS] = { 0 };
		inData[0] = (uint8_t *)rgb;

		int insize[AV_NUM_DATA_POINTERS] = {0};
		insize[0] = inWidth * 4;

		//rgb to yuv
		int h = sws_scale(vsc, inData,insize,0,inHeight,
			yuv->data,yuv->linesize);
		if(h<0)
			return NULL;
		//cout<<h<<"|";
		yuv->pts = vpts;
		vpts++;
		//encode
		int ret = avcodec_send_frame(vc,yuv);
		if(ret != 0)
		{
			return NULL;
		}
		p = av_packet_alloc();
		ret = avcodec_receive_packet(vc,p);
		if(ret != 0 || p->size <= 0)
		{
			av_packet_free(&p);
			return NULL;
		}
		av_packet_rescale_ts(p,vc->time_base,vs->time_base);	
		p->stream_index = vs->index;
		return p;
	}

	bool WriteHead()
	{
		if(!ic) return false;		
		//打开io
		int ret = avio_open(&ic->pb,filename.c_str(),AVIO_FLAG_WRITE);
		if(ret != 0)
		{
			cerr<<"avio_open failed!"<<endl;
			return false;
		}
		cout<<"write "<<filename<<" head success"<<endl;

		//写入封装头
		ret = avformat_write_header(ic,NULL);
		if(ret != 0)
		{
			cerr<<"avformat_write_header failed!"<<endl;
			return false;
		}
		cout<<"write "<<filename<<" head success"<<endl;

		return true;
	}

	bool WriteFrame(AVPacket *pkt)
	{
		if(!ic || !pkt || pkt->size <= 0) return false;
		//av_write_frame
		if(av_interleaved_write_frame(ic,pkt) != 0) return false;
		return true;
	}

	virtual bool WriteEnd()
	{
		if(!ic || !ic->pb) return false;
		//写入尾部信息和索引
		if(av_write_trailer(ic) != 0)
		{
			cerr<<"av_write_trailer failed!"<<endl;
			return false;
		}

		//关闭输入io
		if(avio_close(ic->pb) != 0)
		{
			cerr<<"avio_close failed!"<<endl;
			return false;
		}
		cout<<"WriteEnd success!"<<endl;
		return true;
	}

	bool AddAudioStream()
	{
		//1.找到音频编码
		AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
		if(!codec)
		{
			cerr<<"avcodec_find_encoder AV_CODEC_ID_AAC failed!"<<endl;
			return false;
		}
		//2.创建并打开音频编码器
		ac = avcodec_alloc_context3(codec);
		if(!ac)
		{
			cerr<<"avcodec_alloc_context3 failed!"<<endl;
		}

		ac->bit_rate = aBitrate;
		ac->sample_rate = outSampleRate;
		ac->sample_fmt = (AVSampleFormat)outSampleFmt;
		ac->channels = outChannels;
		ac->channel_layout = av_get_default_channel_layout(outChannels);
		ac->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		int ret = avcodec_open2(ac,codec,NULL);
		if(ret != 0)
		{
			avcodec_free_context(&ac);
			cerr<<"avcodec_open2 failed!"<<endl;
			return false;
		}
		cout<<"avcodec_open2 AV_CODEC_ID_AAC success"<<endl;
		//新曾音频流
		as = avformat_new_stream(ic,NULL);
		if(!as)
		{
			cerr<<"avformat_new_stream failed!"<<endl;
			return false;
		}
		//参数设置
		as->codecpar->codec_tag = 0;
		avcodec_parameters_from_context(as->codecpar,ac);
		av_dump_format(ic,0,filename.c_str(),1);

		//音频重采样上下文
		asc = swr_alloc_set_opts(asc,
			ac->channel_layout,ac->sample_fmt,ac->sample_rate,//输入格式
			av_get_default_channel_layout(inChannels),(AVSampleFormat)inSampleFmt,inSampleRate,
			0,0);
		ret = swr_init(asc);
		if(ret != 0)
		{
			cerr<<"swr_init failed!"<<endl;
			return false;
		}
		//音频重采样后输出AVFrame
		if(!pcm)
		{
			pcm = av_frame_alloc();
			pcm->format = ac->sample_fmt;
			pcm->channels = ac->channels;
			pcm->channel_layout = ac->channel_layout;
			pcm->nb_samples = nb_sample;//一帧音频的采样数量
			ret = av_frame_get_buffer(pcm,0);
			if(ret < 0)
			{
				cerr<<"av_frame_get_buffer failed "<<endl;
				return false;
			}
			cout<<"audio av_frame_get_buffer success"<<endl;
		}
		return true;
	}
	AVPacket* EncodeAudio(const unsigned char *d)
	{
		const uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
		data[0] = (uint8_t *)d;
		int len = swr_convert(asc,pcm->data,pcm->nb_samples,
			data,pcm->nb_samples);
		cout<<len<<"*";

		//2音频编码
		int ret = avcodec_send_frame(ac,pcm);
		if(ret != 0)
		{
			return NULL;
		}
		//需要对pkt进行释放
		AVPacket *pkt = av_packet_alloc();
		av_init_packet(pkt);
		ret = avcodec_receive_packet(ac,pkt);
		if(ret != 0)
		{
			av_packet_free(&pkt);
			return NULL;
		}
		cout<<pkt->size<<"|";
		pkt->stream_index = as->index;
		pkt->pts = apts;
		pkt->dts = pkt->pts;
		apts += av_rescale_q(pcm->nb_samples,{1,ac->sample_rate},ac->time_base);
		return pkt;
	}

	bool IsVideoBefore()
	{	
		if(!ic || !as || !vs) 
		{
			std::cout<<"!ic || !as || !vs = false"<<std::endl;
			return false;
		}

		int ret = av_compare_ts(vpts,vc->time_base,apts,ac->time_base);
		if(ret <= 0)
		{
			std::cout<<"IsVideoBefore = true"<<std::endl;	
			return true;
		}
		std::cout<<"IsVideoBefore = false"<<std::endl;
		return false;
	}
};

XVideoWriter* XVideoWriter::Get(unsigned short index)
{
	static bool isfirst = true;
	if(isfirst)
	{
		av_register_all();
		avcodec_register_all();
		isfirst = false;
	}

	static CXVideoWriter wrs[65535];
	return &wrs[index]; 
}

XVideoWriter::XVideoWriter()
{

}

XVideoWriter::~XVideoWriter()
{

}

bool XVideoWriter::AddVideoStream()
{
	return true;
}

rgb_pcm_to_mp4.cpp

#include "XVideoWriter.h"
#include <iostream>
using namespace std;
int main()
{
	char outfile[] = "rgbpcm.mp4";
	char rgbfile[] = "test.rgb";
	char pcmfile[] = "test.pcm";

	XVideoWriter *xw = XVideoWriter::Get(0);
	cout<<xw->Init(outfile);
	cout<<xw->AddVideoStream();
	xw->AddAudioStream();
	FILE *fp = fopen(rgbfile,"rb");
	if(!fp)
	{
		cout<<"fopen "<<rgbfile<<" failed!"<<endl;
		return  -1;
	}

	FILE *fa = fopen(pcmfile,"rb");
	if(!fa)
	{
		cout<<"open "<<pcmfile<<" failed!"<<endl;
		return -1;
	}


	int size = xw->inWidth*xw->inHeight*4;
	unsigned char *rgb = new unsigned char[size];

	int asize = xw->nb_sample * xw->inChannels * 2;
	unsigned char *pcm = new unsigned char[asize];
	xw->WriteHead();
	AVPacket *pkt = NULL;
	int len = 0;
	for(;;)
	{
		if(xw->IsVideoBefore())
		{
			len = fread(rgb,1,size,fp);
			if(len<=0)
				break;
			pkt = xw->EncodeVideo(rgb);
			if(pkt) 
				cout<<".";
			else
			{
				cout<<"-";
				continue;
			}
			if(xw->WriteFrame(pkt))
			{
				cout<<"+";
			}
		}
		else
		{		
			len = fread(pcm,1,asize,fa);
			if(len <= 0) break;
			pkt = xw->EncodeAudio(pcm);
			xw->WriteFrame(pkt);
		}
	}
	xw->WriteEnd();
	delete rgb;
	rgb = NULL;
	cout<<"\n===========================end===========================\n";
	//rgb 转 yuv

	//编码视频
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值