??今天的主題是播放yuv格式視頻。
在上兩篇博客里面介紹過了如何將視頻解碼為單獨的視頻和音頻,但是如果播放這些格式的文件呢,安卓原生是沒有這些控件來直播播放yuv格式視頻的,必須通過借助其他方式來播放,今天就介紹SDL2來解碼播放yuv文件。
SDL
??什么是SDL?SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平臺多媒體開發庫,專門用來開發多媒體,游戲等。詳細想了解的可以去SDL官方文檔查看
下面是FFmpeg官方給出的播放流程
image.png
根據上面的流程,在得到yuv像素數據的時候就可以渲染了
采用SDL播放yuv文件播放流程:
?? 第一步:初始化SDL多媒體框架->SDL_Init
??第二步:初始化SDL窗口
??第三步:創建渲染器->渲染窗口(OpenGL ES)
??第四步:設置紋理數據->播放YUV視頻
??第五步:將紋理數據拷貝到渲染器
??第六步:顯示幀畫面
??第七步:退出SDL釋放內存
源碼
#include <jni.h>
#include <android/log.h>
#define LOG_I(...) __android_log_print(ANDROID_LOG_ERROR , "main", __VA_ARGS__)
#include "SDL.h"
#include "SDL_log.h"
#include "SDL_main.h"
////avcodec:編解碼(最重要的庫)
//#include "libavcodec/avcodec.h"
////avformat:封裝格式處理
//#include "libavformat/avformat.h"
////avutil:工具庫(大部分庫都需要這個庫的支持)
//#include "libavutil/imgutils.h"
////swscale:視頻像素數據格式轉換
//#include "libswscale/swscale.h"
////導入音頻采樣數據格式轉換庫
//#include "libswresample/swresample.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
}
int main(int argc, char *argv[]) {
//第一步:初始化SDL多媒體框架->SDL_Init
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) == -1) {
LOG_I("SDL_Init failed %s", SDL_GetError());
return 0;
}
LOG_I("SDL_Init Success!");
//第二步:初始化SDL窗口
//參數一:窗口名稱->要求必需是UTF-8編碼
//參數二:窗口在屏幕上面X坐標
//參數三:窗口在屏幕上面Y坐標
//參數四:窗口在屏幕上面寬
int width = 640;
//參數五:窗口在屏幕上面高
int height = 352;
//參數六:窗口狀態(打開的狀態:SDL_WINDOW_OPENGL)
SDL_Window* sdl_window = SDL_CreateWindow("SDL播放器",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
width ,
height,
SDL_WINDOW_OPENGL);
if (sdl_window == NULL){
LOG_I("窗口創建失敗");
return 0;
}
//第三步:創建渲染器->渲染窗口(OpenGL ES)
//最新一期VIP課程
//參數一:渲染目標窗口
//參數二:從哪里開始渲染(-1:默認從第一個為止開始)
//參數三:渲染類型
//SDL_RENDERER_SOFTWARE:軟件渲染
//...
SDL_Renderer* sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
//第四步:創建紋理
//參數一:紋理目標渲染器
//參數二:渲染格式
//參數三:繪制方式(SDL_TEXTUREACCESS_STREAMING:頻繁繪制)
//參數四:紋理寬
//參數五:紋理高
SDL_Texture * sdl_texture = SDL_CreateTexture(sdl_renderer,
SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING,
width,
height);
//第五步:設置紋理數據->播放YUV視頻
//著色器語言(著色器)、渲染器、紋理等等...
//第一點:打開YUV文件(手機:)
FILE* yuv_file = fopen("/storage/emulated/0/DreamTestFile/Test.yuv","rb+");
if (yuv_file == NULL){
LOG_I("文件打開失敗");
return 0;
}
//第二點:循環讀取YUV視頻像素數據格式每一幀畫面->渲染->設置紋理數據
//定義緩沖區(內存空間開辟多大?)
//Y:U:V = 4 : 1 : 1
//假設:Y = 1.0 U = 0.25 V = 0.25
//寬度:Y + U + V = 1.5
//換算:Y + U + V = width * height * 1.5
char buffer_pix[width * height * 3 / 2];
//定義渲染器區域
SDL_Rect sdl_rect;
while (true){
//一行一行的讀取
fread(buffer_pix, 1, width * height * 3 / 2, yuv_file);
//判定是否讀取完畢
if (feof(yuv_file)){
break;
}
//設置紋理數據
//參數一:目標紋理對象
//參數二:渲染區域(NULL:表示默認屏幕窗口寬高)
//參數三:視頻像素數據
//參數四:幀畫面寬
SDL_UpdateTexture(sdl_texture, NULL, buffer_pix, width);
//第六步:將紋理數據拷貝到渲染器
sdl_rect.x = 0;
sdl_rect.y = 0;
sdl_rect.w = width;
sdl_rect.h = height;
//先清空
SDL_RenderClear(sdl_renderer);
//再渲染
SDL_RenderCopy(sdl_renderer,sdl_texture,NULL,&sdl_rect);
//第七步:顯示幀畫面
SDL_RenderPresent(sdl_renderer);
//第八步:延時渲染(沒渲染一幀間隔時間)
SDL_Delay(20);
}
//第九步:是否內存
fclose(yuv_file);
SDL_DestroyTexture(sdl_texture);
SDL_DestroyRenderer(sdl_renderer);
//第十步:推出SDL程序
SDL_Quit();
return 0;
}
上面的源碼只是演示如何播放yuv格式視頻,當然在實際項目中還需要根據具體業務加入具體業務邏輯,在播放網絡流的情況下還需要處理音頻和視頻間的同步和時間戳問題。這里推薦一個SDL和FFmpeg的國外教程,很實用的一個教程。