FFmpeg编程(四)SDL与FFmpeg的联合使用

(一)回顾

FFmpeg编程(二)FFmpeg中级开发

FFmpeg编程(三)SDL开发

(二)FFmpeg与SDL的简单结合

#include <stdio.h>
#include <SDL.h>

#include <libavutil/log.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>

int main(int argc,char* argv[]){
    char* in_file = NULL;

    int ret = -1;            //返回值
    int len;                //存放读取的数据大小
    int stream_idx;            //存放视频流的索引
    int frameFin;            //标志packet中数据帧是否读取完成

    AVFormatContext* fmt_cxt = NULL;    //格式上下文
    AVCodecContext* codec_cxt = NULL;    //编解码器上下文
    struct SwsContext* sws_cxt = NULL;    //图像处理上下文

    AVCodec* codec = NULL;    //编解码器
    AVFrame* frame = NULL;    //
    AVPicture* pict = NULL;    //YUV图像存放

    AVPacket packet;        //

    SDL_Window* win = NULL;    //SDL窗口指针
    SDL_Renderer* rend = NULL;    //SDL渲染器指针
    SDL_Texture* text = NULL;    //纹理

    SDL_Rect rect;    //用于显示数据的矩阵区域

    SDL_Event event;    //SDL事件

    int w_width = 640;    //设置的默认窗口大小,后面会进行调整
    int w_height = 480;

    if(argc<2){        //检查是否设置了播放的文件名称
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Usage: command <file>");
        return ret;
    }
    in_file = argv[1];

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to initialize SDL - %s!",SDL_GetError());
        return ret;
    }
    //------------------------配置所有的FFmpeg信息
    
    av_register_all();    //注册所有协议

    //获取格式上下文
    if(avformat_open_input(&fmt_cxt,in_file,NULL,NULL)<0){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to open video!");
        goto __INIT;
    }

    //获取流的信息,这里换一种方式获取,不直接使用av_find_best_stream获取
    stream_idx = -1;
    for(int i=0;i<fmt_cxt->nb_streams;i++){
        if(fmt_cxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            stream_idx = i;
            break;
        }
    }

    if(stream_idx==-1){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to get video stream!");
        goto __FORMAT;
    }

    //打印输入视频流的详细信息
    if(avformat_find_stream_info(fmt_cxt,0)<0){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to get video stream information!");
        goto __FORMAT;
    }

    av_dump_format(fmt_cxt,stream_idx,in_file,0);    //打印信息

    //开始获取解码器,对视频流进行解码获取YUV数据
    //根据输入流的参数获取对于的解码器
    codec = avcodec_find_decoder(fmt_cxt->streams[stream_idx]->codec->codec_id);
    if(codec==NULL){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to find video decoder!");
        goto __FORMAT;
    }

    //获取对应上下文
    codec_cxt = avcodec_alloc_context3(codec);
    if(!codec_cxt){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to find video decoder context!");
        goto __FORMAT;
    }

    //开始拷贝输入流参数到解码器中
    if(avcodec_copy_context(codec_cxt,fmt_cxt->streams[stream_idx]->codec)<0){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to copy video decoder context!");
        goto __CODEC;
    }

    //打开编解码器
    if(avcodec_open2(codec_cxt,codec,NULL)<0){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to opn video decoder!");
        goto __CODEC;
    }

    frame = av_frame_alloc();    //分配帧
    if(!frame){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to alloc video frame!");
        goto __CODEC;
    }

    //创建图片转换上下文(在上面参数拷贝后面)-----------------
    //源图像宽、高、像素格式;目标图像宽、高、像素格式;以及图像拉伸使用的算法
    sws_cxt = sws_getContext(codec_cxt->width,codec_cxt->height,codec_cxt->pix_fmt,
        codec_cxt->width,codec_cxt->height,AV_PIX_FMT_YUV420P,    //输出像素格式YUV
        SWS_BILINEAR,NULL,NULL,NULL);    //获取图像处理上下文

    pict = (AVPicture*)malloc(sizeof(AVPicture));
    if(!pict){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to alloc video picture frame!");
        goto __FRAME;
    }
    avpicture_alloc(pict,
        AV_PIX_FMT_YUV420P,
        codec_cxt->width,
        codec_cxt->height);    //为图像存放分配空间

    //------------------------开始设置SDL数据
    w_width = codec_cxt->width;
    w_height = codec_cxt->height;

    //创建窗口
    win = SDL_CreateWindow("Media Player",
        SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,    //不指定左上位置
        w_width,w_height,            //设置窗口宽高,与视频一般或者大些都可以
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);    //窗口可用于OpenGL上下文 窗口可以调整大小
    if(!win){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to create window to SDL");
        goto __PICT;
    }

    //创建渲染器
    rend = SDL_CreateRenderer(win,-1,0);
    if(!rend){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to create renderer to SDL");
        goto __WIN;
    }

    //创建纹理
    text = SDL_CreateTexture(rend,SDL_PIXELFORMAT_IYUV,
        SDL_TEXTUREACCESS_STREAMING,    //视频流,连续的
        w_width,w_height);
    if(!text){
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Failed to create texture to SDL");
        goto __RENDER;
    }
    
    rect.x = 0;    //显示矩阵的左上点
    rect.y = 0;
    //------------------------开始读取数据,结合SDL与FFmpeg
    av_init_packet(&packet);    //初始化数据包
    while(av_read_frame(fmt_cxt,&packet)>=0){        
        if(packet.stream_index==stream_idx){    //属于视频流
            len = avcodec_decode_video2(codec_cxt,frame,&frameFin,&packet);    //作用是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame。
            if(len<0){
                SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,"Fail to decode video frame");
                goto __INIT;
            }

            if(frameFin){    //表示读取完成了一帧,开始进行转换,解码数据已经放入了frame中
                sws_scale(sws_cxt,(uint8_t const * const *)frame->data,frame->linesize,    //当前处理区域的每个通道数据指针,每个通道行字节数
                    0,codec_cxt->height,    //参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域,srcSliceY是起始位置,srcSliceH是处理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。
                    pict->data,pict->linesize);     //定义输出图像信息(输出的每个通道数据指针,每个通道行字节数)

                //可以渲染YUV数据
                SDL_UpdateYUVTexture(text,NULL,
                    pict->data[0],pict->linesize[0],
                    pict->data[1],pict->linesize[1],
                    pict->data[2],pict->linesize[2]);

                //更新矩阵的信息
                rect.w = codec_cxt->width;
                rect.h = codec_cxt->height;

                SDL_RenderClear(rend);                   //清空渲染器缓冲区

                SDL_RenderCopy(rend,text,NULL,&rect);    //拷贝纹理到显卡,选择渲染目标的一块矩形区域作为输出
                SDL_RenderPresent(rend);                //将结果显示到窗口
            }
        }

        av_packet_unref(&packet);    //注意:减少引用不会释放空间,因为本函数还在引用这个packet结构体;
        //所以我们在解码函数中剩余的其他帧数据,还是保留在packet中的,后面根据av_read_frame继续向内部添加数据

        //设置事件轮询
        SDL_PollEvent(&event);
        switch(event.type){
            case SDL_QUIT:
                goto __QUIT;
                break;
            default:
                break;
        }

    }

__QUIT:
    ret = 0;
    av_packet_unref(&packet);    //减少引用,使得自己释放空间
    SDL_DestroyTexture(text);
__RENDER:
    SDL_DestroyRenderer(rend);
__WIN:
    SDL_DestroyWindow(win);
__PICT:
    avpicture_free(pict);
    free(pict);
__FRAME:
    av_frame_free(&frame);
__CODEC:
    avcodec_close(codec_cxt);
__FORMAT:
    avformat_close_input(&fmt_cxt);
__INIT:
    SDL_Quit();

    return ret;
}
View Code

相关文章:

  • 2021-11-16
  • 2022-12-23
  • 2021-12-24
  • 2022-01-01
  • 2022-12-23
  • 2021-08-18
  • 2021-12-22
  • 2021-09-11
猜你喜欢
  • 2022-12-23
  • 2021-10-31
  • 2022-02-07
  • 2022-12-23
  • 2022-12-23
  • 2021-05-23
  • 2022-01-02
相关资源
相似解决方案