C++中使用ffmpeg播放YUV数据
发布于 2019-01-31
841人围观 0条评论
发表于 2019-01-31
841人围观 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)
没有帐号?立即注册