一個簡單的視訊播放器(基於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.8,在Ubuntu 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.0,在Ubuntu 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.04下SVN伺服器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