前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ffmpeg+sdl播放类

ffmpeg+sdl播放类

作者头像
_gongluck
发布2018-03-08 14:39:23
1K0
发布2018-03-08 14:39:23
举报

前段时间一直捣鼓ffmpeg,觉得还是VLC比较亲切,虽然我现在都不知道VLC怎么用了。

除了雷神的博客,主要参考的还是这个博客:http://blog.yundiantech.com/?log=index

自己在Qt下做了一个功能简单的播放类(播放,暂停,停止)。以后有时间再弄上快进,后退等。

代码语言:javascript
复制
/*********************************************************************************
  *Copyright(C),ForMySelf
  *FileName:GPlayer.h
  *Author:gongluck
  *Version:1.0
  *Date: 2017-06-15
  *Description:播放本地视频文件
  *Others:
  *Function List:
  *History:
     1.Date: 2017-06-15
       Author: gongluck
       Modification: 很早之前做的了,现在整理一下
     2.Date: 2017-06-16
       Author: gongluck
       Modefication: 加上暂停等功能
**********************************************************************************/

#ifndef GPLAYER_H
#define GPLAYER_H

//Qt
#include <QObject>
#include <QImage>

//stl
#include <Queue>
using namespace std;

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"

#include "SDL.h"
#undef main
}

class GPlayer : public QObject
{
    Q_OBJECT
public:
    explicit GPlayer(QObject *parent = 0);
    ~GPlayer();

    bool SetFilename(const char* filename);
    bool PlayFile(const char* filename = NULL);
    bool Stop();

    AVFormatContext* GetAVFormatCtx(){ return m_pFormatCtx; }
    AVCodecContext* GetVCodecCtx() { return m_pVCodecCtx; }
    AVCodecContext* GetACodecCtx() { return m_pACodecCtx; }
    queue<AVPacket>& GetVPacketQue(){ return m_VpacketQue; }
    queue<AVPacket>& GetAPacketQue(){ return m_ApacketQue; }

    int GetVIndex() { return m_vindex; }
    int GetAIndex() { return m_aindex; }
    int GetDstWidth() { return m_dstWidth; }
    int GetDstHeight() { return m_dstHeight; }
    void SetDstWidth(int width) { m_dstWidth = width; }
    void SetDstHeight(int height) { m_dstHeight = height; }
    SDL_AudioSpec GetAudioSpec() { return m_AudioSpec; }
    void SetPts(double pts) { m_pts = pts; }
    double GetPts() { return m_pts; }
    bool GetStopFlag() { return m_bStop; }
    void SetStopFlag(bool bStop) { m_bStop = bStop; }
    bool GetPauseFlag() { return m_bPause; }
    void SetPauseFlag(bool bPause)
    {
        if(bPause)
            SDL_PauseAudio(1);
        else
            SDL_PauseAudio(0);
        m_bPause = bPause;
    }
    bool GetPlayingFlag() { return m_bPlaying; }
    void SetPlayingFlag(bool bPause) { m_bPlaying = bPause; }

signals:
    void sig_picture(QImage);
public slots:

protected:
    bool PreV();
    bool PreA();

private:
    bool m_bInit;
    char* m_filename;
    unsigned int m_filenameLength;

    AVFormatContext* m_pFormatCtx;
    AVCodecContext* m_pVCodecCtx;
    AVCodecContext* m_pACodecCtx;
    AVCodec* m_pVCodec;
    AVCodec* m_pACodec;

    SDL_AudioSpec m_AudioSpec;
    SDL_Thread* m_TReadPacket;
    SDL_Thread* m_TPlayVideo;

    queue<AVPacket> m_VpacketQue;
    queue<AVPacket> m_ApacketQue;

    int m_vindex;
    int m_aindex;
    int m_dstWidth;
    int m_dstHeight;

    double m_pts;
    bool m_bStop;
    bool m_bPause;
    bool m_bPlaying;
};

#endif // GPLAYER_H
代码语言:javascript
复制
/*********************************************************************************
  *Copyright(C),ForMySelf
  *FileName:GPlayer.cpp
  *Author:gongluck
  *Version:1.0
  *Date: 2017-06-15
  *Description:播放本地视频文件
  *Others:
  *Function List:
  *History:
     1.Date: 2017-06-15
       Author: gongluck
       Modification: 很早之前做的了,现在整理一下
     2.Date: 2017-06-16
       Author: gongluck
       Modefication: 加上暂停等功能
**********************************************************************************/

#include "GPlayer.h"

int SDLCALL ReadPacket(void *data)
{
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    GPlayer* player = (GPlayer*)data;
    AVFormatContext* pFormatCtx = player->GetAVFormatCtx();
    queue<AVPacket>& VpacketQue = player->GetVPacketQue();
    queue<AVPacket>& ApacketQue = player->GetAPacketQue();
    int vindex = player->GetVIndex();
    int aindex = player->GetAIndex();

    av_init_packet(packet);
    while(av_read_frame(pFormatCtx,packet) >= 0)
    {
        if(player->GetStopFlag())
            break;
        while(player->GetPauseFlag())
        {
            SDL_Delay(10);
        }
        if(packet->stream_index == vindex)
        {
            while(VpacketQue.size()>1023)
            {
                if(player->GetStopFlag())
                    break;
                SDL_Delay(10);
            }
            VpacketQue.push(*packet);
        }
        else if(packet->stream_index == aindex)
        {
            while(ApacketQue.size()>1023)
            {
                if(player->GetStopFlag())
                    break;
                SDL_Delay(10);
            }
            ApacketQue.push(*packet);
        }
        else
        {
            av_free_packet(packet);
        }
    }
    av_free(packet);
    return 0;
}

int SDLCALL PlayVideo(void *data)
{
    AVFrame frame = {0};
    AVFrame frameRGB = {0};
    AVPacket packet = {0};
    GPlayer* player = (GPlayer*)data;
    AVFormatContext* pFormatCtx = player->GetAVFormatCtx();
    AVCodecContext* pVCodecCtx = player->GetVCodecCtx();
    queue<AVPacket>& VpacketQue = player->GetVPacketQue();
    int vindex = player->GetVIndex();
    int got_picture = 0;
    struct SwsContext* img_convert_ctx = NULL;
    int size = 0;
    uint8_t* buf = NULL;

    img_convert_ctx = sws_getContext(pVCodecCtx->width,pVCodecCtx->height,pVCodecCtx->pix_fmt
                                     ,player->GetDstWidth(),player->GetDstHeight(),AV_PIX_FMT_RGB32,SWS_BICUBIC
                                     ,NULL,NULL,NULL);
    size = avpicture_get_size(AV_PIX_FMT_RGB32,player->GetDstWidth(),player->GetDstHeight());
    buf = (uint8_t*)av_mallocz(sizeof(uint8_t) * size);
    avpicture_fill((AVPicture*)&frameRGB,buf,AV_PIX_FMT_RGB32,player->GetDstWidth(),player->GetDstHeight());//把buf和frameRGB绑定

    while(true)
    {
        if(player->GetStopFlag())
            break;
        else if(player->GetPauseFlag())
        {
            SDL_Delay(10);
            continue;
        }
        if(VpacketQue.size() == 0)
        {
            SDL_Delay(10);
            continue;
        }
        packet = VpacketQue.front();
        if(avcodec_decode_video2(pVCodecCtx,&frame,&got_picture,&packet)<0)
        {
            av_free_packet(&packet);
            VpacketQue.pop();
            continue;
        }
        if(got_picture)
        {
            sws_scale(img_convert_ctx,frame.data,frame.linesize,0,pVCodecCtx->height,frameRGB.data,frameRGB.linesize);
            QImage img(buf,player->GetDstWidth(),player->GetDstHeight(),QImage::Format_RGB32);
//            while(frame.pkt_pts * av_q2d(pFormatCtx->streams[vindex]->time_base) > player->GetPts())
//                SDL_Delay(1);
            if(frame.pkt_pts * av_q2d(pFormatCtx->streams[vindex]->time_base) > player->GetPts())
                  SDL_Delay((frame.pkt_pts*av_q2d(pFormatCtx->streams[vindex]->time_base) - player->GetPts()) *1000);
            emit player->sig_picture(img);
            VpacketQue.pop();
        }
    }

    return 0;
}

void SDLCALL audioCallback(void *userdata, Uint8 * stream, int len)
{
    AVFrame frame = {0};
    AVPacket packet = {0};
    GPlayer* player = (GPlayer*)userdata;
    AVFormatContext* pFormatCtx = player->GetAVFormatCtx();
    AVCodecContext* pACodecCtx = player->GetACodecCtx();
    queue<AVPacket>& ApacketQue = player->GetAPacketQue();
    int aindex = player->GetAIndex();
    int got_picture = 0;
    SDL_AudioSpec audiospec = player->GetAudioSpec();
    static struct SwrContext* swr_convert_ctx = swr_alloc_set_opts(NULL,av_get_default_channel_layout(audiospec.channels),AV_SAMPLE_FMT_S16,audiospec.freq
                                                            ,av_get_default_channel_layout(pACodecCtx->channels),pACodecCtx->sample_fmt,pACodecCtx->sample_rate
                                                            ,NULL,NULL);
    swr_init(swr_convert_ctx);
    int size = av_samples_get_buffer_size(NULL,audiospec.channels,audiospec.samples,AV_SAMPLE_FMT_S16,1);
    uint8_t* buf = (uint8_t*)av_mallocz(sizeof(uint8_t) * size);

    while(ApacketQue.size() == 0)
    {
        if(player->GetStopFlag())
            break;
        SDL_Delay(10);
    }
    packet = ApacketQue.front();
    if( avcodec_decode_audio4(pACodecCtx,&frame,&got_picture,&packet) < 0 )
    {
        av_free_packet(&packet);
        ApacketQue.pop();
        return ;
    }
    if(got_picture)
    {
        swr_convert(swr_convert_ctx,&buf,size,(const uint8_t**)frame.data,frame.nb_samples);
        SDL_memset(stream,0,len);
        SDL_MixAudio(stream,buf,len,SDL_MIX_MAXVOLUME);
        player->SetPts(frame.pkt_pts * av_q2d(pFormatCtx->streams[aindex]->time_base));
    }
    av_free_packet(&packet);
    ApacketQue.pop();
    if(player->GetPauseFlag())
        SDL_PauseAudio(1);
    else
        SDL_PauseAudio(0);
}

GPlayer::GPlayer(QObject *parent) : QObject(parent)
  ,m_bInit(false)
  ,m_filename(NULL)
  ,m_filenameLength(0)
  ,m_pFormatCtx(NULL)
  ,m_vindex(-1)
  ,m_aindex(-1)
  ,m_pVCodecCtx(NULL)
  ,m_pVCodec(NULL)
  ,m_pACodecCtx(NULL)
  ,m_pACodec(NULL)
  ,m_AudioSpec{0}
  ,m_pts(0.0)
  ,m_bPause(false)
  ,m_bStop(false)
{
    //初始化ffmpeg
    av_register_all();

    //初始化SDL
    if(SDL_Init(SDL_INIT_AUDIO) != 0)
    {
        m_bInit = false;
    }
    else
    {
        m_bInit = true;
    }
}

GPlayer::~GPlayer()
{
    if(m_filename != NULL)
    {
        free(m_filename);
        m_filename = NULL;
        m_filenameLength = 0;
    }
}

bool GPlayer::SetFilename(const char* filename)
{
    if(m_filename != NULL)
    {
        free(m_filename);
        m_filename = NULL;
        m_filenameLength = 0;
    }
    m_filenameLength = strlen(filename)+1;
    if(m_filenameLength<=0 || m_filenameLength>260)
        return false;
    m_filename = (char*)malloc(m_filenameLength);
    if(m_filename == NULL)
        return false;
    strncpy(m_filename,filename,m_filenameLength);
    return true;
}

bool GPlayer::PlayFile(const char* filename)
{
    if(filename != NULL)
    {
        if(!SetFilename(filename))
            return false;
    }
    if(m_pFormatCtx != NULL)
        goto ERROR;
    if(avformat_open_input(&m_pFormatCtx,m_filename,NULL,NULL)<0)
        goto ERROR;
    if(avformat_find_stream_info(m_pFormatCtx,NULL)<0)
        goto ERROR;

    if(!PreV() || !PreA())
        goto ERROR;

    SetStopFlag(false);
    SetPauseFlag(false);
    SetPlayingFlag(true);
    m_TReadPacket = SDL_CreateThread(ReadPacket,"ReadPacket",this);
    m_TPlayVideo = SDL_CreateThread(PlayVideo,"PlayVideo",this);
    SDL_PauseAudio(0);

    return true;
ERROR:
    if(m_pFormatCtx != NULL)
        avformat_close_input(&m_pFormatCtx);
    m_pVCodecCtx = m_pACodecCtx = NULL;
    m_pVCodec = m_pACodec = NULL;
    m_vindex = -1;
    m_aindex = -1;
    return false;
}

bool GPlayer::PreV()
{
    for(int i=0;i<m_pFormatCtx->nb_streams;++i)
    {
        if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
            m_aindex = i;
        else if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            m_vindex = i;
    }
    if(m_vindex==-1 || m_aindex==-1)
        goto ERROR;

    m_pVCodecCtx = m_pFormatCtx->streams[m_vindex]->codec;
    m_pVCodec = avcodec_find_decoder(m_pVCodecCtx->codec_id);
    if(avcodec_open2(m_pVCodecCtx,m_pVCodec,NULL) <0)
        goto ERROR;
    m_pACodecCtx = m_pFormatCtx->streams[m_aindex]->codec;
    m_pACodec = avcodec_find_decoder(m_pACodecCtx->codec_id);
    if(avcodec_open2(m_pACodecCtx,m_pACodec,NULL) <0)
        goto ERROR;

    return true;
ERROR:
    return false;
}

bool GPlayer::PreA()
{
    SDL_AudioSpec want = {0};
    want.callback = audioCallback;
    want.channels = m_pACodecCtx->channels;
    want.format = AUDIO_S16SYS;//ffmpeg的格式与SDL的格式 不相同
    want.freq = m_pACodecCtx->sample_rate;
    want.samples = m_pACodecCtx->frame_size;//!!!
    want.userdata = this;
    if(SDL_OpenAudio(&want,&m_AudioSpec) != 0)
        goto ERROR;
    return true;
ERROR:
    return false;
}

bool GPlayer::Stop()
{
    m_bStop = true;
    m_bPlaying = false;
    SDL_CloseAudio();

    if(m_pVCodecCtx != NULL)
        avcodec_close(m_pVCodecCtx);
    if(m_pACodecCtx != NULL)
        avcodec_close(m_pACodecCtx);
    if(m_pFormatCtx != NULL)
        avformat_close_input(&m_pFormatCtx);

    m_pFormatCtx = NULL;
    m_pVCodecCtx = NULL;
    m_pACodecCtx = NULL;
    m_pVCodec = NULL;
    m_pACodec = NULL;

    memset(&m_AudioSpec,0,sizeof(m_AudioSpec));
    m_TReadPacket = NULL;
    m_TPlayVideo = NULL;

    while(!m_VpacketQue.empty())
    {
        av_free_packet(&m_VpacketQue.front());
        m_VpacketQue.pop();
    }
    while(!m_ApacketQue.empty())
    {
        av_free_packet(&m_ApacketQue.front());
        m_ApacketQue.pop();
    }

    m_vindex = -1;
    m_aindex = -1;
//    m_dstWidth = 0;
//    m_dstHeight = 0;

    m_pts = 0;
    return true;
}

我自己最不喜欢别人不加注释,但是自己平常也不怎么写注释。

工程地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年06月16日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档