C++中使用ffmpeg播放YUV数据
发布于 2019-01-31
945人围观 0条评论
发表于 2019-01-31
945人围观 0条评论
前言
本文参考雷霄骅的博文最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器,使用c++根据ffmpeg-4.1版本改写(原文代码基于旧版本ffmpeg)。代码见下文。
本文代码地址见https://github.com/2997215859/ffplay-learn/blob/master/Video/yuv_player.cpp
本文代码基于ffmpeg-4.1版本,事先需要安装好ffmpeg
本文代码提供CMakeLists.txt,见附录CMakeLists.txt部分,或根据CMakeLists.txt改写。需要链接的库如下(基本上安装ffmpeg、ffplay、SDL2之后就有了)。
avdevice avfilter avformat avcodec swscale swresample postproc avutil m xcb xcb-shm xcb xcb-shape xcb xcb-xfixes xcb-render xcb-shape xcb asound pthread m fontconfig freetype freetype z bz2 lzma SDL2 SDL2main
代码注解
代码主要部分就是读取之前解码器存储的yuv数据,并使用SDL渲染
#include <iostream>
#ifdef __cplusplus
extern "C" {
#endif
#include <SDL2/SDL.h>
#ifdef __cplusplus
};
#endif
using namespace std;
int stopRefresh = 0;
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define BREAK_EVENT (SDL_USEREVENT + 2)
int refreshVideo (void *opaque) {
stopRefresh = 0;
while (!stopRefresh) {
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
stopRefresh = 0;
// Break
SDL_Event event;
event.type = BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
int main (int args, char* argv[]) {
string filepath = "/home/sensetime/videos/output.yuv";
if (SDL_Init(SDL_INIT_VIDEO)) { // 初始化SDL
cerr << "Failed to initialize SDL - " << SDL_GetError() << endl;
return -1;
}
int screenW = 1280;
int screenH = 720;
int pixelW = 1280;
int pixelH = 720;
int bpp = 12;
// 创建窗口
SDL_Window *screen = SDL_CreateWindow("YUV player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screenW, screenH, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!screen) {
cerr << "Failed to create window - " << SDL_GetError() << endl;
return -1;
}
// 创建渲染器
SDL_Renderer *sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
Uint32 pixFormat = SDL_PIXELFORMAT_IYUV;
// IYUV: Y + U + V (3 planes)
// YV12: Y + V + U (3 planes)
// 为渲染器创建纹理
SDL_Texture * sdlTexture = SDL_CreateTexture(sdlRenderer, pixFormat, SDL_TEXTUREACCESS_STREAMING, pixelW, pixelH);
FILE *fp = fopen(filepath.c_str(), "rb+");
if (fp == NULL) {
cerr << "Failed to open this file" << endl;
return -1;
}
SDL_Thread *refresh_thread = SDL_CreateThread(refreshVideo, NULL, NULL);
unsigned char buffer[pixelH*pixelW*bpp/8];
SDL_Event event;
while (true) {
SDL_WaitEvent(&event);
if (event.type == REFRESH_EVENT) {
if (fread(buffer, 1, pixelW * pixelH * bpp / 8, fp) != pixelH * pixelW * bpp / 8) {
// Loop
fseek(fp, 0 ,SEEK_SET);
fread(buffer, 1, pixelW * pixelH * bpp / 8, fp);
}
SDL_Rect sdlRect;
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = screenW;
sdlRect.h = screenH;
SDL_UpdateTexture(sdlTexture, NULL, buffer, pixelW);
// Fix: if window is resize
SDL_RenderClear(sdlRenderer); // 清空渲染器
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); // 将纹理复制到渲染器
SDL_RenderPresent(sdlRenderer);
} else if (event.type == SDL_WINDOWEVENT) {
cout << event.type << endl;
SDL_GetWindowSize(screen, &screenW, &screenH);
} else if (event.type == SDL_QUIT) {
stopRefresh = 1;
} else if (event.type == BREAK_EVENT) {
break;
}
}
SDL_Quit();
return 0;
}附录
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(player)
set(CMAKE_CXX_STANDARD 11)
include_directories(./)
include_directories(/usr/include/.)
include_directories(/usr/local/include/.)
link_directories(/usr/lib/)
link_directories(/usr/local/lib/)
# 设置可执行文件生成路径
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
# 生成debug版本
SET(CMAKE_BUILD_TYPE "release")
if (CMAKE_BUILD_TYPE STREQUAL debug)
add_definitions(-D_DEBUG)
endif ()
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -std=c++11")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -std=c++11")
add_executable(yuv_player
yuv_player.cpp)
target_link_libraries(yuv_player
avdevice
avfilter
avformat
avcodec
swscale
swresample
postproc
avutil
# avresample
m xcb xcb-shm xcb xcb-shape xcb xcb-xfixes xcb-render xcb-shape xcb asound
pthread m fontconfig freetype freetype
z
bz2
lzma
SDL2
SDL2main)
没有帐号?立即注册