(ffmpeg)ffmpeg+SDL的简单播放器(雷霄骅)更新版

代码源自雷神,一个是播放音频的demo,可以播放MP3和AAC,但是MP3应该是没有封面的。另一个是播放ts格式的视频,没有声音。源码可以到雷神博客下载,但是因为ffmpeg库的更新问题,并不能直接在ubuntu下直接运行,笔者做了修改,在ubuntu 18.04下可以直接编译运行。

环境配置请参考之前的博文:

(ffmpeg)ubuntu18.04安装ffmpeg4.4

ffmpeg版本:4.4

1.音频播放器

/**

 * 最简单的基于FFmpeg的音频播放器 2 

 * Simplest FFmpeg Audio Player 2 

 *

 * 雷霄骅 Lei Xiaohua

 * leixiaohua1020@126.com

 * 中国传媒大学/数字电视技术

 * Communication University of China / Digital TV Technology

 * http://blog.csdn.net/leixiaohua1020

 *

 * 本程序实现了音频的解码和播放。

 * 是最简单的FFmpeg音频解码方面的教程。

 * 通过学习本例子可以了解FFmpeg的解码流程。

 *

 * 该版本使用SDL 2.0替换了第一个版本中的SDL 1.0。

 * 注意:SDL 2.0中音频解码的API并无变化。唯一变化的地方在于

 * 其回调函数的中的Audio Buffer并没有完全初始化,需要手动初始化。

 * 本例子中即SDL_memset(stream, 0, len);

 *

 * This software decode and play audio streams.

 * Suitable for beginner of FFmpeg.

 *

 * This version use SDL 2.0 instead of SDL 1.2 in version 1

 * Note:The good news for audio is that, with one exception, 

 * it's entirely backwards compatible with 1.2.

 * That one really important exception: The audio callback 

 * does NOT start with a fully initialized buffer anymore. 

 * You must fully write to the buffer in all cases. In this 

 * example it is SDL_memset(stream, 0, len);

 *

 * Version 2.0

 */

#include <stdio.h>

#include <stdlib.h>

#include <string.h>



#define __STDC_CONSTANT_MACROS



#ifdef _WIN32

//Windows

extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

#include "libswresample/swresample.h"

#include "SDL2/SDL.h"

};

#else

//Linux...

#ifdef __cplusplus

extern "C"

{

#endif

#include <libavcodec/avcodec.h>

#include <libavformat/avformat.h>

#include <libswresample/swresample.h>

#include <SDL2/SDL.h>

#ifdef __cplusplus

};

#endif

#endif



#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio



//Output PCM

#define OUTPUT_PCM 0

//Use SDL

#define USE_SDL 1



//Buffer:

//|-----------|-------------|

//chunk-------pos---len-----|

static  Uint8  *audio_chunk; 

static  Uint32  audio_len; 

static  Uint8  *audio_pos; 



/* The audio function callback takes the following parameters: 

 * stream: A pointer to the audio buffer to be filled 

 * len: The length (in bytes) of the audio buffer 

*/ 

void  fill_audio(void *udata,Uint8 *stream,int len){ 

	//SDL 2.0

	SDL_memset(stream, 0, len);

	if(audio_len==0)

		return; 



	len=(len>audio_len?audio_len:len);	/*  Mix  as  much  data  as  possible  */ 



	// SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);

	memcpy(stream, audio_pos, len);

	audio_pos += len; 

	audio_len -= len; 

} 

//-----------------





int main(int argc, char* argv[])

{

	AVFormatContext	*pFormatCtx;

	int				i, audioStream;

	AVCodecContext	*pCodecCtx;

	const AVCodec			*pCodec;

	AVPacket		*packet;

	uint8_t			*out_buffer;

	AVFrame			*pFrame;

	SDL_AudioSpec wanted_spec;

    int ret;

	uint32_t len = 0;

	int got_picture;

	int index = 0;

	int64_t in_channel_layout;

	struct SwrContext *au_convert_ctx;



	FILE *pFile=NULL;

	char url[]="WavinFlag.aac";//xiaoqingge.mp3



	// av_register_all();//modify

	avformat_network_init();

	pFormatCtx = avformat_alloc_context();

	//Open

	if(avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0){

		printf("Couldn't open input stream.\n");

		return -1;

	}

	// Retrieve stream information

	if(avformat_find_stream_info(pFormatCtx,NULL)<0){

		printf("Couldn't find stream information.\n");

		return -1;

	}

	// Dump valid information onto standard error

	av_dump_format(pFormatCtx, 0, url, false);



	// Find the first audio stream

	audioStream=-1;

	for(i=0; i < pFormatCtx->nb_streams; i++)

		if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){

		// if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){

			audioStream=i;

			break;

		}



	if(audioStream==-1){

		printf("Didn't find a audio stream.\n");

		return -1;

	}



	// Get a pointer to the codec context for the audio stream

	pCodecCtx = avcodec_alloc_context3(NULL);

	if(pCodecCtx == NULL)

	{

		printf("pCodecCtx err\r\n");

		return -1;

	}

	avcodec_parameters_to_context(pCodecCtx,pFormatCtx->streams[audioStream]->codecpar);

	// pCodecCtx=pFormatCtx->streams[audioStream]->codec;



	pCodecCtx->pkt_timebase = pFormatCtx->streams[audioStream]->time_base;

	// Find the decoder for the audio stream

	pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

	if(pCodec==NULL){

		printf("Codec not found.\n");

		return -1;

	}



	// Open codec

	if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){

		printf("Could not open codec.\n");

		return -1;

	}



	

#if OUTPUT_PCM

	pFile=fopen("output.pcm", "wb");

#endif



	packet=(AVPacket *)av_malloc(sizeof(AVPacket));

	// av_init_packet(packet);



	//Out Audio Param

	uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;

	//nb_samples: AAC-1024 MP3-1152

	int out_nb_samples=pCodecCtx->frame_size;

	AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;

	int out_sample_rate=44100;

	int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);

	//Out Buffer Size

	int out_buffer_size=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);



	out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);

	pFrame=av_frame_alloc();

//SDL------------------

#if USE_SDL

	//Init

	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  

		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 

		return -1;

	}

	//SDL_AudioSpec

	wanted_spec.freq = out_sample_rate; 

	wanted_spec.format = AUDIO_S16SYS; 

	wanted_spec.channels = out_channels; 

	wanted_spec.silence = 0; 

	wanted_spec.samples = out_nb_samples; 

	wanted_spec.callback = fill_audio; 

	wanted_spec.userdata = pCodecCtx; 



	if (SDL_OpenAudio(&wanted_spec, NULL)<0){ 

		printf("can't open audio.\n"); 

		return -1; 

	} 

#endif



	//FIX:Some Codec's Context Information is missing

	in_channel_layout=av_get_default_channel_layout(pCodecCtx->channels);

	//Swr



	au_convert_ctx = swr_alloc();

	au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,out_channel_layout, out_sample_fmt, out_sample_rate,

		in_channel_layout,pCodecCtx->sample_fmt , pCodecCtx->sample_rate,0, NULL);

	swr_init(au_convert_ctx);



	//Play

	SDL_PauseAudio(0);



	while(av_read_frame(pFormatCtx, packet)>=0){

		if(packet->stream_index==audioStream){

			// ret = avcodec_decode_audio4( pCodecCtx, pFrame,&got_picture, packet);

			ret = avcodec_send_packet(pCodecCtx, packet);

			if ( ret < 0 ) {

				continue;

            }

			got_picture = avcodec_receive_frame(pCodecCtx, pFrame); //got_picture = 0 success, a frame was returned

			// if ( ret < 0 ) {

            //     printf("Error in decoding audio frame.\n");

            //     return -1;

            // }

			if ( got_picture == 0 ){

				swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);

#if 1

				printf("index:%5d\t pts:%ld\t packet size:%d\n",index,packet->pts,packet->size);

#endif





#if OUTPUT_PCM

				//Write PCM

				fwrite(out_buffer, 1, out_buffer_size, pFile);

#endif

				index++;

			}



#if USE_SDL

			while(audio_len>0)//Wait until finish

				SDL_Delay(1); 



			//Set audio buffer (PCM data)

			audio_chunk = (Uint8 *) out_buffer; 

			//Audio buffer length

			audio_len =out_buffer_size;

			audio_pos = audio_chunk;



#endif

		}

		// av_free_packet(packet);

		av_packet_unref(packet);

	}



	swr_free(&au_convert_ctx);



#if USE_SDL

	SDL_CloseAudio();//Close SDL

	SDL_Quit();

#endif

	

#if OUTPUT_PCM

	fclose(pFile);

#endif

	av_free(out_buffer);

	avcodec_close(pCodecCtx);

	avformat_close_input(&pFormatCtx);



	return 0;

}





编译:

gcc simplest_ffmpeg_audio_player.cpp -g -o simplest_ffmpeg_audio_player.out -I /usr/local/include -L /usr/local/lib \
-lSDL2main -lSDL2 -lavformat -lavcodec -lavutil -lswresample -lm -lpthread

2.视频播放器

/**

 * 最简单的基于FFmpeg的视频播放器2(SDL升级版)

 * Simplest FFmpeg Player 2(SDL Update)

 *

 * 雷霄骅 Lei Xiaohua

 * leixiaohua1020@126.com

 * 中国传媒大学/数字电视技术

 * Communication University of China / Digital TV Technology

 * http://blog.csdn.net/leixiaohua1020

 *

 * 第2版使用SDL2.0取代了第一版中的SDL1.2

 * Version 2 use SDL 2.0 instead of SDL 1.2 in version 1.

 *

 * 本程序实现了视频文件的解码和显示(支持HEVC,H.264,MPEG2等)。

 * 是最简单的FFmpeg视频解码方面的教程。

 * 通过学习本例子可以了解FFmpeg的解码流程。

 * 本版本中使用SDL消息机制刷新视频画面。

 * This software is a simplest video player based on FFmpeg.

 * Suitable for beginner of FFmpeg.

 *

 * 备注:

 * 标准版在播放视频的时候,画面显示使用延时40ms的方式。这么做有两个后果:

 * (1)SDL弹出的窗口无法移动,一直显示是忙碌状态

 * (2)画面显示并不是严格的40ms一帧,因为还没有考虑解码的时间。

 * SU(SDL Update)版在视频解码的过程中,不再使用延时40ms的方式,而是创建了

 * 一个线程,每隔40ms发送一个自定义的消息,告知主函数进行解码显示。这样做之后:

 * (1)SDL弹出的窗口可以移动了

 * (2)画面显示是严格的40ms一帧

 * Remark:

 * Standard Version use's SDL_Delay() to control video's frame rate, it has 2

 * disadvantages:

 * (1)SDL's Screen can't be moved and always "Busy".

 * (2)Frame rate can't be accurate because it doesn't consider the time consumed 

 * by avcodec_decode_video2()

 * SU(SDL Update)Version solved 2 problems above. It create a thread to send SDL 

 * Event every 40ms to tell the main loop to decode and show video frames.

 */



#include <stdio.h>



#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"

#include "SDL2/SDL.h"

};

#else

//Linux...

#ifdef __cplusplus

extern "C"

{

#endif

#include <libavcodec/avcodec.h>

#include <libavformat/avformat.h>

#include <libswscale/swscale.h>

#include <libavutil/imgutils.h>

#include <SDL2/SDL.h>

#ifdef __cplusplus

};

#endif

#endif



//Refresh Event

#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)



#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)



int thread_exit=0;

int thread_pause=0;



int sfp_refresh_thread(void *opaque){

	thread_exit=0;

	thread_pause=0;



	while (!thread_exit) {

		if(!thread_pause){

			SDL_Event event;

			event.type = SFM_REFRESH_EVENT;

			SDL_PushEvent(&event);

		}

		SDL_Delay(40);

	}

	thread_exit=0;

	thread_pause=0;

	//Break

	SDL_Event event;

	event.type = SFM_BREAK_EVENT;

	SDL_PushEvent(&event);



	return 0;

}





int main(int argc, char* argv[])

{



	AVFormatContext	*pFormatCtx;

	int				i, videoindex;

	AVCodecContext	*pCodecCtx;

	const AVCodec			*pCodec;

	AVFrame	*pFrame,*pFrameYUV;

	unsigned char *out_buffer;

	AVPacket *packet;

	int ret, got_picture;



	//------------SDL----------------

	int screen_w,screen_h;

	SDL_Window *screen; 

	SDL_Renderer* sdlRenderer;

	SDL_Texture* sdlTexture;

	SDL_Rect sdlRect;

	SDL_Thread *video_tid;

	SDL_Event event;



	struct SwsContext *img_convert_ctx;



	//char filepath[]="bigbuckbunny_480x272.h265";

	char filepath[]="Titanic.ts";



	// av_register_all();

	avformat_network_init();

	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){

		printf("Couldn't find stream information.\n");

		return -1;

	}

	videoindex=-1;

	for(i=0; i<pFormatCtx->nb_streams; i++) 

			if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){

		// 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;

	// pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

	pCodecCtx = avcodec_alloc_context3(NULL);

	if(pCodecCtx == NULL)

	{

		printf("pCodecCtx err\r\n");

		return -1;

	}

	avcodec_parameters_to_context(pCodecCtx,pFormatCtx->streams[videoindex]->codecpar);

	pCodec=avcodec_find_decoder(pCodecCtx->codec_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();

	pFrameYUV=av_frame_alloc();



	out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  pCodecCtx->width, pCodecCtx->height,1));

	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,

		AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);



	//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, 

		pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); 

	



	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  

		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 

		return -1;

	} 

	//SDL 2.0 Support for multiple windows

	screen_w = pCodecCtx->width;

	screen_h = pCodecCtx->height;

	screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,

		screen_w, screen_h,SDL_WINDOW_OPENGL);



	if(!screen) {  

		printf("SDL: could not create window - exiting:%s\n",SDL_GetError());  

		return -1;

	}

	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  

	//IYUV: Y + U + V  (3 planes)

	//YV12: Y + V + U  (3 planes)

	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);  



	sdlRect.x=0;

	sdlRect.y=0;

	sdlRect.w=screen_w;

	sdlRect.h=screen_h;



	packet=(AVPacket *)av_malloc(sizeof(AVPacket));



	video_tid = SDL_CreateThread(sfp_refresh_thread,NULL,NULL);

	//------------SDL End------------

	//Event Loop

	

	for (;;) {

		//Wait

		SDL_WaitEvent(&event);

		if(event.type==SFM_REFRESH_EVENT){

			while(1){

				if(av_read_frame(pFormatCtx, packet)<0)

					thread_exit=1;



				if(packet->stream_index==videoindex)

					break;

			}

			// ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);

			ret = avcodec_send_packet(pCodecCtx, packet);

			if(ret < 0){

				continue;

			}

			got_picture = avcodec_receive_frame(pCodecCtx, pFrame);

			// if(ret < 0){

			// 	printf("Decode Error.\n");

			// 	return -1;

			// }

			if(got_picture == 0){

				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

				//SDL---------------------------

				SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );  

				SDL_RenderClear( sdlRenderer );  

				//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  

				SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, NULL);  

				SDL_RenderPresent( sdlRenderer );  

				//SDL End-----------------------

			}

			// av_free_packet(packet);

			av_packet_unref(packet);

		}else if(event.type==SDL_KEYDOWN){

			//Pause

			if(event.key.keysym.sym==SDLK_SPACE)

				thread_pause=!thread_pause;

		}else if(event.type==SDL_QUIT){

			thread_exit=1;

		}else if(event.type==SFM_BREAK_EVENT){

			break;

		}



	}

	// av_packet_unref(packet);

	sws_freeContext(img_convert_ctx);



	SDL_Quit();

	//--------------

	av_frame_free(&pFrameYUV);

	av_frame_free(&pFrame);

	avcodec_close(pCodecCtx);

	avformat_close_input(&pFormatCtx);



	return 0;

}



编译:

gcc simplest_ffmpeg_player_su.cpp -g -o simplest_ffmpeg_player_su.out -I /usr/local/include -L /usr/local/lib \
-lSDL2main -lSDL2 -lavformat -lavcodec -lavutil -lswscale -lm -lpthread -lswresample

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值