首页 > 其他分享 >SDL2+FFmpeg5.0播放视频文件

SDL2+FFmpeg5.0播放视频文件

时间:2023-11-16 11:55:31浏览次数:29  
标签:pCodecCtx SDL2 sdlRenderer height SDL pFrameYUV FFmpeg5.0 视频文件 NULL

一、概述

  上一节使用SDL2播放了YUV视频文件,本节使用SDL2+FFmpeg5.0播放一个视频文件(只播放视频,不播放声音)

  播放效果图:

 

二、代码示例

#include "sdl_ffmpeg_play.h"


//sdl刷新事件
#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)
//sdl退出事件
#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)

int sdl_thread_exit = 0;

int sfp_refresh_thread(void* opaque) {
    sdl_thread_exit = 0;
    while (!sdl_thread_exit) {
        SDL_Event event;
        event.type = SFM_REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(10);
    }
    sdl_thread_exit = 0;
    //Break
    SDL_Event event;
    event.type = SFM_BREAK_EVENT;
    SDL_PushEvent(&event);

    return 0;
}


SDLFFmpegPlay::SDLFFmpegPlay() {

    AVFormatContext* pFormatCtx;
    int                i, videoindex;
    AVCodecContext* pCodecCtx;
    const AVCodec* pCodec;
    AVFrame* pFrame, * pFrameYUV;
    uint8_t* out_buffer;
    AVPacket* packet;
    int ret, got_picture;

    //------------SDL----------------
    int screen_w, screen_h;
    SDL_Window* screen;
    SDL_Renderer* sdlRenderer;
    SDL_Texture* sdlTexture;
    SDL_Rect sdlRect;
    SDL_Thread* video_tid;
    SDL_Event event;
    char retError[128] = {0};
    struct SwsContext* img_convert_ctx;

    char filepath[] = "E:/tony/demo/visualstudio_workspace/SDLDemo/SDLDemo/videos/diao_si_nan_shi.mov";

    avformat_network_init();
    pFormatCtx = avformat_alloc_context();

    //尝试打开媒体流文件
    ret = avformat_open_input(&pFormatCtx, filepath, NULL, NULL);
    if (ret != 0) {
        av_strerror(ret, retError,sizeof(retError));
        cout << "无法打开文件" << ret << ":" << retError << endl;
        return;
    }

    //获取文件媒体流信息
    ret = avformat_find_stream_info(pFormatCtx, NULL);
    if ( ret < 0) {
        av_strerror(ret, retError, sizeof(retError));
        cout << "无法获取文件媒体流信息:" << ret << ":" << retError << endl;
        avformat_close_input(&pFormatCtx);
        return;
    }

    //查找视频流索引
    videoindex = -1;
    videoindex = av_find_best_stream(pFormatCtx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
    if (AVERROR_STREAM_NOT_FOUND == videoindex) {
        cout << "无法找到视频流" << endl;
        avformat_close_input(&pFormatCtx);
        return;
    }

    //查找解码器
    pCodec = avcodec_find_decoder(pFormatCtx->streams[videoindex]->codecpar->codec_id);
    if (NULL == pCodec) {
        cout << "无法找到解码器" << endl;
        avformat_close_input(&pFormatCtx);
        return;
    }

    //初始化解码器上下文
    pCodecCtx = avcodec_alloc_context3(pCodec);

    //初始化解码器上下文
    ret = avcodec_parameters_to_context(pCodecCtx,pFormatCtx->streams[videoindex]->codecpar);
    if (ret < 0) {
        av_strerror(ret, retError, sizeof(retError));
        cout << "初始化解码器上下文失败:" << ret << ":" << retError << endl;
        avformat_close_input(&pFormatCtx);
        return;
    }

    //打开解码器
    ret = avcodec_open2(pCodecCtx,pCodec,NULL);
    if (ret != 0) {
        av_strerror(ret, retError, sizeof(retError));
        cout << "无法打开解码器:" << ret << ":" << retError << endl;
        avformat_close_input(&pFormatCtx);
        return;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();
    pFrameYUV->width = pCodecCtx->width;
    pFrameYUV->height = pCodecCtx->height;
    pFrameYUV->format = AV_PIX_FMT_YUV420P;
    ret = av_frame_get_buffer(pFrameYUV, 0);
    cout << "ret:" << ret << endl;
    cout << "width:" << pCodecCtx->width << endl;
    cout << "height" <<pCodecCtx->height<< endl;
    //给frameYUV分配内存
    //av_frame_get_buffer(pFrameYUV, AV_PIX_FMT_YUV420P);
    //out_buffer = (uint8_t*)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P), pCodecCtx->width, pCodecCtx->height));
    //avpicture_fill((AVPicture*)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

    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;
    }
    //SDL 2.0 Support for multiple windows
    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;
    }
    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    //IYUV: Y + U + V  (3 planes)
    //YV12: Y + V + U  (3 planes)
    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);
    //------------SDL End------------
    //Event Loop
    cout << "开始循环解码" << endl;
    for (;;) {
        //Wait
        SDL_WaitEvent(&event);
        if (event.type == SFM_REFRESH_EVENT) {
            cout << "开始解码" << endl;
            //------------------------------
            if (av_read_frame(pFormatCtx, packet) >= 0) {
                if (packet->stream_index == videoindex) {
                    //把编码数据送入解码器解码
                    ret = avcodec_send_packet(pCodecCtx,packet);//返回0代表解码成功
                    if (ret < 0) {
                        cout << "解码packet失败" << endl;
                        return;
                    }
                    //从解码器中拿出解码后的数据
                    //got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
                    //拿到数据后对数据进行转换
                    while (avcodec_receive_frame(pCodecCtx, pFrame)==0) {
                        cout <<"解码数据->width:"<< pFrame->width<<",height="<<pFrame->height << endl;
                        sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                        //SDL---------------------------
                        SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
                        SDL_RenderClear(sdlRenderer);
                        //SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
                        SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
                        SDL_RenderPresent(sdlRenderer);
                        //SDL End-----------------------
                    }
                }
                av_packet_unref(packet);
            }
            else {
                //Exit Thread
                sdl_thread_exit = 1;
            }
        }
        else if (event.type == SDL_QUIT) {
            sdl_thread_exit = 1;
        }
        else if (event.type == SFM_BREAK_EVENT) {
            break;
        }

    }
    if (img_convert_ctx) {
        sws_freeContext(img_convert_ctx);
    }


    SDL_Quit();
    //--------------
    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

SDLFFmpegPlay::~SDLFFmpegPlay() {

}

  三、遇到的问题

    1.目标frame未分配内存空间

[swscaler @ 000002a75aa0a700] bad dst image pointers

    解决办法:使用av_frame_get_buffer分配一下即可

    pFrameYUV = av_frame_alloc();
    pFrameYUV->width = pCodecCtx->width;
    pFrameYUV->height = pCodecCtx->height;
    pFrameYUV->format = AV_PIX_FMT_YUV420P;
    ret = av_frame_get_buffer(pFrameYUV, 0);

  2.avcodec_receive_frame用法错误,导致解码后的AVFrame中的数据是空的、宽高为0,错误提示如下

[swscaler @ 0000022b16e2a700] bad src image pointers

  错误用法:

//从解码器中拿出解码后的数据
got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
//拿到数据后对数据进行转换
got_picture = avcodec_receive_frame(pCodecCtx, pFrame);
if (got_picture) {
                cout <<"解码数据->width:"<< pFrame->width<<",height="<<pFrame->height << endl;
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                //SDL---------------------------
                SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
                SDL_RenderClear(sdlRenderer);
                //SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
                SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
                SDL_RenderPresent(sdlRenderer);
                //SDL End-----------------------
}

  正确用法

if (avcodec_receive_frame(pCodecCtx, pFrame)==0) {
                cout <<"解码数据->width:"<< pFrame->width<<",height="<<pFrame->height << endl;
                sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                //SDL---------------------------
                SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
                SDL_RenderClear(sdlRenderer);
                //SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
                SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
                SDL_RenderPresent(sdlRenderer);
                //SDL End-----------------------
}

  

标签:pCodecCtx,SDL2,sdlRenderer,height,SDL,pFrameYUV,FFmpeg5.0,视频文件,NULL
From: https://www.cnblogs.com/tony-yang-flutter/p/17835917.html

相关文章

  • 【ffmpeg】使用 FFmpeg 在一个视频文件上添加一个循环的 GIF 水印
    这段代码使用FFmpeg在一个视频文件(1.mp4)上添加一个循环的GIF水印(3.gif),并将输出保存为另一个视频文件(output.mp4),(如果在使用时遇到问题,可能需要调整一些参数,例如帧率、水印的位置或大小,以满足特定的需求)。 【ffmpeg命令】ffmpeg-i1.mp4-ignore_loop0-i3.g......
  • SDL2+SDL_Thread+SDL_Event实现yuv文件的播放
    一、概述上一节使用单线程播放了YUV文件。在一个线程中播放yuv文件逻辑看起来简单,但是会产生一些问题。如:视频卡顿、无响应等问题。本节在上一节的基础上对播放YUV文件的代码进行改造,加入SDL_Event和SDL_Thread。使SDL_Thread现成发出命令时刷新YUV视频帧。等收到结束命......
  • SDL2加载一个图片
    一、概述使用SDL2+SDL2_Image显示一张图片操作步骤:SDL2的操作流程:1.初始化SDLSDL_Init2.创建SDL_WindowSDL_CreateWindow3.创建渲染器SDL_RendererSDL_CreateRenderer4.生成一个SDL_Surface,这个Surfac......
  • SDL2 无法解析的外部符号 main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@
    一、概述在使用VisualStudio+CMake集成SDL2的过程中。运行一个Demo示例出现了以下错误提示无法解析的外部符号main,函数"int__cdeclinvoke_main(void)"(?invoke_main@@YAHXZ) 二、解决办法上面问题的主要原因是程序找不到入口函数因为SDL中自己也定义了......
  • 相机突然断电,保存的DAT视频文件如何打开
    3-6本文主要解决因相机突然断电导致拍摄的视频文件打不开的问题。在平常使用相机拍摄视频,比如使用佳能相机拍摄视频的时候,如果电池突然断电,就非常有可能会导致视频没来得及保存而损坏的情况,比如会产生下图中的这种DAT文件这个DAT文件就是相机临时保存的数据没来得及处理为视频文件......
  • Windows10+VSCode+cmake+opencv+ffmpeg+sdl2环境配置
    一、概述在Windows10上配置一个C++开发环境:工具:VSCode编译器:Mingw64(使用gcc进行编译)构建工具:CMake第三方库:集成OpenCV、FFmpeg、SDL2二、操作步骤1.安装mingw64并配置bin目录到环境变量2.下载VSCode并安装3.安装CMake并......
  • MP4视频文件损坏怎么修复?
    3-2作为摄影师,或者在平时有拍摄工作的事情的,比如搞婚庆、搞航拍什么的,有一定的概率会遇到损坏的视频文件,比如相机突然断电、无人机炸机等,有可能会导致保存的MP4文件损坏。这种文件使用播放器播放的话,会提示播放不了如果遇到这种情况,该咋办呢?下面有个方法,也许可以帮你修复损坏的视频......
  • 使用fluent-ffmpeg将完整视频文件转码切片为.ts .m3u8文件以实现hls流媒体传输
    使用fluent-ffmpeg将完整视频文件转码切片为.ts.m3u8文件以实现hls流媒体传输安装项目内安装:npmiffmpegnpmifluent-ffmpeg外部环境安装:官网下载ffmpeg,将文件bin目录添加为Path系统环境变量C:\Users\lenovo\Desktop\ffmpeg-6.0-essentials_build\ffmpeg-6.0-essential......
  • 视频融合/监控汇聚平台EasyCVR如何推送本地录像视频文件进行AI视频智能分析?
    安防视频监控平台EasyCVR是一个具有强大拓展性、灵活的视频能力和轻便部署的平台。它支持多种主流标准协议,包括国标GB28181、RTSP/Onvif、RTMP等,还可以支持厂家的私有协议和SDK接入,例如海康Ehome、海大宇等设备的SDK。该平台不仅拥有传统安防视频监控的功能,还具备接入AI智能分析的......
  • EasyGBS可以通过什么方法可以实现长久的存储大量视频文件
    通过采用上述技术手段,EasyGBS可以确保大量视频文件的持久性和可访问性,从而为用户提供稳定可靠的视频监控服务。EasyGBS可以通过以下方式实现长久的存储大量视频文件:1.分片存储:为了解决大量视频文件存储的问题,EasyGBS可以将视频文件分成多个小片,然后分别存储。这种分片存储的方式......