最簡單的基於FFMPEG+SDL的視訊播放器:拆分-解碼器和播放器
=====================================================
最簡單的基於FFmpeg的視訊播放器系列文章列表:
=====================================================
本文補充記錄《最簡單的基於FFMPEG+SDL的視訊播放器》中的兩個例子:FFmpeg視訊解碼器和SDL畫素資料播放器。這兩個部分是從視訊播放器中拆分出來的兩個例子。FFmpeg視訊解碼器實現了視訊資料到YUV資料的解碼,而SDL畫素資料播放器實現了YUV資料的顯示。簡而言之,原先的FFmpeg+SDL視訊播放器實現了:
視訊資料->YUV->顯示器
FFmpeg視訊解碼器實現了:
視訊資料->YUV
SDL畫素資料播放器實現了:
YUV->顯示器FFmpeg視訊解碼器
原始碼
/** * 最簡單的基於FFmpeg的視訊解碼器 * Simplest FFmpeg Decoder * * 雷霄驊 Lei Xiaohua * [email protected] * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * * 本程式實現了視訊檔案解碼為YUV資料。它使用了libavcodec和 * libavformat。是最簡單的FFmpeg視訊解碼方面的教程。 * 通過學習本例子可以瞭解FFmpeg的解碼流程。 * This software is a simplest decoder based on FFmpeg. * It decodes video to YUV pixel data. * It uses libavcodec and libavformat. * Suitable for beginner of FFmpeg. * */ #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" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libavutil/imgutils.h> #ifdef __cplusplus }; #endif #endif int main(int argc, char* argv[]) { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame,*pFrameYUV; unsigned char *out_buffer; AVPacket *packet; int y_size; int ret, got_picture; struct SwsContext *img_convert_ctx; char filepath[]="Titanic.mkv"; FILE *fp_yuv=fopen("output.yuv","wb+"); 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]->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); 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); packet=(AVPacket *)av_malloc(sizeof(AVPacket)); //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); while(av_read_frame(pFormatCtx, packet)>=0){ if(packet->stream_index==videoindex){ ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if(ret < 0){ printf("Decode Error.\n"); return -1; } if(got_picture){ sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); y_size=pCodecCtx->width*pCodecCtx->height; fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V printf("Succeed to decode 1 frame!\n"); } } av_free_packet(packet); } //flush decoder //FIX: Flush Frames remained in Codec while (1) { ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0) break; if (!got_picture) break; sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); int y_size=pCodecCtx->width*pCodecCtx->height; fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V printf("Flush Decoder: Succeed to decode 1 frame!\n"); } sws_freeContext(img_convert_ctx); fclose(fp_yuv); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; }
執行結果
程式執行後,會解碼下面的視訊檔案。解碼後的YUV420P資料被儲存成了一個檔案。使用YUV播放器設定寬高之後可以檢視YUV內容。
SDL畫素資料播放器
原始碼
/** * 最簡單的SDL2播放視訊的例子(SDL2播放RGB/YUV) * Simplest Video Play SDL2 (SDL2 play RGB/YUV) * * 雷霄驊 Lei Xiaohua * [email protected] * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程式使用SDL2播放RGB/YUV視訊畫素資料。SDL實際上是對底層繪圖 * API(Direct3D,OpenGL)的封裝,使用起來明顯簡單于直接呼叫底層 * API。 * * 函式呼叫步驟如下: * * [初始化] * SDL_Init(): 初始化SDL。 * SDL_CreateWindow(): 建立視窗(Window)。 * SDL_CreateRenderer(): 基於視窗建立渲染器(Render)。 * SDL_CreateTexture(): 建立紋理(Texture)。 * * [迴圈渲染資料] * SDL_UpdateTexture(): 設定紋理的資料。 * SDL_RenderCopy(): 紋理複製給渲染器。 * SDL_RenderPresent(): 顯示。 * * This software plays RGB/YUV raw video data using SDL2. * SDL is a wrapper of low-level API (Direct3D, OpenGL). * Use SDL is much easier than directly call these low-level API. * * The process is shown as follows: * * [Init] * SDL_Init(): Init SDL. * SDL_CreateWindow(): Create a Window. * SDL_CreateRenderer(): Create a Render. * SDL_CreateTexture(): Create a Texture. * * [Loop to Render data] * SDL_UpdateTexture(): Set Texture's data. * SDL_RenderCopy(): Copy Texture to Render. * SDL_RenderPresent(): Show. */ #include <stdio.h> extern "C" { #include "sdl/SDL.h" }; const int bpp=12; int screen_w=500,screen_h=500; const int pixel_w=320,pixel_h=180; unsigned char buffer[pixel_w*pixel_h*bpp/8]; //Refresh Event #define REFRESH_EVENT (SDL_USEREVENT + 1) #define BREAK_EVENT (SDL_USEREVENT + 2) int thread_exit=0; int refresh_video(void *opaque){ thread_exit=0; while (!thread_exit) { SDL_Event event; event.type = REFRESH_EVENT; SDL_PushEvent(&event); SDL_Delay(40); } thread_exit=0; //Break SDL_Event event; event.type = BREAK_EVENT; SDL_PushEvent(&event); return 0; } int main(int argc, char* argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } SDL_Window *screen; //SDL 2.0 Support for multiple windows screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if(!screen) { printf("SDL: could not create window - exiting:%s\n",SDL_GetError()); return -1; } SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0); Uint32 pixformat=0; //IYUV: Y + U + V (3 planes) //YV12: Y + V + U (3 planes) pixformat= SDL_PIXELFORMAT_IYUV; SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h); FILE *fp=NULL; fp=fopen("test_yuv420p_320x180.yuv","rb+"); if(fp==NULL){ printf("cannot open this file\n"); return -1; } SDL_Rect sdlRect; SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL); SDL_Event event; while(1){ //Wait SDL_WaitEvent(&event); if(event.type==REFRESH_EVENT){ if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){ // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp); } SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w); //FIX: If window is resize sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = screen_w; sdlRect.h = screen_h; SDL_RenderClear( sdlRenderer ); SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect); SDL_RenderPresent( sdlRenderer ); }else if(event.type==SDL_WINDOWEVENT){ //If Resize SDL_GetWindowSize(screen,&screen_w,&screen_h); }else if(event.type==SDL_QUIT){ thread_exit=1; }else if(event.type==BREAK_EVENT){ break; } } SDL_Quit(); return 0; }
執行結果
程式執行後,會讀取程式資料夾下的一個YUV420P檔案,內容如下所示。接下來會將YUV內容繪製在彈出的視窗中。
下載
Simplest FFmpeg Player
專案主頁
SourceForge:https://sourceforge.net/projects/simplestffmpegplayer/本程式實現了視訊檔案的解碼和顯示(支援HEVC,H.264,MPEG2等)。
是最簡單的FFmpeg視訊解碼方面的教程。
通過學習本例子可以瞭解FFmpeg的解碼流程。
專案包含6個工程:
simplest_ffmpeg_player:標準版,FFmpeg學習的開始。
simplest_ffmpeg_player_su:SU(SDL Update)版,加入了簡單的SDL的Event。
simplest_ffmpeg_decoder:一個包含了封裝格式處理功能的解碼器。使用了libavcodec和libavformat。
simplest_ffmpeg_decoder_pure:一個純淨的解碼器。只使用libavcodec(沒有使用libavformat)。
simplest_video_play_sdl2:使用SDL2播放YUV的例子。
simplest_ffmpeg_helloworld:輸出FFmpeg類庫的資訊。
相關推薦
最簡單的基於FFMPEG+SDL的視訊播放器:拆分-解碼器和播放器
=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================本文補充記錄《
最簡單的基於FFMPEG+SDL的音訊播放器:拆分-解碼器和播放器
=====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================本文補充記錄《
100行程式碼實現最簡單的基於FFMPEG+SDL的視訊播放器(SDL1.x)
=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介FFMPEG
最簡單的基於FFMPEG+SDL的視訊播放器 ver2 (採用SDL2.0
=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介之前做過一個
最簡單的基於FFMPEG+SDL的音視訊播放器
一、概述 在《最簡單的基於FFMPEG+SDL的音訊播放器》記錄一中,我們實現了音訊的播放。更早前,我們在《最簡單的基於FFMPEG+SDL的視訊播放器》記錄一和二中,實現了視訊的播放。在實現視訊播放的時候,我們設定了一個延遲40ms,否則視訊就會以解碼的速
XCode版【100行程式碼實現最簡單的基於FFMPEG+SDL的視訊播放器】
【來自】 1.新建XCode工程後,發現即使安裝了SDL和FFMPEG也編譯不成功,需要修改各種環境。經過我的不懈努力加百穀啥的...貼個能編譯通過的過程出來。謹記! 2.首先需要編譯好ffmpeg原始碼,然後還需要安裝SDL(ffmpeg直接編譯,SDL我是通過brew安
100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器(SDL1.x)【轉】
工程 全屏 升級版 gin avcodec ive 系列文章 相同 hello 轉自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版權聲明:本文為博主原創文章,未經博主允許不得轉載。
最簡單的基於FFMPEG+SDL的音訊播放器 ver2 (採用SDL2 0)
=====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================簡介之前做過一個
最簡單的基於FFMPEG的視訊編碼器(YUV編碼為H.264)
=====================================================最簡單的基於FFmpeg的視訊編碼器文章列表:=====================================================本文介紹一個最簡單的
《基於 FFmpeg + SDL 的視訊播放器的製作》課程的視訊
這兩天開始帶廣播電視工程大二的暑假小學期的課程設計了。本次小學期課程內容為《基於 FFmpeg + SDL 的視訊播放器的製作》,其中主要講述了視音訊開發的入門知識。由於感覺本課程的內容不但適合本科生,而且也比較適合無視音訊基礎的開發者入門使用,所以在講課的同時也錄製了一部分
最簡單的基於FFmpeg的移動端例子:Android 視訊轉碼器
=====================================================最簡單的基於FFmpeg的移動端例子系列文章列表:=====================================================本文記錄一個安
史上最全的基於ffmpeg+sdl網路攝像頭編解碼播放資料(包含交叉編譯過程,附帶完整原始碼)
原創博文,嚴禁私自轉載,轉載請註明出處!!! 近期,由於工作需要,要在開發板上跑一個攝像頭,攝像頭款式比較老,不支援rtsp格式,所以選擇編譯ffmpeg+sdl實現軟解碼播放攝像頭,特此記錄整個編譯過程(非常之艱辛,發文留念) 在ubuntu上交叉編譯環境的搭建:因為開發板上搭建的程式的執
基於FFmpeg的視訊播放器開發系列教程(三)
本篇開始講解音訊解碼播放,該專案用Qt的音訊類QAudioFormat, QAudioOutput等進行解碼,先講解一些關於音訊的知識。 1.取樣頻率 指每秒鐘取得聲音樣本的次數。取樣的過程就是抽取某點的頻率值,很顯然,在一秒中內抽取的點越多,獲取得頻率資
基於FFmpeg的視訊播放器開發系列教程(二)
本節課程的目的:讀幀解碼顯示視訊 開始進入ffmepg的開發之旅。音視訊的細節知識不統一講解,我在教程中逐點滲透,容我以雷神的話開篇。 &nb
基於FFmpeg的視訊播放器開發系列教程(一)
前言 在各大部落格,論壇,看到很多人對流媒體音視訊的開發感興趣,可是不知道怎麼入門,對音視訊的瞭解也很少,寫程式碼更不用說了,經過一段時間的整理,我準備在csdn上寫一套ffmpeg音視訊播放器的開發教程,希望對想從事ffmpeg音視訊領域的
最簡單的基於FFmpeg的封裝格式處理:視音訊分離器簡化版(demuxer-simple)
=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介打算記錄
最簡單的基於FFmpeg的移動端例子:IOS HelloWorld
=====================================================最簡單的基於FFmpeg的移動端例子系列文章列表:=====================================================本文記錄IOS
Qt實戰--基於FFmpeg的視訊引擎實現類
FFmpeg 搞音視訊開發的基本都會接觸到FFmpeg這個庫,支援幾乎所有的音視訊編解碼格式。相對於上節我們用OpenCV實現的獲取視訊幀,FFmpeg對底層的控制粒度更細,有利於我們後續開發,精準控制編解碼格式,獲取碼流資訊,實現進度調整等; FFmpeg編譯或下載教程網上很多
嵌入式Linux下基於FFmpeg的視訊硬體編解碼
摘要: 對FFmpeg多媒體解決方案中的視訊編解碼流程進行研究。結合對S3C6410處理器視訊硬體編解碼方法的分析,闡述了嵌入式Linux下基於FFmpeg的H.264視訊硬體編解碼在S3C6410處理器上的實現方法,為嵌入式多媒體開發提供參考。 引言 目前,智慧手機、PDA和平板電腦等越來越多的嵌入式裝
最簡單的基於Flash的流媒體示例:RTMP推送和接收(ActionScript)
=====================================================Flash流媒體文章列表:=====================================================本文記錄一些基於Flash的流媒體處理