SDL播放视频流程:

在这里插入图片描述

相关函数的说明可以看 SDL视频播放API
由于SDL只能播放YUV和RGB格式的像素帧,所以对于MP4、mkv、avi、flv等封装格式的视频文件来说必须对其解码成YUV或RGB,才能用SDL播放,FFMPEG解码成YUV

SDL播放视频流demo

/* SDL 播放视频流*/
#define SDL_MAIN_HANDLED
#include <iostream>
#include <cstdio>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <SDL.h>
}
const char* infile = "1.mp4";
int main(int argc, char** argv)
{
    AVFormatContext* afc = avformat_alloc_context();
    if (avformat_open_input(&afc, infile, NULL, NULL) != 0)
    {
        cout << "open video error " << endl;
        return 0;
    }
    if (avformat_find_stream_info(afc, NULL) < 0)
    {
        cout << "find stream error" << endl;
        return 0;
    }
    int videoflag = -1;
    AVCodec* vdecodec;
    AVCodecContext* vcc = NULL;
    for (int i = 0; i < afc->nb_streams; ++i)
    {
        if (afc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)  //视频
        {
            videoflag = i;
            vdecodec = avcodec_find_decoder(afc->streams[videoflag]->codecpar->codec_id);
            vcc = avcodec_alloc_context3(vdecodec);  //为解码器创建内存
            avcodec_parameters_to_context(vcc, afc->streams[i]->codecpar); //初始化解码器参数
            if (avcodec_open2(vcc, vdecodec, NULL) < 0)
            {
                cout << "open video decodec error" << endl;
                return -1;
            }
        }
    }
    if (videoflag == -1)
    {
        cout << "video stream not find" << endl;
        return -1;
    }
    int win_w = vcc->width;
    int win_h = vcc->height;
    if (SDL_Init(SDL_INIT_VIDEO))
    {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }
    SDL_Window* screen = SDL_CreateWindow("player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win_w, win_h, SDL_WINDOW_OPENGL);
    if (!screen)
    {
        printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
        return -1;
    }
    SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, win_w, win_h);
    SDL_Rect sdlRect; //设置播放窗口的起始位置和大小
    sdlRect.x = 0;
    sdlRect.y = 0;
    sdlRect.w = win_w;
    sdlRect.h = win_h;
    AVFrame* frame = av_frame_alloc();//视频解码后的frame
    AVFrame* yuv = av_frame_alloc();
    AVPacket* pkt = (AVPacket*)av_malloc(sizeof(AVPacket));
    unsigned char* out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, vcc->width, vcc->height, 1));
    av_image_fill_arrays(yuv->data, yuv->linesize, out_buffer, AV_PIX_FMT_YUV420P, vcc->width, vcc->height, 1); //填充yuv像素数据缓冲区,没有这步会导致sws_scale()失败
    SwsContext* ssc = sws_getContext(vcc->width, vcc->height, vcc->pix_fmt,
        vcc->width, vcc->height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC, NULL, NULL, NULL);
    int vret = 0;
    int count = 0;
    while (av_read_frame(afc, pkt) >= 0)
    {
        if (pkt->stream_index == videoflag) //视频解码
        {
            if ( vret = avcodec_send_packet(vcc, pkt) != 0)  //向解码器发送视频数据包
            {
                cout << "send video packet to decodec error, code:" << vret << endl;
                return -1;
            }
            while (vret >= 0)
            {
                vret = avcodec_receive_frame(vcc, frame);  //接收解码后的数据帧
                if (vret == 0)
                {
                    sws_scale(ssc, frame->data, frame->linesize, 0, vcc->height, yuv->data, yuv->linesize);
                    cout << "sws_scale ok" << endl;
                    SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
                        yuv->data[0], yuv->linesize[0],
                        yuv->data[1], yuv->linesize[1],
                        yuv->data[2], yuv->linesize[2]);
                    SDL_RenderClear(sdlRenderer);
                    SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
                    SDL_RenderPresent(sdlRenderer); //播放
                    SDL_Delay(40); //延时40ms,相当于1s播放25帧
                    count++;
                    cout << "player one frame succeed, this is "  << count << endl;
                }
                else if (vret == AVERROR(EAGAIN)|| vret == AVERROR_EOF)  //接收到的数据无效 需要重新读入
                {
                    cout << "receive video frame  error, need again" << endl;
                    break;
                }
                else
                {
                    cout << "avcodec_receive_frame video error  code:" << vret << endl;
                    return -1;
                }
            }
        }
    }
    //播放最后解码器中剩下的数据帧
    if (vret = avcodec_send_packet(vcc, NULL) != 0)
    {
        cout << "send video packet to decodec error, code:" << vret << endl;
        return -1;
    }
    while (vret >= 0)
    {
        vret = avcodec_receive_frame(vcc, frame);
        if (vret == 0)
        {
            sws_scale(ssc, frame->data, frame->linesize, 0, vcc->height, yuv->data, yuv->linesize);
            cout << "sws_scale ok" << endl;
            SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
                        yuv->data[0], yuv->linesize[0],
                        yuv->data[1], yuv->linesize[1],
                        yuv->data[2], yuv->linesize[2]);
            SDL_RenderClear(sdlRenderer);
            SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
            SDL_RenderPresent(sdlRenderer);
            SDL_Delay(40);
            count++;
            cout << "player one frame from AVCodecContext succeed, this is " << count << endl;
        }
        else
        {
            cout << "avcodec_receive_frame video error  code:" << vret << endl;
            break;
        }  
    }
    cout << "player ok" << endl;
    SDL_Quit();
    //释放资源
    av_packet_unref(pkt);
    av_frame_free(&yuv);
    av_frame_free(&frame);
    avcodec_free_context(&vcc);
    avformat_close_input(&afc);
    return 0;
}

 

 
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐