1. 程式人生 > >SDL2播放FFmpeg解壓的視頻

SDL2播放FFmpeg解壓的視頻

open dex n) end pac ios 文件中 h.264 second

SDL2簡化了播放過程,這裏引入播放視頻。

1. 以我的《FFmpeg入門測試》為工程。

2. 到http://www.libsdl.org/index.php下載SDL2-devel-2.0.9-VC.zip (Visual C++ 32/64-bit)最新版。解壓將SDL2-2.0.9\lib\x86目錄內的SDL2.dll考入解決方案的Debug目錄中。在解決方案目錄中新建SDL目錄,拷入SDL2-2.0.9內的include和lib兩個目錄。

3. 創建開發環境:

3.1 包含編譯文件

3.1.1 包含頭目錄:項目->屬性->VC++目錄->包含目錄:F:\FFmpegTest\SDL\include。

3.1.2 包含庫目錄:項目->屬性->VC++目錄->庫目錄:F:\FFmpegTest\SDL\lib\x86。

3.1.3 包含鏈接庫文件:項目->屬性->鏈接器->輸入->附加依賴項:SDL2.lib;SDL2main.lib。

4. 輸入源代碼:

#include <iostream>

#define __STDC_CONSTANT_MACROS //使用FFmpeg定義宏

extern "C"
{
    #include "libavutil/imgutils.h"   //av_image使用
    #include "libavcodec/avcodec.h"   //編解碼處理
    #include "libavformat/avformat.h" //文件封裝處理
    #include "libswscale/swscale.h"//圖像變換
    #include "libavdevice/avdevice.h"
    #include "SDL.h"
};

void PrintErrStr(const char *str)
{
	printf(str);
	printf("\n\n");
	system("pause");
}

int Decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt)//解碼
{
	 int ret = avcodec_send_packet(dec_ctx, pkt);// 先發送包數據到解碼上下文中
	 if (ret < 0)
	 {
		 PrintErrStr("發送數據包進行解碼時出錯.");
		 return ret;
	 }
	 return avcodec_receive_frame(dec_ctx, frame);// 然後從解碼上下文中讀取幀數據到frame對象中
}

#undef main
int main()
{
	AVFormatContext *pFormatCtx = NULL;//主要用於處理封裝格式(FLV/MKV/RMVB等)
	AVCodecContext *pVideoCodecCtx = NULL;
	AVCodecContext *pAudioCodecCtx = NULL;
	AVCodec *pVideoCodec = NULL;
	AVCodec *pAudioCodec = NULL;
	AVFrame *pFrame;   //存儲非壓縮的數據(視頻對應RGB/YUV像素數據,音頻對應PCM采樣數據)
	AVPacket packet;   //存儲壓縮數據(視頻對應H.264等碼流數據,音頻對應AAC/MP3等碼流數據)

	//SDL變量
	SDL_Window *screen;
	SDL_Renderer* sdlRenderer;
	SDL_Texture* sdlTexture;
	SDL_Rect sdlRect;

	int iHour, iMinute, iSecond, iTotalSeconds;//HH:MM:SS
	int videoStreamIndex, audioStreamIndex; //音視頻流索引
	int ret, FrameBytes;
	const char In_FileName[] = "F:\\TestMov\\Titanic.ts";//輸入文件
	const char Out_h264_FileName[] = "F:\\TestMov\\output.h264";//輸出h264文件
	const char Out_rgb24_FileName[] = "F:\\TestMov\\output.rgb24";//輸出h264文件

	FILE *fp_Out_h264_File = fopen(Out_h264_FileName, "wb+");
	FILE *fp_Out_rgb24_File = fopen(Out_rgb24_FileName, "wb+");

	int OutImgW = 640;
	int OutImgH = 272;
	int Img_linesize = OutImgW * 3;
	int OutImg_linesize[3] = {Img_linesize, 0, 0};

	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = OutImgW;
	sdlRect.h = OutImgH;

	avdevice_register_all();//註冊所有組件
	if (avformat_open_input(&pFormatCtx, In_FileName, NULL, NULL) != 0)//打開輸入視頻文件
	{
		PrintErrStr("打開輸入文件失敗。");
		return -1;
	}

	if (avformat_find_stream_info(pFormatCtx, NULL) < 0)//獲取文件信息
	{
		PrintErrStr("沒有發現流信息。");
		return -1;
	}

	videoStreamIndex = -1;
	audioStreamIndex = -1;
	for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)//尋找流索引
	{
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)//查找視頻流索引
		{
			videoStreamIndex = i;
		}
		else
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)//查找音頻流索引
		{
			audioStreamIndex = i;
		}
	}
	if ((videoStreamIndex == -1)||(audioStreamIndex == -1))
	{
		PrintErrStr("沒有發現視頻或音頻流。");
		return -1;
	}

	pVideoCodec = avcodec_find_decoder(pFormatCtx->streams[videoStreamIndex]->codecpar->codec_id);//查找視頻解碼器
	if (pVideoCodec == NULL)
	{
		PrintErrStr("沒有找到視頻解碼器。");
		return -1;
	}
	pAudioCodec = avcodec_find_decoder(pFormatCtx->streams[audioStreamIndex]->codecpar->codec_id);//查找音頻解碼器
	if (pVideoCodec == NULL)
	{
		PrintErrStr("沒有找到音頻解碼器。");
		return -1;
	}

	pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);//申請視頻解碼空間
	if (pVideoCodecCtx == NULL)
	{
		PrintErrStr("無法分配視頻解碼器。");
		return -1;
	}
	pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);//申請音頻解碼空間
	if (pAudioCodecCtx == NULL)
	{
		PrintErrStr("無法分配音頻解碼器。");
		return -1;
	}
	avcodec_parameters_to_context(pVideoCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
	avcodec_parameters_to_context(pAudioCodecCtx, pFormatCtx->streams[audioStreamIndex]->codecpar);
	 
	if (avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0) //打開視頻解碼器
	{  
		PrintErrStr("沒有打開視頻解碼器。");
		return -1;
	}
	if (avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0) //打開音頻解碼器
	{
		PrintErrStr("沒有打開音頻解碼器。");
		return -1;
	}

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))//SDL2初始化視頻 
	{
		printf("無法初始化SDL2:%s\n", SDL_GetError());
		printf("\n\n");
		system("pause");
		return -1;
	}
	screen = SDL_CreateWindow("SDL2 + ffmpeg player‘s Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, OutImgW, OutImgH, SDL_WINDOW_SHOWN);
	if (!screen) 
	{
		printf("創建SDL2窗口失敗:%s\n", SDL_GetError());
		printf("\n\n");
		system("pause");
		return -1;
	}
	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);//SDL2創建渲染
	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, OutImgW, OutImgH);//SDL2創建紋理

	pFrame = av_frame_alloc();//申請解壓幀緩存
	if (pFrame == NULL)
	{
		PrintErrStr("申請解壓幀緩存失敗。");
		return -1;
	}

	FrameBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height, 1);//獲取視頻幀字節數
	uint8_t *pFrameBuffer = (uint8_t *)av_malloc(FrameBytes);//創建動態視頻幀數組

	while (av_read_frame(pFormatCtx, &packet) >= 0)//從文件中讀取一個packet 
	{
		if (packet.stream_index == videoStreamIndex)//如果是視頻幀 
		{
			//fwrite(packet.data, packet.size, 1, fp_Out_h264_File);//寫視頻包文件
			ret = Decode(pVideoCodecCtx, pFrame, &packet);//解碼 packet->pFrame
			if (ret == 0) //已得到解碼的圖像在pFrame裏
			{
				struct SwsContext *pSwsCtx = NULL;
				pSwsCtx = sws_getContext( pVideoCodecCtx->width, pVideoCodecCtx->height, pVideoCodecCtx->pix_fmt,//源寬度高度和格式YUV420 
					                      OutImgW, OutImgH, AV_PIX_FMT_RGB24,//轉換到目標的寬度高度和格式
					                      SWS_FAST_BILINEAR,//縮放算法
					                      NULL, NULL, NULL);//初始化轉換
				if (!pSwsCtx)
				{
					PrintErrStr("無法初始化轉換Frame幀。");
					return -1;
				} 
				sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pVideoCodecCtx->height, &pFrameBuffer, OutImg_linesize);
				
				//SDL_Update
				SDL_UpdateTexture(sdlTexture, NULL, pFrameBuffer, Img_linesize);//刷新顯示
				SDL_RenderClear(sdlRenderer);//清除上次渲染
				SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);//拷貝紋理
				SDL_RenderPresent(sdlRenderer);//渲染
				SDL_Delay(40);//延時

				//fwrite(pFrameBuffer, FrameBytes, 1, fp_Out_rgb24_File);//寫RGB視頻文件
				sws_freeContext(pSwsCtx);//釋放轉換
			}
		}
		av_packet_unref(&packet);
	}

	//輸出信息
	puts("[文件信息]");
	iTotalSeconds = (int)pFormatCtx->duration / 1000000; //S
	iHour = iTotalSeconds / 3600;//小時
	iMinute = iTotalSeconds % 3600 / 60;//分鐘
	iSecond = iTotalSeconds % 60;//秒
	printf("播放時間:%02d:%02d:%02d\n", iHour, iMinute, iSecond);
	printf("流 個 數:%d\n", pFormatCtx->nb_streams);
	printf("封裝格式:%s\n", pFormatCtx->iformat->long_name);
	printf("\n");

	puts("[視頻信息]");
	printf("編碼格式:%s\n", pVideoCodec->long_name);
	printf("視頻碼率:%I64d kb/s\n", pVideoCodecCtx->bit_rate / 1000);
	printf("分 辨 率:%d * %d\n", pVideoCodecCtx->width, pVideoCodecCtx->height);
	printf("\n");

	puts("[音頻信息]");
	printf("編碼格式:%s\n", pAudioCodec->long_name);
	printf("音頻碼率:%I64d kb/s\n", pAudioCodecCtx->bit_rate / 1000);
	printf("通 道 數:%d\n", pAudioCodecCtx->channels);
	printf("采 樣 率:%d\n", pAudioCodecCtx->sample_rate);
	printf("\n");

	SDL_Quit();
	fclose(fp_Out_h264_File);
	fclose(fp_Out_rgb24_File);
	av_free(pFrameBuffer);
	av_frame_free(&pFrame);
	avcodec_close(pVideoCodecCtx);
	avcodec_close(pAudioCodecCtx);
	avformat_close_input(&pFormatCtx);

	printf("\n\n");
	system("pause");
}

5. 編譯運行:輸出一個窗口播放視頻,輸出文件已無意義,被註釋掉。

因SDL內也有一個main,所以在主程序main前輸入#undef main,避免錯誤。

SDL2播放FFmpeg解壓的視頻