前段时间一直捣鼓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;
}
我自己最不喜欢别人不加注释,但是自己平常也不怎么写注释。
工程地址