SDL,ffmpeg实现简单视频播放器

看了雷神的一些文章和解释,自己重新实现了一下相关代码的东西,做为加深。

一起在音视频领域加油咯!

// 基于FFmpeg用SDL实现一个视频播放器(.h264)
//

/*
AVFormatContext:统领全局的基本结构体。主要用于处理封装格式(FLV/MKV/RMVB等)。
AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。
AVStream,AVCodecContext:视音频流对应的结构体,用于视音频编解码。
AVFrame:存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据)
AVPacket:存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据)
原文链接:https://blog.csdn.net/leixiaohua1020/article/details/41181155
*/

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
}

class SDLHandle
{
public:
    SDLHandle(int w, int h) 
    {
        m_rect.x = 0;
        m_rect.y = 0;
        m_rect.w = w;
        m_rect.h = h;
        SdlInit();
    }

    ~SDLHandle()
    {
        if (m_pTexture)
        {
            SDL_DestroyTexture(m_pTexture);
        }
        if (m_pRender)
        {
            SDL_DestroyRenderer(m_pRender);
        }
        if (m_pWnd)
        {
            SDL_DestroyWindow(m_pWnd);
        }
        SDL_Quit();
    }

    bool CreateSDLWindow(const char* title, Uint32 flag)
    {
        m_pWnd = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_rect.w, m_rect.h, flag);
        if (!m_pWnd)
        {
            printf("CreateWindows error:%s.\n", SDL_GetError());
            return false;
        }

        m_pRender = SDL_CreateRenderer(m_pWnd, -1, 0);
        if (!m_pRender)
        {
            return false;
        }
        m_pTexture = SDL_CreateTexture(m_pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, m_rect.w, m_rect.h);
        if (!m_pTexture)
        {
            return false;
        }
        return true;
    }

    void UpdateTexture(AVFrame* pFrame)
    {
        if (!pFrame)
        {
            return;
        }

        //SDL_UpdateTexture(m_pTexture, &m_rect, pFrame->data[0], pFrame->linesize[0]);
        SDL_UpdateYUVTexture(m_pTexture, &m_rect, pFrame->data[0], pFrame->linesize[0], pFrame->data[1], pFrame->linesize[1], pFrame->data[2], pFrame->linesize[2]);
        
        SDL_RenderClear(m_pRender);
        SDL_RenderCopy(m_pRender, m_pTexture, nullptr, &m_rect);
        SDL_RenderPresent(m_pRender);

        SDL_Delay(40);
    }
private:
    bool SdlInit()
    {
		if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
		{
			printf("sdl_init error:%s\n", SDL_GetError());
            return false;
		}
        return true;
    }
private:
    SDL_Renderer* m_pRender = nullptr;
    SDL_Window* m_pWnd = nullptr;
    SDL_Texture* m_pTexture = nullptr;

    SDL_Rect m_rect;
};


int main(int argc, char* argv[])
{
    AVFormatContext* pFormatCtx = nullptr;
    AVCodecContext* pCodecCtx = nullptr;
    AVCodec* pCodec = nullptr;  // 解码器
    int iVideoIndex = 0, iRet = 0;

    AVFrame* pFrame = nullptr;
    AVFrame* pFrameYUV = nullptr;
    AVPacket* pPacket = nullptr;
    unsigned char* out_buffer = nullptr;
    
    SwsContext* pSwsCtx = nullptr;
    FILE* fp_yuv = nullptr;
    int got_picture = 0;
    // sdl
    int nScreen_w = 0, nScreen_h = 0;
    SDL_Window* pScreen = nullptr;      // 播放窗口
    SDL_Renderer* pRender = nullptr;    // 渲染器
    SDL_Texture* pSDLTexture = nullptr; // 纹理

    char filepath[] = "bigbuckbunny_480x272.h265";

    SDLHandle* m_pSDlHandle = nullptr;
    // ffmpeg
    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();
    if (!pFormatCtx)
    {
        printf("avformat_alloc_context error!\n");
        goto exit;
    }

    // 该函数用于打开多媒体数据并且获得一些相关的信息
    if (avformat_open_input(&pFormatCtx, filepath, nullptr, nullptr) < 0)
    {
        printf("avformat_open_input failed!\n");
        goto exit;
    }

    // 该函数可以读取一部分视音频数据并且获得一些相关的信息
    if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
    {
        printf("avformat_find_stream_info error!\n");
        goto exit;
    }

    iVideoIndex = -1;
    for (int i=0; i< pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
        {
            iVideoIndex = i;
            break;
        }
    }

    if (iVideoIndex == -1) 
    {
        printf("not find a video stream.\n");
        goto exit;
    }

    pCodecCtx = pFormatCtx->streams[iVideoIndex]->codec;

    nScreen_w = pCodecCtx->width;
    nScreen_h = pCodecCtx->height;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     // 查找解码器
    if (pCodec == nullptr)
    {
        printf("avcodec_find_decode error.\n");
        goto exit;
    }
    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
    {
        printf("avcodec_open2 failed!\n");
        goto exit;
    }
    
	printf("--------------- File Information ----------------\n");
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("-------------------------------------------------\n");

    // 解码准备
    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();
    pPacket = (AVPacket*)av_malloc(sizeof(AVPacket));

    // 申请内存  w*h*1.5(Y:w*h U:w*h/4 V:w*h/4)
    out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));

    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

    pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr);
   

    m_pSDlHandle = new SDLHandle(pCodecCtx->width, pCodecCtx->height);

    if (!m_pSDlHandle->CreateSDLWindow("SDL_TEXT", SDL_WINDOW_OPENGL))
    {
        printf("CreateSDLWindow error:%s\n", SDL_GetError());
        goto exit;
    }

    // 解码
    while (av_read_frame(pFormatCtx,pPacket) >= 0)
    {
        if (pPacket->stream_index == iVideoIndex)   // 视频packet
        {
            iRet = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);
            if (iRet < 0)
            {
                printf("avcodec_decode_video2 error!\n");
                goto exit;
            }

            if (got_picture)
            {
                sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

                m_pSDlHandle->UpdateTexture(pFrame);
            }
        }

        av_free_packet(pPacket);
    }

    // 解码器虽然读完了 但是还有一些帧缓存
    while (true)
    {
        iRet = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket);
        if (iRet < 0)
        {
            break;
        }
        if (!got_picture)
        {
            break;
        }

		sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

		m_pSDlHandle->UpdateTexture(pFrame);
    }

exit:
    if (m_pSDlHandle)
    {
        delete m_pSDlHandle;
        m_pSDlHandle = nullptr;
    }
    if (pSwsCtx)
    {
        sws_freeContext(pSwsCtx);
    }
    if (pFrame)
    {
        av_frame_free(&pFrame);
    }
    if (pFrameYUV)
    {
        av_frame_free(&pFrameYUV);
    }
    
    if (pCodecCtx)
    {
        avcodec_close(pCodecCtx);
    }
    if (pFormatCtx)
    {
        avformat_close_input(&pFormatCtx);
    }

    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值