1. 程式人生 > >一個簡單的視訊播放器(基於FFMPEG4.0+SDL2.0.8,在Ubuntu 14.04下開發驗證)

一個簡單的視訊播放器(基於FFMPEG4.0+SDL2.0.8,在Ubuntu 14.04下開發驗證)

昨天那個例子,在 Ubuntu 14.04下播放視訊時,有個問題,有播放幾秒後,畫面就變成黑白的了。剛開始懷疑是UV資料丟失,不過在將YUV資料輸出到檔案,用YUV Player Deluxe播放,畫面色彩正常著。

今天在主程式中新起了一個SDL Thread,發現畫面就好了,奇怪的很。問題雖然已經解決,但原因還沒找到。應該是和SDL Window的某些特性相關,視訊解碼和生成YUV是沒有問題的。 

修改點:

1. 新建一個獨立的SDL Thread,每40毫秒給主執行緒傳送一個event;

2. 在主線執行緒主迴圈中,監聽event,收到event後,再做對應動作,包括按鍵退出、暫停、畫面重新整理等。

開發環境同上一篇,Makefile不變,程式碼如下:

/*
 * A simple player with FFMPEG4.0 and SDL2.0.8.
 * Only support video decoder, not support audio and subtitle.
 * Created by [email protected]
 * Reference: https://blog.csdn.net/leixiaohua1020/article/details/38868499
 *
 * Modifications:
 * 20180525: add key event loop
 *   
 */
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL2/SDL.h>
#include <libavutil/imgutils.h>

/* Program will quit if quit is SDL_TRUE */
SDL_bool quit  = SDL_FALSE; 
SDL_bool pause =  SDL_FALSE;

/* SDL Event Type, defined by myself. Loop every 40ms, to notify SDL Window refresh with a new frame */
#define FRAME_REFRESH_EVENT (SDL_USEREVENT+1)
static int frame_refresh_thread(void *arg)
{
	while(!quit) {
		if(!pause) {
			SDL_Event event;
			event.type = FRAME_REFRESH_EVENT;
			SDL_PushEvent(&event);
		}
		SDL_Delay(40);
	}
	return 0;
}

int main(int argc, char *argv[])
{
	/* AVFormatContext contains:
	 * 1. iformat(AVInputFormat) : It's either autodetected or set by user.
	 * 2. oformat(AVOutputFormat): always set by user for output.
	 * 3. streams "array" of AVStreams: describe all elementary streams stored in the file.
	 *    AVStreams are typically referred to using their index in this array. */
	/* We can keep pFormatCtx as NULL. avformat_open_input() will allocate for it. */
	AVFormatContext *pFormatCtx = NULL;

	int             i, videoindex, audioindex, titleindex;
	AVCodecContext  *pCodecCtx;
	AVCodec         *pCodec;
	AVFrame         *pFrame, *pFrameYUV;
	AVStream	    *avStream;
	AVPacket        *packet;
	unsigned char   *out_buffer;
        int             ret;
	struct SwsContext *img_convert_ctx;

	char filepath[256] = {0};
	/* SDL */
	int screen_w, screen_h = 0;
	SDL_Window   *screen;
	SDL_Renderer *sdlRenderer;
	SDL_Texture  *sdlTexture;
	SDL_Rect     sdlRect;
	SDL_Event    event;
	SDL_Thread   *refresh_thread;

	/* Parse input parameter. The right usage: ./myplayer xxx.mp4 */
	if(argc < 2 || argc > 2) {
		printf("Too few or too many parameters. e.g. ./myplayer xxx.mp4\n");
		return -1;
	}
	if(strlen(argv[1]) >= sizeof(filepath)) {
		printf("Video path is too long, %d bytes. It should be less than %d bytes.\n",
			strlen(argv[1]), sizeof(filepath));
		return -1;
	}
	strncpy(filepath, argv[1], sizeof(filepath));

	/* Open the specified file(autodetecting the format) and read the header, exporting the information
	 * stored there into AVFormatContext. The codecs are not opened. 
	 * The stream must be closed with avformat_close_input(). */
	ret = avformat_open_input(&pFormatCtx, filepath, NULL, NULL);
	if(ret != 0) {
		printf("[error]avformat_open_input: %s\n", av_err2str(ret));
		return -1;
	}

	/* Some formats do not have a header or do not store enough information there, so it it recommended 
         * that you call the avformat_find_stream_info() which tries to read and decode a few frames to find 
         * missing information. 
         * This is useful for file formats with no headers such as MPEG. */
	ret = avformat_find_stream_info(pFormatCtx, NULL);
	if(ret < 0) {
		printf("[error]avformat_find_stream_info: %s\n", av_err2str(ret));
		
		avformat_close_input(&pFormatCtx);
		return -1;
	}

	/* Find out which stream is video, audio, and subtitle. */
	videoindex = audioindex = titleindex = -1;
	for(i = 0; i < pFormatCtx->nb_streams; i++) {
		avStream = pFormatCtx->streams[i];
		switch(avStream->codecpar->codec_type) {
			case AVMEDIA_TYPE_VIDEO:
				videoindex = i;
				break;
			case AVMEDIA_TYPE_AUDIO:
				audioindex = i;
				break;
			case AVMEDIA_TYPE_SUBTITLE:
				titleindex = i;
				break;
		}
	}
	if(videoindex == -1) {
		printf("Didn't find a video stream.\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}

	/* Open video codec */
	pCodecCtx = avcodec_alloc_context3(NULL);   /* It should be freed with avcodec_free_context() */
	if(!pCodecCtx) {
		printf("[error]avcodec_alloc_context3() fail\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	ret = avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
	if(ret < 0) {
		printf("[error]avcodec_parameters_to_context: %s\n", av_err2str(ret));
		avcodec_free_context(&pCodecCtx);
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if(pCodec == NULL) {
		printf("Video Codec not found.\n");
		avcodec_free_context(&pCodecCtx);
		avformat_close_input(&pFormatCtx);		
		return -1;
	}
	ret = avcodec_open2(pCodecCtx, pCodec, NULL);
	if(ret < 0) {
		printf("[error]avcodec_open2: %s\n", av_err2str(ret));
		avcodec_free_context(&pCodecCtx);
		avformat_close_input(&pFormatCtx);		
		return -1;
	}

	/* Output info */
	printf("-------------File Information-------------\n");
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("------------------------------------------\n");

	/* SDL */
	pFrame    = av_frame_alloc();   /* av_frame_free(&pFrame); */
	pFrameYUV = av_frame_alloc();
	/* av_image_get_buffer_size() returns the size in bytes of the amount of data required to store an image with the given parameters. */
	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);
	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) < 0) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());		

		av_free(out_buffer);
		av_frame_free(&pFrame);	
		av_frame_free(&pFrameYUV);
		avcodec_free_context(&pCodecCtx);
		avformat_close_input(&pFormatCtx);		
		return -1;	
	}

	screen_w = pCodecCtx->width;
	screen_h = pCodecCtx->height;
	screen   = SDL_CreateWindow("Simplest ffmpleg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
				screen_w, screen_h, SDL_WINDOW_RESIZABLE);  //SDL_WINDOW_OPENGL
	if(!screen) {
		printf("SDL: could not create window - %s\n", SDL_GetError());

		av_free(out_buffer);
		av_frame_free(&pFrame);	
		av_frame_free(&pFrameYUV);
		avcodec_free_context(&pCodecCtx);
		avformat_close_input(&pFormatCtx);		
		return -1;	
	}
	
	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	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;
	/* SDL End */

	/* For decoding, call avcodec_send_packet() to give the decoder raw compressed data in an AVPacket. 
         *		 call avcodec_receive_frame() in a loop until AVERROR_EOF is returned. 
	 * 		 On success, it will return an AVFrame containing uncompressed audio or video data. */
	packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(packet);
	
	refresh_thread = SDL_CreateThread(frame_refresh_thread, NULL, NULL);
	while(!quit) {	
		if(SDL_WaitEvent(&event) != 1)
			continue;

		switch(event.type) {
		case SDL_KEYDOWN:  
			if(event.key.keysym.sym == SDLK_ESCAPE)  
				quit = SDL_TRUE;
			if(event.key.keysym.sym == SDLK_SPACE)  
				pause = !pause;			
			break;

		case SDL_QUIT:    /* Window is closed */
			quit = SDL_TRUE;
			break;

		case FRAME_REFRESH_EVENT:  /* refresh window with a new frame */
			if(av_read_frame(pFormatCtx, packet) < 0) {  /* No more packets */
				quit = SDL_TRUE;
				break;
			}
			// Only handle video packet
			if(packet->stream_index != videoindex) {
				av_packet_unref(packet);
				av_init_packet(packet);
				break;		
			}
	
			/* @return 0 on success
			 *    AVERROR(EAGAIN): input is not accepted in the current state - user must read output with avcodec_receive_frame()
			 *        AVERROR_EOF: the decoder has been flushed, and no new packets can be sent to it 
			 *    AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush 
			 *    AVERROR(ENOMEM): failed to add packet to internal queue, or similar */
			ret = avcodec_send_packet(pCodecCtx, packet);
			if( ret != 0 ) {
				av_packet_unref(packet);
				av_init_packet(packet);
				break;
			}
	
			/* Got frame */
			do {
				/* @return 0 on success, a frame was returned
				 *     AVERROR(EAGAIN): output is not available in this state - user must try to send new input
				 *         AVERROR_EOF: the decoder has been fully flushed, and there will be no more output frames
				 *     AVERROR(EINVAL): codec not opened, or it is an encoder
				 *     other negative values: legitimate decoding errors */
				ret = avcodec_receive_frame(pCodecCtx, pFrame);
				if(ret < 0) 
					break;
				else if(ret == 0) {  /* Got a frame successfully */
					sws_scale(img_convert_ctx, (const unsigned char * const *)pFrame->data, pFrame->linesize, 0, 
						pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
					/* SDL */
					SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
						pFrameYUV->data[0], pFrameYUV->linesize[0],
						pFrameYUV->data[1], pFrameYUV->linesize[1], 
						pFrameYUV->data[2], pFrameYUV->linesize[2]);
					SDL_RenderClear(sdlRenderer);
					SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
					SDL_RenderPresent(sdlRenderer);
					/* SDL End */
				} else if(ret == AVERROR_EOF) {
					avcodec_flush_buffers(pCodecCtx);
					break;
				}
			} while(ret != AVERROR(EAGAIN));  

			av_packet_unref(packet);
			av_init_packet(packet);

			break;
		} /* End of switch */
	} /* End of while(!quit) */

	sws_freeContext(img_convert_ctx);
	SDL_DestroyRenderer(sdlRenderer);
	SDL_Quit();
	
	av_free(packet);
	av_free(out_buffer);
	av_frame_free(&pFrame);	
	av_frame_free(&pFrameYUV);
	avcodec_close(pCodecCtx);
	avcodec_free_context(&pCodecCtx);
	avformat_close_input(&pFormatCtx);

	return 0;
}

相關推薦

一個簡單視訊播放(基於FFMPEG4.0+SDL2.0.8Ubuntu 14.04開發驗證)

昨天那個例子,在 Ubuntu 14.04下播放視訊時,有個問題,有播放幾秒後,畫面就變成黑白的了。剛開始懷疑是UV資料丟失,不過在將YUV資料輸出到檔案,用YUV Player Deluxe播放,畫面色彩正常著。今天在主程式中新起了一個SDL Thread,發現畫面就好了,

簡單基於FFMPEG+SDL的視訊播放 ver2 (採用SDL2.0

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介之前做過一個

簡單基於FFMPEG+SDL的音訊播放 ver2 (採用SDL2 0

                =====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================簡介之前做過一個

ffmpeg+sdl教程----編寫一個簡單播放5(同步視訊到音訊)

    個人認為,這這部分教程的新增程式碼量雖然不是最多的,難度卻是最大的,重複看了多次才明白,因為有兩個問題的困擾,搞得還不清楚: 1.音訊和視訊既然都有各自的時間戳,各自按各自的時間戳來播放不就行了,為什麼還需要同步呢? 2.如果要把視訊同步到音訊,怎麼同步?或者說以什麼

基於MFC的OpenCV簡單視訊播放

1.程式功能簡介 我們點選OpenFile按鈕,選取路徑,然後點選Ok,顯示或播放所選的圖片或視訊。 2.功能實現 2.1 Dlg類標頭檔案中新增以下項: public: CString FileName;//記錄所選檔案路徑CRect rect;//關聯影象控制元

[SimplePlayer] 實現一個簡單播放

false rip 音頻 class sam 文件中 多線程處理 設備 pos 簡單的播放器需要實現一個最基本的功能:播放視頻文件。 實現這個功能需要包含以下幾個步驟: 從視頻文件中提取視頻圖像 在屏幕上顯示視頻圖像 視頻幀的同步,也就是保證視頻圖像在合適的時間在屏幕

樹梅派應用23:QT+樹莓派實現一個簡單播放

說起樹莓派,買了也有一段時間了,但是始終都沒有做出什麼好玩的裝置出來,恰好最近在學C++,看到樹莓派放在牆角吃了一年多灰,為何不利用它來學一下程式設計呢? 先給我的工作臺來個特寫: 說幹就幹,在經歷了一番折騰以後,先準備所需的器材和必要的零部件,在這裡我簡單的羅列一下:先是

Android簡單視訊播放之VideoView(一)

早上起來有時間,發一篇博文,最近在開發電視機頂盒的視訊播放,涉及到Android當中比較常見的視訊播放器控制元件的使用,以此為例,記錄下來。 首先,上效果圖: 通過VideoView播放視訊的步驟: 實現方式:使用XML佈局和java程式碼控制元

一個純粹的視訊解碼程式(基於FFMPEG 4.0Ubuntu 14.04驗證)

程式功能:將指定的視訊檔案,解碼為原始YUV資料,只包含視訊流。開發環境:Ubuntu 14.04, GCC 4.8.4, FFMPEG 4.0編譯方法: 將程式碼copy命名為SimpleDecoder.c,與Makefile放置於同一目錄下,執行 make 即可。執行方法

java簡單視訊播放筆記

匯入庫檔案的方法: Native.loadLibrary("E:\\VideoLAN\\VLC\\libvlc.dll",LibVlc.class); //連結外部庫 美化swing控制元件: UIManager.setLookAndFeel("com.sun.java.

Android 超簡單音樂播放(九)搜尋網路歌曲獲得熱門榜單(GridView)(易源api的使用)(JSON的解析)(重新整理)

首先感謝易源API! 提供了QQ音樂的介面~ 咳咳... 明天要寫文件..看來..哎..我的歌詞要等假期了? 迴歸正題~ 首先上一下我的介面啊實現啊啥的~ 點選榜單會出現對應的歌曲~ 可以搜尋網路歌曲~ 可以重新整理~ 先寫榜單的實現~ 首先是net這個Frag

Ubuntu 14.04SVN伺服器server的搭建和客戶端的簡單操作

參考:Ubuntu 14.04下搭建SVN伺服器(SVN Server) 一:搭建環境 檢視Ubuntu系統的版本資訊:可用cat /proc/version命令、uname -a命令與sb_release -a命令。 Ubuntu:14.04 64位

ubuntu 14.04 單機安裝 hadoop 2.7.2+scala 2.11.8+spark 2.0偽分散式教程

一.安裝java 將java下載後手動解壓到/home/che資料夾下(可採用右擊壓縮包,單擊“提取”) 在終端(可用Ctrl+Alt+T快捷鍵開啟)中輸入: sudo gedit /etc/profile 在開啟的文字中新增: expor

Installing CUDA 8.0 + cuDNN 5.1 + TensorFlow with Ubuntu 14.04 ()

這篇文章因為篇幅原因分為上下兩篇,此為下篇,上篇連結為: Step 2. Install NVIDIA cuDNN Once the CUDA Toolkit is installed, download cuDNN v5.1 Library(或者從百度雲下載) for

Ubuntu 14.04搭建Python3.4 + PyQt5.3.2 + Eric6.0開發平臺

引言 找了很多Python GUI工具集,還是覺得PyQt比較理想,功能強大跨平臺,還支援介面設計器。花一天時間折騰了Ubuntu14.04(32位)+ Python3.4 + Qt5.3.2 + PyQt5.3.2 + Eric6.0 的完整開發平臺的搭建,各種出錯差點放

第二十四篇-用VideoView製作一個簡單視訊播放

這是一個播放本地視訊的播放器,videoUrl1是手機裡放置視訊的路徑 效果圖: MainActivity.java package com.example.aimee.videotest; import android.Manifest; import android.co

android平臺基於ffmpeg和ANativeWindow實現簡單視訊播放

音視訊實踐學習 android全平臺編譯ffmpeg以及x264與fdk-aac實踐 ubuntu下使用nginx和nginx-rtmp-module配置直播推流伺服器 android全平臺編譯ffmpeg合併為單個庫實踐 android-studio使用c

從零開始仿寫一個抖音App——基於FFmpeg的極簡視訊播放

本文發於掘金——何時夕,搬運轉載請註明出處,否則將追究版權責任。交流qq群:859640274 GitHub地址 好久不見,最近加班比較多所以第二篇音視訊方面的文章 delay 了一週,大家多包涵哈。本文預計閱讀時間二十分鐘。 本文分為以下章節,讀者可以按需閱讀 1.FFmpeg原始碼

100行程式碼實現最簡單基於FFMPEG+SDL的視訊播放(SDL1.x)

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介FFMPEG

Android基礎學習總結(十六)——基於ijkplayer封裝支援簡單介面UI定製的視訊播放

前言 專案開發中遇到需要解析播放m3u8視訊流的情況,但是原生的PlayerView非常慢,使用起來複雜,不適合上手,這裡找到一款ijkplayer是Bilibili基於ffmpeg開發並開源的輕量級視訊播放器,支援播放本地網路視訊,也支援流媒體播放。支援An