FFmpeg_3.2.4+SDL_2.0.5学习(1)音视频解码帧及显示/播放数据

int OpenAVFile(const char* szFileName)
{
	char errbuf[256] = { 0 };
	int iRes = 0;
	int vindex = -1;
	AVFormatContext* pFmtCtx = NULL;
	AVCodecContext* vCodecCtx = NULL;
	AVCodec* vCodec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* pfe = NULL;
	AVFrame* YUV = NULL;
	uint8_t* buf = NULL;
	struct SwsContext* img_ctx = NULL;

	SDL_Window* window = NULL;
	SDL_Renderer* renderer = NULL;
	SDL_Texture* texture = NULL;
	SDL_Rect rect= { 0 };

	av_register_all();
	if(SDL_Init(SDL_INIT_VIDEO) != 0)
		return ERROR_INIT;

	pFmtCtx = avformat_alloc_context();
	if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
		return ERROR_OPEN;
	if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
		return ERROR_FIND;
	av_dump_format(pFmtCtx, -1, szFileName, NULL);

	for (int i = 0; i < pFmtCtx->nb_streams; ++i)
	{
		if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
			vindex = i;
	}
	if (vindex == -1)
		return ERROR_FIND;

	vCodecCtx = avcodec_alloc_context3(NULL);
	if(avcodec_parameters_to_context(vCodecCtx, pFmtCtx->streams[vindex]->codecpar) < 0)
		return ERROR_COPY;
	vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
	if (!vCodec)
		return ERROR_FIND;
	if(avcodec_open2(vCodecCtx, vCodec, NULL) != 0)
		return ERROR_OPEN;

	window = SDL_CreateWindow("video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 680, 540, SDL_WINDOW_OPENGL);
	renderer = SDL_CreateRenderer(window, -1, 0);
	texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, vCodecCtx->width, vCodecCtx->height);
	if (!window || !renderer || !texture)
		return ERROR_CREATE;
	rect.w = vCodecCtx->width;
	rect.h = vCodecCtx->height;

	img_ctx = sws_getContext(vCodecCtx->width, vCodecCtx->height, vCodecCtx->pix_fmt,
		vCodecCtx->width, vCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL,NULL);
	if (!img_ctx)
		return ERROR_GET;
	pkt = av_packet_alloc();
	pfe = av_frame_alloc();
	YUV = av_frame_alloc();
	buf = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height,1));
	av_image_fill_arrays(YUV->data, YUV->linesize, buf, AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height, 1);
	while (av_read_frame(pFmtCtx, pkt) >= 0)
	{
		if (pkt->stream_index == vindex)
		{
			if ((iRes = avcodec_send_packet(vCodecCtx, pkt)) != 0)
				av_strerror(iRes, errbuf, 256);//return -5;
			if ((iRes=  avcodec_receive_frame(vCodecCtx, pfe)) != 0)
				av_strerror(iRes, errbuf, 256); //return -6;
			sws_scale(img_ctx, pfe->data, pfe->linesize, 0, vCodecCtx->height, YUV->data, YUV->linesize);
			
			SDL_UpdateTexture(texture, &rect, YUV->data[0], YUV->linesize[0]);
			SDL_RenderClear(renderer);
			SDL_RenderCopy(renderer, texture, NULL, NULL);
			SDL_RenderPresent(renderer);
		}
	}
	
	av_free(buf);
	av_frame_free(&YUV);
	av_frame_free(&pfe);
	av_packet_free(&pkt);
	sws_freeContext(img_ctx);
	SDL_DestroyTexture(texture);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
	avcodec_free_context(&vCodecCtx);
	avformat_close_input(&pFmtCtx);
	avformat_free_context(pFmtCtx);
	return 0;
}
uint8_t* g_buf = NULL;
int g_MaxLen = 0;
int g_CurPos = 0;
void aCallback(void *userdata, Uint8 * stream, int len)
{
	SDL_memset(stream, 0, len);//important
	SDL_MixAudio(stream, g_buf, len, SDL_MIX_MAXVOLUME);//memcpy(stream, g_buf, len);
	g_CurPos += len;
	g_MaxLen -= len;
}

int OpenAVFile(const char* szFileName)
{
	char errbuf[256] = { 0 };
	int iRes = 0;
	int aindex = -1;
	AVFormatContext* pFmtCtx = NULL;
	AVCodecContext* aCodecCtx = NULL;
	AVCodec* aCodec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* pfe = NULL;
	uint8_t* buf = NULL;
	struct SwrContext* audio_ctx = NULL;
	SDL_AudioSpec want = { 0 };
	SDL_AudioSpec recv = { 0 };

	av_register_all();
	if (SDL_Init(SDL_INIT_AUDIO) != 0)
		return ERROR_INIT;

	pFmtCtx = avformat_alloc_context();
	if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
		return ERROR_OPEN;
	if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
		return ERROR_FIND;
	av_dump_format(pFmtCtx, -1, szFileName, NULL);

	for (int i = 0; i < pFmtCtx->nb_streams; ++i)
	{
		if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
			aindex = i;
	}
	if (aindex == -1)
		return ERROR_FIND;

	aCodecCtx = avcodec_alloc_context3(NULL);
	if (avcodec_parameters_to_context(aCodecCtx, pFmtCtx->streams[aindex]->codecpar) < 0)
		return ERROR_COPY;
	aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
	if (!aCodec)
		return ERROR_FIND;
	if (avcodec_open2(aCodecCtx, aCodec, NULL) != 0)
		return ERROR_OPEN;

	want.callback = aCallback;
	//want.channels = 1;//设为单声道就不用装换pannel了
	want.channels = aCodecCtx->channels;
	want.format = AUDIO_S16SYS;//ffmpeg的fmt与SDL的不一样,但是可以自己计算转换
	want.freq = aCodecCtx->sample_rate;
	want.samples = aCodecCtx->frame_size;
	want.silence = 0;
	want.userdata = aCodecCtx;
	SDL_OpenAudio(&want, &recv);

	//audio_ctx = swr_alloc();
	audio_ctx = swr_alloc_set_opts(NULL, av_get_default_channel_layout(recv.channels), AV_SAMPLE_FMT_S16, recv.freq,
		av_get_default_channel_layout(aCodecCtx->channels), aCodecCtx->sample_fmt, aCodecCtx->sample_rate, 0, NULL);
	swr_init(audio_ctx);//必须要

	pkt = av_packet_alloc();
	pfe = av_frame_alloc();
	g_buf = (uint8_t*)av_malloc(av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1));
	while (av_read_frame(pFmtCtx, pkt) >= 0)
	{
		if (pkt->stream_index == aindex)
		{
			if ((iRes = avcodec_send_packet(aCodecCtx, pkt)) != 0)
				av_strerror(iRes, errbuf, 256);//return -5;
			if ((iRes = avcodec_receive_frame(aCodecCtx, pfe)) != 0)
				av_strerror(iRes, errbuf, 256); //return -6;

			//单声道
			//memset(g_buf, 0, 10240);
			//memcpy(g_buf, pfe->data[0], pfe->linesize[0]);
			//g_CurPos = 0;
			//g_MaxLen = pfe->linesize[0];

			swr_convert(audio_ctx, &g_buf, av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1), 
				(const uint8_t **)pfe->data, pfe->nb_samples);
			g_CurPos = 0;
			g_MaxLen = av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1);

			SDL_PauseAudio(0);
			while (g_MaxLen > 0)
				SDL_Delay(1);
		}
	}

	av_free(buf);
	av_frame_free(&pfe);
	av_packet_free(&pkt);
	swr_free(&audio_ctx);
	SDL_Quit();
	avcodec_free_context(&aCodecCtx);
	avformat_close_input(&pFmtCtx);
	avformat_free_context(pFmtCtx);
	return 0;
}

视频的重要处理: sws_getContext,获得转换上下文

av_image_get_buffer_size,获得(转换后)图片大小

av_image_fill_arrays,将自定义内存块绑定到输出的AVFrame中

sws_scale,转换

音频的重要处理:

av_get_default_channel_layout,根据声道数获取默认的声道布局

swr_alloc_set_opts,获取转换上下文

swr_init,获取到上下文后必须初始化

av_samples_get_buffer_size,计算(参数格式的)音频数据的大小

swr_convert,转换 对于音频的相关参数,FFmpeg和SDL中的channels,sample_rate...等等这些"数值"都是可以直接互相赋值的。但是format,FFmpeg和SDL就各自有自己的定义。

下面是我自己做的由AVSampleFormat转SDL_AudioFormat的函数

/*{
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
AV_SAMPLE_FMT_S16,         ///< signed 16 bits
AV_SAMPLE_FMT_S32,         ///< signed 32 bits
AV_SAMPLE_FMT_FLT,         ///< float
AV_SAMPLE_FMT_DBL,         ///< double

AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP,        ///< float, planar
AV_SAMPLE_FMT_DBLP,        ///< double, planar
AV_SAMPLE_FMT_S64,         ///< signed 64 bits
AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
}*/
/*  \verbatim
++---------------------- - sample is signed if set
||
|| ++---------- - sample is bigendian if set
|| ||
|| || ++-- - sample is float if set
|| || ||
|| || || +-- - sample bit size-- - +
|| || || | |
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
\endverbatim
*
*  There are macros in SDL 2.0 and later to query these bits.
*/
SDL_AudioFormat GetFmt(AVSampleFormat avfmt)
{
	SDL_AudioFormat res = 0;
	bool isp = false;
	bool iss = false;
	bool isf = false;
	bool isd = false;
	int bits = 0;
	switch (avfmt)
	{
	case AV_SAMPLE_FMT_NONE://// = -1,
		break;
	case	AV_SAMPLE_FMT_U8:         ///< unsigned 8 bits
		bits = 8;
		break;
	case	AV_SAMPLE_FMT_S16:         ///< signed 16 bits
		bits = 16;
		iss = true;
		break;
	case AV_SAMPLE_FMT_S32:         ///< signed 32 bits
		bits = 32;
		iss = true;
		break;
	case AV_SAMPLE_FMT_FLT:         ///< float
		isf = true;
		break;
	case	AV_SAMPLE_FMT_DBL:         ///< double
		isd = true;
		break;
	case	AV_SAMPLE_FMT_U8P:         ///< unsigned 8 bits, planar
		bits = 8;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S16P:        ///< signed 16 bits, planar
		bits = 16;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S32P:        ///< signed 32 bits, planar
		bits = 32;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_FLTP:        ///< float, planar
		isf = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_DBLP:       ///< double, planar
		isd = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S64:        ///< signed 64 bits
		bits = 64;
		iss = true;
		break;
	case	AV_SAMPLE_FMT_S64P:       ///< signed 64 bits, planar
		bits = 64;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_NB:           ///< Number of sample formats. DO NOT USE if linking dynamically
		break;
	}
	if (iss)
		res |= 1 << 15;
	if (isf)
		res |= 1 << 8;
	bits &= 255;
	res |= bits;

	return res;
}

例子代码中为了简便,SDL播放音频的format就用AUDIO_S16SYS,对应FFmpeg就是AV_SAMPLE_FMT_S16,都标识signed 16bit。注意SDL中音频数据没有planner。

代码下载

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏搞前端的李蚊子

Html5模拟通讯录人员排序(sen.js)

// JavaScript Document  var PY_Json_Str = ""; var PY_Str_1 = ""; var PY_Str_...

5896
来自专栏Pulsar-V

Save Camera Document

#pragma once #include "HCCamera.h" #include <time.h> #include <cstdio> #incl...

2828
来自专栏余生开发

echarts太阳分布图-饼图来回穿梭

var dom = document.getElementById("container");

1172
来自专栏码匠的流水账

spring security reactive获取security context

本文主要研究下reactive模式下的spring security context的获取。

1762
来自专栏MelonTeam专栏

Bitmap 源码阅读笔记

导语: Android 系统上的图片的处理,跟Bitmap 这个类脱不了关系,我们有必要去深入阅读里面的源码,以便在工作中能更好的处理Bitmap相关的问题...

2498
来自专栏码匠的流水账

聊聊spring cloud的HystrixAutoConfiguration

本文主要研究一下spring cloud的HystrixAutoConfiguration

972
来自专栏技术之路

wpf键盘记录器

很简单的一个wpf键盘记录器 ? 这个程序我一样用了全局勾子,之前用的都是winform上运行了,前一段时间 在国外的论坛上逛看到了一个wpf能用的就做了一个小...

2005
来自专栏我的博客

egret自定义loadingUI

class LoadingUI extends egret.Sprite{ private textField:egret.TextField...

3858
来自专栏项勇

[Android笔记7]之通过DatePickerDialog,TimePickerDialog调用系统时间设置

2763
来自专栏linux驱动个人学习

高通msm8909耳机调试

1、DTS相应修改: DTS相关代码:kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi: 1 s...

7475

扫码关注云+社区