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)

 

上一篇: C++使用ffmpeg + SDL2.0搭建简单播放器

下一篇:

立即登录,发表评论
没有帐号?立即注册
0 条评论