ffmpegプレイヤーは、詳細なビデオ表示を実現します(おすすめ)


FFmpegはデジタルオーディオ、ビデオを記録、変換するために使用できるセットであり、それをストリームに変換するオープンソースコンピュータプログラムです。これは、現在リードされているサウンド/ビデオコードのライブラリlibavcodecを含んでいます。FFmpegはLinux下開発で作られたものですが、Windowsを含むほとんどのオペレーティングシステムでコンパイルできます。このプロジェクトはFabrice Bellardによって開始されました。今はMichael Niedier mayerによって運営されています。複数のビデオフォーマット間の相互変換は、例えば、録画されたビデオaviなどを現在のビデオサイトで採用されているflivフォーマットに簡単に実現することができる。
fflayはffmpegのソースコードの中で、ソースプレイヤーの実例を持っています。ローカルビデオファイルの再生とオンラインストリーミングメディアの再生をサポートしています。機能は非常に強いです。
FFplay:FFplay is a very simple and portble media player using the FFmpeg libries and the SDL library.It is mostly used as a testbed for the various FFmpeg API.
flayのコードは、ffmpegの関数ライブラリを十分に呼び出していますので、ffmpegの使用を学びたいです。あるいは、ffmpegに基づいて自分のプレイヤーを開発したいです。
ffmpeg自体の開発文書が少ないため、fflayプレイヤーのソースコードの実現は比較的複雑で、基本的なffmpegコンポーネントの呼び出し以外に、ビデオフレームのレンダリング、オーディオフレームの再生、音声ビデオ同期ポリシーおよびスレッドスケジュールなどの問題が含まれています。
したがって、ここでは、ffmpeg公式サイトで紹介されているフイプレイヤの簡略化されたバージョンの開発ルーチンをベースに、この基礎の上で、段階的にゆっくりと深化し、最終的にはビデオプレーヤの完全な論理を実現することを検討します。
前の記事ではffmpegベースのプレーヤーフレームを構築すればいいと紹介しました。
本論文では、上記の記事をもとに、ffmpegを復号したビデオフレームをレンダリング表示する方法について議論を続けている。
1、ビデオフレームレンダリング
上記の記事では、どのようにしてffmpegに基づいてビデオプレイヤーのフレームを構築するかを紹介しました。プログラムを実行すると、いくつかの画像を生成する以外に、プログラムは何もできないようです。
これは、ffmpegがパッケージのapiとコンポーネントを通じて異なるビデオパッケージのフォーマットと符号化フォーマットの違いをブロックしてくれて、開発者に統一したappiインターフェースを提供しています。開発者は各符号化方式とパッケージ方式の具体的な技術詳細を知る必要がなく、ffmpegから提供されたapiを呼び出すだけで、パッケージの解凍と復号の操作ができます。
動画フレームのレンダリングおよびオーディオフレームの再生には、ffmpegは無力であるため、sdlライブラリのような他の第三者構成要素を介して行われる必要がある。
ここではsdlライブラリを使ってビデオフレームのレンダリングを行う方法を説明します。sdlは下の階にopenglグラフィックスライブラリをカプセル化しています。sdlが提供するアプリはopenglの描画操作を簡略化しています。
sdlライブラリのコンパイルのインストールについては、「公衆番号:ブレークポイント実験室」の前の記事「ffmpegプレーヤーが詳細解-フレーム構築を実現する」を参照してください。
1.1レンダリング環境構築
ビデオフレームの表示前には、ビデオを表示するためのウィンドウオブジェクトと、ウィンドウに取り付けられたキャンバスオブジェクトを用意する必要があります。
SDLウィンドウを作成し、画像サイズとピクセル数を指定します。

//   SDL  ,       
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
キャンバスオブジェクトを作成

//       
bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);
1.2ビデオフレームレンダリング
ウィンドウとキャンバスオブジェクトの作成が完了すると、ビデオフレームの描画表示が開始されます。
キャンバスオブジェクトを操作する前に、他のスレッドがキャンバスの中のコンテンツに対して競合的なアクセスを避けるために、そのプログラムを保護する必要があります。スレッドの操作に慣れていない学生は、マルチスレッド環境において、複数のスレッドが臨界エリアのリソースに対して競合するアクセスをスレッドと同期して操作することを知ることができる。
SDL_LockYUVOverlay(bmp)//locks the overlay for direct access to pixel data
キャンバスに復号後のビデオフレームを注入する。

sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pict.data, pict.linesize);
キャンバスオブジェクトのビデオフレーム充填動作が完了したら、sdlスレッドロックを解除します。

//Unlocks a previously locked overlay. An overlay must be unlocked before it can be displayed
SDL_UnlockYUVOverlay(bmp);
ビデオフレームのレンダリング

SDL_DisplayYUVOverlay(bmp, &rect);//    
sdlパッケージのapiの描画インターフェースによって、ビデオフレームのレンダリングはまだ非常に容易であるため、直接openglを採用すれば、描画プロセスは比較的複雑になります。ルーチンの主な目的は、ffmpegの使用を紹介するためです。したがって、ここでsdlを採用してレンダリングプロセスを簡略化しました。
1.3プロジェクトのソースコードのコンパイル
このルーチンは前の文章で使ったコンパイル方法と全く同じです。

tutorial02: tutorial02.c
	gcc -o tutorial02 -g3 tutorial02.c -I${FFMPEG_INCLUDE} -I${SDL_INCLUDE} \
	-L${FFMPEG_LIB} -lavutil -lavformat -lavcodec -lswscale -lswresample -lz -lm \
	`sdl-config --cflags --libs`

clean:
	rm -rf tutorial02
make命令を実行してコンパイルを開始し、コンパイルが完了したら、ソースディレクトリで[tutorial 02]という実行可能ファイルを生成することができます。
現在実行可能なファイルのすべての依存ダイナミックライブラリは、lddコマンドで照会できます。
1.4検証
[tutorial 02 url]コマンドを実行すると、画面出力が表示されます。

./tutorial02 rtmp://58.200.131.2:1935/livetv/hunantv

画面はすでにありますが、音声が足りないので、次の文章は引き続きプレイヤーの開発を改善し、どのように音声を再生するかを検討します。
2、ビデオ再生中に発生する可能性のある問題
動画再生中に以下の2つの問題が発生する可能性があります。
sdlオーディオデバイスSDL_が見つかりません。OpenAudioのsuch audio device
sdl Could not initialize SDLを初期化できません。no available video device
解決方法は「公衆番号:断点実験室」の前の文章「fflaソースコンパイル」を参照してください。
3、ソースリスト
ソースは非常に簡単です。上の内容だけに基づいて、sdlのレンダリング環境の構築を追加しました。ソース全体はまだmainのメインスレッドで実行されています。後の内容は複数のスレッドのスケジュールと同期のシーンに関連します。

// tutorial02.c
// A pedagogical video player that will stream through every video frame as fast as it can.
//
// This tutorial was written by Stephen Dranger ([email protected]).
//
// Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, 
// and a tutorial by Martin Bohme ([email protected])
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
//
// Updates tested on:
// Mac OS X 10.11.6
// Apple LLVM version 8.0.0 (clang-800.0.38)
//
// Use 
//
// $ gcc -o tutorial02 tutorial02.c -lavutil -lavformat -lavcodec -lswscale -lz -lm `sdl-config --cflags --libs`
//
// to build (assuming libavutil/libavformat/libavcodec/libswscale are correctly installed your system).
//
// Run using
//
// $ tutorial02 myvideofile.mpg
//
// to play the video stream on your screen.


#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <SDL.h>
#include <SDL_thread.h>

#ifdef __MINGW32__
#undef main // Prevents SDL from overriding main().
#endif

#include <stdio.h>

int main(int argc, char *argv[]) {
/*--------------    -------------*/
	AVFormatContext *pFormatCtx = NULL;//                   
	AVCodecContext *pCodecCtx = NULL;//        ,          、  、            
	AVCodec *pCodec = NULL;//            ,            ,                   
	AVPacket packet;//                  ,         packet   
	AVFrame *pFrame = NULL;//           ,     、      、     ,QP ,        
	struct SwsContext *sws_ctx = NULL;//           
	AVDictionary *optionsDict = NULL;
 
 	SDL_Surface *screen = NULL;//SDL    ,A structure that contains a collection of pixels used in software blitting
 	SDL_Overlay *bmp = NULL;//SDL  
 	SDL_Rect rect;//SDL    
 	SDL_Event event;//SDL    

 	int i, videoStream;//    ,       
 	int frameFinished;//          
 
/*-------------     ------------*/
	if (argc<2) {//            
		fprintf(stderr, "Usage: test <file>
"); exit(1); } // Register all available formats and codecs, ffmpeg av_register_all(); /*----------------------- * Open video file, , , pFormatCtx * read the file header and stores information about the file format in the AVFormatContext structure * The last three arguments are used to specify the file format, buffer size, and format options * but by setting this to NULL or 0, libavformat will auto-detect these -----------------------*/ if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) { return -1; // Couldn't open file. } /*----------------------- * , pFormatCtx->stream * check out & Retrieve the stream information in the file * then populate pFormatCtx->stream with the proper information * pFormatCtx->streams is just an array of pointers, of size pFormatCtx->nb_streams -----------------------*/ if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { return -1; // Couldn't find stream information. } // Dump information about file onto standard error, pFormatCtx av_dump_format(pFormatCtx, 0, argv[1], 0); // Find the first video stream. videoStream = -1;// -1 for(i = 0; i < pFormatCtx->nb_streams; i++) {// ( 、 、 ) if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {// videoStream = i;// , -1 break; } } if (videoStream == -1) {// return -1; // Didn't find a video stream. } // Get a pointer to the codec context for the video stream, pFormatCtx->streams pCodecCtx = pFormatCtx->streams[videoStream]->codec; /*----------------------- * Find the decoder for the video stream, , ( ) * The stream's information about the codec is in what we call the "codec context. * This contains all the information about the codec that the stream is using -----------------------*/ pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) {// fprintf(stderr, "Unsupported codec!
"); return -1; // Codec not found. } // Open codec, if (avcodec_open2(pCodecCtx, pCodec, &optionsDict) < 0) { return -1; // Could not open codec. } // Allocate video frame, ( ) pFrame = av_frame_alloc(); // Initialize SWS context for software scaling, AV_PIX_FMT_YUV420P sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); //SDL_Init initialize the Event Handling, File I/O, and Threading subsystems, SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {//initialize the video audio & timer subsystem fprintf(stderr, "Could not initialize SDL - %s
", SDL_GetError());//tell the library what features we're going to use exit(1); } // Make a screen to put our video, SDL2.0 SDL_SetVideoMode SDL_Overlay , SDL_CreateWindow SDL_CreateRenderer #ifndef __DARWIN__ screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);// SDL , #else screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);// SDL , #endif if (!screen) {// SDL fprintf(stderr, "SDL: could not set video mode - exiting
"); exit(1); } SDL_WM_SetCaption(argv[1],0);// SDL // Allocate a place to put our YUV image on that screen, bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); /*-------------- -------------*/ i = 0;// Read frames and save first five frames to disk /*----------------------- * read in a packet and store it in the AVPacket struct * ffmpeg allocates the internal data for us,which is pointed to by packet.data * this is freed by the av_free_packet() -----------------------*/ while(av_read_frame(pFormatCtx, &packet) >= 0) {// , AVPacket // Is this a packet from the video stream, if (packet.stream_index == videoStream) { /*----------------------- * Decode video frame, , frameFinished true * packet frame, packet * avcodec_decode_video2() frameFinished * Technically a packet can contain partial frames or other bits of data * ffmpeg's parser ensures that the packets we get contain either complete or multiple frames * convert the packet to a frame for us and set frameFinisned for us when we have the next frame -----------------------*/ avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); // Did we get a video frame, if (frameFinished) { SDL_LockYUVOverlay(bmp);//locks the overlay for direct access to pixel data, , AVFrame pict;// AV_PIX_FMT_YUV420P pict.data[0] = bmp->pixels[0];// pict.data[1] = bmp->pixels[2]; pict.data[2] = bmp->pixels[1]; pict.linesize[0] = bmp->pitches[0];// pict.linesize[1] = bmp->pitches[2];//linesize-Size, in bytes, of the data for each picture/channel plane pict.linesize[2] = bmp->pitches[1];//For audio, only linesize[0] may be set // Convert the image into YUV format that SDL uses, AV_PIX_FMT_YUV420P , pict sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pict.data, pict.linesize); SDL_UnlockYUVOverlay(bmp);//Unlocks a previously locked overlay. An overlay must be unlocked before it can be displayed // rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp, &rect);// } } // Free the packet that was allocated by av_read_frame, AVPacket av_packet_unref(&packet); /*------------------------- * SDL SDL_Event * SDL , * SDL_PollEvent() is the favored way of receiving system events * since it can be done from the main loop and does not suspend the main loop * while waiting on an event to be posted * poll for events right after we finish processing a packet ------------------------*/ SDL_PollEvent(&event); switch (event.type) {// SDL case SDL_QUIT:// printf("SDL_QUIT
"); SDL_Quit();// exit(0);// break; default: break; }//end for switch }//end for while /*-------------- -------------*/ // Free the YUV frame. av_free(pFrame); // Close the codec. avcodec_close(pCodecCtx); // Close the video file. avformat_close_input(&pFormatCtx); return 0; }
ここで、fmpegプレーヤーについて詳しく説明します。ビデオ表示の文章はここまで紹介します。より多くの関連のffmpegプレーヤーの実現内容は以前の文章を検索してください。または下記の関連記事を引き続き閲覧してください。これからもよろしくお願いします。