FFMPEG+SDL 學習第一章

說明一點,我之前是做Java的,在多媒體這塊也是初學者,突然接觸C語言感覺有一定難度,所以,6月份花了一個月時間自學了C語言和C++,粗略的看了ffplay.c的源代碼,感覺邏輯性不如Java那麼強,看起來挺吃力,所以,我也希望各位同仁,在學習ffmpeg之前,沒有C和C++基礎的,需要惡補一下。不管怎麼樣,我相信功夫不負有心人,讓我們共同學習共同進步吧!

在這裏我把我的學習經歷分享給大家,對的地方大家參考參考,不對的地方也希望大家多多指正,謝謝!

寫代碼之前,先請各位朋友去了解一下FFMPEG和SDL的工作原理,我簡單介紹一下,FFMPEG用於視音頻編解碼,SDL用於視音頻播放。

下面簡單寫了FFMPEG+SDL實現的一個視音頻播放器,爲了供初學者學習,還沒做視音頻同步。播放起來有些卡頓,但是,基本播放音視頻的功能是有了。



/**
 * 丁鵬 
 * 
 * 2016-10-18
 */
#include "stdafx.h"
#include 
#include 
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "libswscale/swscale.h"
//SDL
#include "sdl/SDL.h"
#include "sdl/SDL_thread.h"
};

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

//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){ 
	if(audio_len==0)		/*  Only  play  if  we  have  data  left  */ 
			return; 
	len=(len>audio_len?audio_len:len);	/*  Mix  as  much  data  as  possible  */ 

	SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
	audio_pos += len; 
	audio_len -= len; 
} 

int _tmain(int argc, _TCHAR* argv[])
{
	AVFormatContext	*pFormatCtx;
	AVCodecContext	*pCodecCtx, *vCodecCtx;
	AVCodec			*pCodec, *vCodec;

	char url[] = "掀起你的蓋頭來.FLV";

	av_register_all();
	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(av_find_stream_info(pFormatCtx)<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
	int	audioStream = -1, videoStream = -1;
	for (int i = 0; i < pFormatCtx->nb_streams; i++) {
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
			videoStream = i;
		}

		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
			audioStream = i;
		}
	}
		
	if (audioStream == -1 || videoStream == -1){
		printf("Didn't find a audio|video stream.\n");
		return -1;
	}

	vCodecCtx = pFormatCtx->streams[videoStream]->codec;

	// Get a pointer to the codec context for the audio stream
	pCodecCtx = pFormatCtx->streams[audioStream]->codec;

	// Find the decoder for the audio stream
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

	vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
	if(pCodec == NULL) {
		printf("Codec not found.\n");
		return -1;
	}

	// Open 視頻
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
		printf("Could not open codec.\n");
		return -1;
	}

	// Open 音頻
	if (avcodec_open2(vCodecCtx, vCodec, NULL) < 0){
		printf("Could not open codec.\n");
		return -1;
	}

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

	AVFrame	*pFrame = av_frame_alloc(), *pFrameYUV = av_frame_alloc();
	int frame_size = avpicture_get_size(PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height);

	uint8_t *v_buffer = (uint8_t *)av_malloc(frame_size*sizeof(uint8_t));

	avpicture_fill((AVPicture*)pFrameYUV, v_buffer, PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height);

	SwsContext *swsCtx = sws_getCachedContext(NULL, vCodecCtx->width, vCodecCtx->height, vCodecCtx->pix_fmt, 
		vCodecCtx->width, vCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
	if (sws_init_context(swsCtx, NULL, NULL) < 0) {
		printf("轉換器初始化失敗\n");
		exit(0);
	}

	//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_Surface *surface = SDL_SetVideoMode(vCodecCtx->width, vCodecCtx->height, 0, SDL_SWSURFACE);
	SDL_Overlay *bmp = SDL_CreateYUVOverlay(vCodecCtx->width, vCodecCtx->height, SDL_IYUV_OVERLAY, surface);
	SDL_Rect rect;
	rect.x = 0;
	rect.y = 0;
	rect.w = vCodecCtx->width;
	rect.h = vCodecCtx->height;

	//解析音頻
	uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	int audio_length = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);
	
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = pCodecCtx->sample_rate; 
	wanted_spec.format = AUDIO_S16SYS; 
	wanted_spec.channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);// pCodecCtx->channels;
	wanted_spec.silence = 0; 
	wanted_spec.samples = audio_length;
	wanted_spec.callback = fill_audio; 
	wanted_spec.userdata = NULL; 

	if (SDL_OpenAudio(&wanted_spec, NULL) < 0){ 
		printf("can't open audio.\n"); 
		return -1; 
	} 

	int got_frame, got_audio;
	//Swr
	struct SwrContext *swrCtx = swr_alloc();
	swrCtx = swr_alloc_set_opts(swrCtx, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, pCodecCtx->sample_rate,
		pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);

	swr_init(swrCtx);

	while(av_read_frame(pFormatCtx, packet) >= 0){
		if (packet->stream_index == videoStream){
			//Decode  
			avcodec_decode_video2(vCodecCtx, pFrame, &got_frame, packet);
		
			if (got_frame) {
				printf("o");
				sws_scale(swsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, vCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

				SDL_LockYUVOverlay(bmp);
				bmp->pixels[0] = pFrameYUV->data[0];
				bmp->pixels[1] = pFrameYUV->data[1];
				bmp->pixels[2] = pFrameYUV->data[2];

				bmp->pitches[0] = pFrameYUV->linesize[0];
				bmp->pitches[1] = pFrameYUV->linesize[1];
				bmp->pitches[2] = pFrameYUV->linesize[2];
				
				SDL_UnlockYUVOverlay(bmp);
				SDL_DisplayYUVOverlay(bmp, &rect);
				//Delay 40ms  
				SDL_Delay(40);
			}
		}
		else if (packet->stream_index == audioStream){
			//Decode  
			avcodec_decode_audio4(pCodecCtx, pFrame, &got_audio, packet);

			if (got_audio) {
				printf("x");
				swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);

				//FIX:FLAC,MP3,AAC Different number of samples
				if (wanted_spec.samples != pFrame->nb_samples){
					SDL_CloseAudio();
					audio_length = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pFrame->nb_samples, AV_SAMPLE_FMT_S16, 1);
					wanted_spec.samples = pFrame->nb_samples;
					SDL_OpenAudio(&wanted_spec, NULL);
				}
			}

			audio_chunk = (Uint8 *)out_buffer;

			//Audio buffer length  
			audio_len = audio_length;
			audio_pos = audio_chunk;

			//Play
			SDL_PauseAudio(0);
			while (audio_len>0)//Wait until finish  
				SDL_Delay(1);
		}
		av_free_packet(packet);
	}

	swr_free(&swrCtx);
	sws_freeContext(swsCtx);

	//SDL_free(&event);
	SDL_FreeSurface(surface);
	SDL_FreeYUVOverlay(bmp);
	SDL_CloseAudio();//Close SDL
	SDL_Quit();

	av_free(v_buffer);
	av_free(out_buffer);
	// Close the codec
	avcodec_close(pCodecCtx);
	avcodec_close(vCodecCtx);
	// Close the video file
	av_close_input_file(pFormatCtx);

	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章