生活随笔
收集整理的這篇文章主要介紹了
FFmpeg+SDL视频播放器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本博客是摘自雷霄驊大神的課程《基于 FFmpeg + SDL 的視頻播放器的制作》課程 里的內容,非常適合音視頻小白入門,在這里感謝雷神的指導!
目錄
- FFmpeg和SDL的整合實現視頻播放
- 進階:脫離開發環境的獨立播放器
FFmpeg和SDL的整合實現視頻播放
整合方式
? FFmpeg解碼器實現了:視頻文件->YUV
? SDL視頻顯示實現了:YUV->屏幕
? FFmpeg+SDL整合之后實現了:視頻文件->YUV->屏幕
#include <stdio.h>#define __STDC_CONSTANT_MACROSextern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL.h"
};
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)int thread_exit
= 0;int sfp_refresh_thread(void* opaque
) {thread_exit
= 0;while (!thread_exit
) {SDL_Event event
;event
.type
= SFM_REFRESH_EVENT
;SDL_PushEvent(&event
);SDL_Delay(40);}thread_exit
= 0;SDL_Event event
;event
.type
= SFM_BREAK_EVENT
;SDL_PushEvent(&event
);return 0;
}int main(int argc
, char* argv
[])
{AVFormatContext
* pFormatCtx
;int i
, videoindex
;AVCodecContext
* pCodecCtx
;AVCodec
* pCodec
;AVFrame
* pFrame
, * pFrameYUV
;uint8_t* out_buffer
;AVPacket
* packet
;int ret
, got_picture
;int screen_w
, screen_h
;SDL_Window
* screen
;SDL_Renderer
* sdlRenderer
;SDL_Texture
* sdlTexture
;SDL_Rect sdlRect
;SDL_Thread
* video_tid
;SDL_Event event
;struct SwsContext* img_convert_ctx
;char filepath
[] = "屌絲男士.mov";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
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {videoindex
= i
;break;}if (videoindex
== -1) {printf("Didn't find a video stream.\n");return -1;}AVCodecParameters
* codecParameters
= pFormatCtx
->streams
[videoindex
]->codecpar
;pCodecCtx
= avcodec_alloc_context3(nullptr);avcodec_parameters_to_context(pCodecCtx
, codecParameters
);pCodec
= (AVCodec
*)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;}packet
= av_packet_alloc();pFrame
= av_frame_alloc();pFrameYUV
= av_frame_alloc();out_buffer
= (uint8_t*)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_TIMER
)) {printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}screen_w
= pCodecCtx
->width
;screen_h
= pCodecCtx
->height
;screen
= SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED
, SDL_WINDOWPOS_UNDEFINED
,screen_w
, screen_h
, SDL_WINDOW_OPENGL
);if (!screen
) {printf("SDL: could not create window - exiting:%s\n", SDL_GetError());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
;packet
= (AVPacket
*)av_malloc(sizeof(AVPacket
));video_tid
= SDL_CreateThread(sfp_refresh_thread
, NULL, NULL);for (;;) {SDL_WaitEvent(&event
);if (event
.type
== SFM_REFRESH_EVENT
) {if (av_read_frame(pFormatCtx
, packet
) >= 0) {if (packet
->stream_index
== videoindex
) {if (avcodec_send_packet(pCodecCtx
, packet
) < 0) {printf("avcodec_send_packet failed!.\n");continue;}while (1) {ret
= avcodec_receive_frame(pCodecCtx
, pFrame
);if (ret
!= 0)break;sws_scale(img_convert_ctx
, (const uint8_t* const*)pFrame
->data
, pFrame
->linesize
, 0, pCodecCtx
->height
, pFrameYUV
->data
, pFrameYUV
->linesize
);SDL_UpdateTexture(sdlTexture
, NULL, pFrameYUV
->data
[0], pFrameYUV
->linesize
[0]);SDL_RenderClear(sdlRenderer
);SDL_RenderCopy(sdlRenderer
, sdlTexture
, NULL, NULL);SDL_RenderPresent(sdlRenderer
);}}av_packet_unref(packet
);}else {thread_exit
= 1;}}else if (event
.type
== SDL_QUIT
) {thread_exit
= 1;}else if (event
.type
== SFM_BREAK_EVENT
) {break;}}sws_freeContext(img_convert_ctx
);SDL_Quit();av_frame_free(&pFrameYUV
);av_frame_free(&pFrame
);avcodec_close(pCodecCtx
);avformat_close_input(&pFormatCtx
);return 0;
}
進階:脫離開發環境的獨立播放器
因為VS會生成可執行文件(在Debug文件夾中),利用argv參數進行視頻文件的選擇播放
main()函數的參數
? argc argv:全稱為ARGument Counter 和 ARGument Vector。其中argv存儲了來自于命令行的參數;而argc存儲了參數的個數。
? 例如在命令行中輸入“ffmpeg -i test.mkv test.ts ”,則argc取值為4, 而argv[]數組取值如下:
argv[0]=“ffmpeg”
argv[1]=“-i”
argv[2]=“test.mkv”
argv[3]=“test.ts”
? 動態鏈接庫(*.dll)
動態鏈接庫不能被編譯進應用程序。因而使用應用程序的時候必須在相同目錄下保存用到的動態鏈接庫文件。
總結
以上是生活随笔為你收集整理的FFmpeg+SDL视频播放器的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。