FFmpeg学习之视频像素和尺寸转换

1. 获取像素格式转换的上下文

sws_getCachedContext

2. 像素转换

sws_scale

#include 
#include 

using namespace std;

//测试解封装
extern "C"{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
}

//引用库
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"swscale.lib")

//static修饰的分数转浮点函数,避免功能性函数重定义
static double r2d(AVRational r)
{
	return (r.den == 0) ? 0 : (double)r.num/(double)r.den;
}

void XSleep(int ms)
{
	// c++11
	chrono::milliseconds du(ms);
	this_thread::sleep_for(du);
}

int main(int argc, char* argv[])
{
	cout << "Test Demux FFmpeg.club" << endl;

	//初始化封装库
	av_register_all();

	//初始化网络库 可以打开rtsp rtmp http 协议的流媒体视频)
	avformat_network_init();

	//注册解码器
	avcodec_register_all();

	//参数设置
	AVFormatContext *ic = NULL;
	const char* path = "abc.mp4";

	AVDictionary *opts = NULL;//NULL表示使用默认配置
	//设置rtsp流,以tcp协议打开
	//av_dict_set(&opts, "rtsp_transport", "tcp", 0);

	//设置网络延时时间
	//av_dict_set(&opts, "max_delay", "500", 0);

	//解封装上下文
	int ret = avformat_open_input(
		&ic,
		path,		//视频源
		0,			//0表示自动选择解封装器
		&opts		//参数设置,比如rtsp的延时时间
		);

	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "open " << path << " failed ! :" << buff << endl;
		getchar();
		return -1;
	}
	cout << "open " << path << " succeed !" << endl;

	//获取流信息
	ret = avformat_find_stream_info(ic, 0);

	//总时长
	int totalTime = ic->duration/AV_TIME_BASE;
	cout << path << " total time is " << totalTime << " s." << endl;

	//打印视频流详细信息
	av_dump_format(ic, 0, path, 0);

	//音视频索引,用于读取时区分音视频数据
	int audioStream = 0;
	int videoStream = 1;

	//获取音视频流信息 (通过遍历,或函数获取)
	//遍历方式
	for (int i = 0; i < ic->nb_streams; i++)
	{
		AVStream *as = ic->streams[i];
		cout << "format = " << as->codecpar->format << endl;
		cout << "codec_id = " << as->codecpar->codec_id << endl;
		//音频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;
			cout << i << "音频信息" << endl;
			cout << "sample_rate = " <codecpar->sample_rate << endl;
			//AVSampleFormat
			cout << "channels = " << as->codecpar->channels << endl;
			//一帧数据:单通道样本数
			cout << "frame size = " << as->codecpar->frame_size << endl;
		}
		//视频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;
			cout << i << "视频信息" << endl;
			//这里边可能没有宽高参数
			cout << "width = " << as->codecpar->width << endl;
			cout << "height = " << as->codecpar->height << endl;
			//帧率 fps 
			cout << "video FPS = " << r2d(as->avg_frame_rate) << endl;
		}
	}
	//函数方式获取
	audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);

	///
	//视频解码器打开
	//找到解码器
	AVCodec *vcodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
	if (!vcodec)
	{
		cout << "couldn't found the codec, codec_id is " << ic->streams[videoStream]->codecpar->codec_id << endl;
		getchar();
		return -1;
	}
	cout << "video AVCodec id is :" << ic->streams[videoStream]->codecpar->codec_id << endl;

	//创建解码器上下文
	AVCodecContext *vc = avcodec_alloc_context3(vcodec);

	//配置解码器上下文参数(由streams中的配置复制过来)
	avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);
	//8线程解码
	vc->thread_count = 8;

	//打开解码器上下文
	ret = avcodec_open2(vc, 0, 0);
	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "avcodec_open2 error ! " << buff << endl;
		getchar();
		return -1;
	}
	cout << "video avcodec_open2 succeed ! " << endl;
	getchar();

	///
	//音频解码器打开
	//找到解码器
	AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
	if (!acodec)
	{
		cout << "couldn't found the codec, codec_id is " << ic->streams[audioStream]->codecpar->codec_id << endl;
		getchar();
		return -1;
	}
	cout << "audio AVCodec id is :" << ic->streams[audioStream]->codecpar->codec_id << endl;

	//创建解码器上下文
	AVCodecContext *ac = avcodec_alloc_context3(acodec);

	//配置解码器上下文参数(由streams中的配置复制过来)
	avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);
	//8线程解码
	vc->thread_count = 8;

	//打开解码器上下文
	ret = avcodec_open2(ac, 0, 0);
	if (ret != 0)
	{
		char buff[1024] = { 0 };
		av_strerror(ret, buff, sizeof(buff) - 1);
		cout << "avcodec_open2 error ! " << buff << endl;
		getchar();
		return -1;
	}
	cout << "audio avcodec_open2 succeed ! " << endl;
	getchar();

	//AVPacket 申请空间、初始化
	AVPacket *pkt = av_packet_alloc();
	//AVFrame 申请空间、初始化
	AVFrame *frame = av_frame_alloc();
	//像素格式转换上下文
	SwsContext *vctx = NULL;

	unsigned char *rgb = NULL;
	while (1)
	{
		int ret = av_read_frame(ic, pkt);
		if (ret)
		{
			//读到结尾
			cout << "==============end==============" << endl;
			//跳转到3秒位置
			int ms = 3000;//3秒,根据时间基数转换
			int64_t pos = (double)ms / (double)1000 * r2d(ic->streams[pkt->stream_index]->time_base);
			av_seek_frame(ic, 0, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
			cout << " \\ \\ \\ \\ \\ \\ \\ \\ at 3 ms" << endl;
			continue;
		}
		cout << "pkt->size = " << pkt->size << endl;
		//显示的时间
		cout << "pkt->pts = " << pkt->pts << endl;
		cout << "pkt->pts ms = " << pkt->pts * (r2d(ic->streams[pkt->stream_index]->time_base) * 1000) << endl;

		//XSleep(500);

		//解码的时间
		cout << "pkt->dts = " << pkt->dts << endl;

		//解码上下文
		AVCodecContext *cc = 0;
		if (pkt->stream_index == videoStream)
		{
			cc = vc;
			cout << "图像" << endl;
		}
		if (pkt->stream_index == audioStream)
		{
			cc = ac;
			cout << "音频" << endl;
		}

		///解码视频
		//发送packet到线程解码,发完立即返回
		ret = avcodec_send_packet(cc, pkt);

		//释放库内部单次使用的内存,并将引用计数-1
		av_packet_unref(pkt);

		if (ret != 0)
		{
			char buff[1024] = { 0 };
			av_strerror(ret, buff, sizeof(buff) - 1);
			cout << " avcodec_send_packet error, " << buff << endl;
			continue;
		}

		while (1)
		{
			//从线程中拿解码后的数据,一次send可能对应多次receive
			ret = avcodec_receive_frame(cc, frame);
			if (ret != 0)
			{
				break;
			}
			cout << " recv frame " << frame->format << " " << frame->linesize[0] << endl;
			//视频像素转换
			if (cc == vc)
			{
				//创建或拿到转换上下文
				vctx = sws_getCachedContext(
					vctx,		//传递NULL会新创建
					frame->width, frame->height,	//输入的宽高
					(AVPixelFormat)frame->format, 	//输入的格式
					frame->width, frame->height,	//输出的宽高
					AV_PIX_FMT_RGBA,				//输出格式RGBA
					SWS_BILINEAR,					//转换的算法
					0,0,0);
				if (vctx)
				{
					cout << " 像素格式尺寸转换上下文创建或者获取成功 ! " << endl;
					if (rgb == NULL)
					{
						rgb = new unsigned char[frame->width*frame->height * 4];
					}
					uint8_t *data[2] = { 0 };
					data[0] = rgb;
					int lines[2] = { 0 };
					lines[0] = frame->width * 4;
					//调用像素转换
					sws_scale(
						vctx,
						frame->data,		//输入数据
						frame->linesize,	//输入行大小
						0,
						frame->height,		//输入高度
						data,				//输出的数据和大小
						lines
						);
				}
				else
				{
					cout << " 像素格式尺寸转换上下文创建或者获取失败 ! " << endl;
				}
			}
		}
	}
	//释放空间
	av_frame_free(&frame);
	av_packet_free(&pkt);

	if (ic)
	{
		//释放封装上下文,并把ic并置0
		avformat_close_input(&ic);
	}

	getchar();
	return 0;
}

你可能感兴趣的:(FFmpeg学习笔记,音视频,c++)