mirror of
https://gitee.com/drabel/LibQQt.git
synced 2025-01-04 10:18:44 +08:00
384 lines
10 KiB
C++
384 lines
10 KiB
C++
#include "FFmpegPlayer.h"
|
||
#include<QDebug>
|
||
#include<iostream>
|
||
#include "qqtdefine.h"
|
||
#if defined (__WIN32__) || defined (__WIN64__)
|
||
#include<windows.h>
|
||
#else
|
||
#define nullptr NULL
|
||
#endif
|
||
using namespace std;
|
||
|
||
bool isquit=false; //
|
||
|
||
// 包队列初始化
|
||
void packet_queue_init(PacketQueue* q)
|
||
{
|
||
memset(q, 0, sizeof(PacketQueue));
|
||
q->last_pkt = NULL;
|
||
q->first_pkt = NULL;
|
||
q->mutex = SDL_CreateMutex();
|
||
q->cond = SDL_CreateCond();
|
||
}
|
||
|
||
// 放入packet到队列中,不带头指针的队列
|
||
int packet_queue_put(PacketQueue*q, AVPacket *pkt)
|
||
{
|
||
AVPacketList *pktl;
|
||
if (av_dup_packet(pkt) < 0)
|
||
return -1;
|
||
|
||
pktl = (AVPacketList*)av_malloc(sizeof(AVPacketList));
|
||
if (!pktl)
|
||
return -1;
|
||
|
||
pktl->pkt = *pkt;
|
||
pktl->next = nullptr;
|
||
|
||
SDL_LockMutex(q->mutex);
|
||
|
||
if (!q->last_pkt) // 队列为空,新插入元素为第一个元素
|
||
q->first_pkt = pktl;
|
||
else // 插入队尾
|
||
q->last_pkt->next = pktl;
|
||
|
||
q->last_pkt = pktl;
|
||
|
||
q->nb_packets++;
|
||
q->size += pkt->size;
|
||
|
||
SDL_CondSignal(q->cond);
|
||
SDL_UnlockMutex(q->mutex);
|
||
return 0;
|
||
}
|
||
|
||
// 从队列中取出packet
|
||
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
|
||
AVPacketList *pkt1;
|
||
int ret;
|
||
|
||
SDL_LockMutex(q->mutex);
|
||
|
||
for (;;)
|
||
{
|
||
if (isquit)
|
||
return -1;
|
||
pkt1 = q->first_pkt;
|
||
if (pkt1) {
|
||
q->first_pkt = pkt1->next;
|
||
if (!q->first_pkt) {
|
||
q->last_pkt = NULL;
|
||
}
|
||
q->nb_packets--;
|
||
q->size -= pkt1->pkt.size;
|
||
*pkt = pkt1->pkt;
|
||
av_free(pkt1);
|
||
ret = 1;
|
||
break;
|
||
} else if (!block) {
|
||
ret = 0;
|
||
break;
|
||
} else {
|
||
SDL_CondWait(q->cond, q->mutex);
|
||
}
|
||
}
|
||
SDL_UnlockMutex(q->mutex);
|
||
return ret;
|
||
}
|
||
|
||
static void packet_queue_flush(PacketQueue *q)
|
||
{
|
||
SDL_LockMutex(q->mutex);
|
||
AVPacketList *pkt, *pkt1;
|
||
for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1)
|
||
{
|
||
pkt1 = pkt->next;
|
||
if(pkt1->pkt.data != (uint8_t *)"FLUSH")
|
||
{
|
||
|
||
}
|
||
av_free_packet(&pkt->pkt);
|
||
av_freep(&pkt);
|
||
|
||
}
|
||
q->last_pkt = NULL;
|
||
q->first_pkt = NULL;
|
||
q->nb_packets = 0;
|
||
q->size = 0;
|
||
SDL_UnlockMutex(q->mutex);
|
||
}
|
||
//////////////////////////////////////////////
|
||
// 解码音频数据
|
||
int audio_decode_frame(mediaState* MS, uint8_t* audio_buf, int buf_size)
|
||
{
|
||
int len1;
|
||
int data_size = 0;
|
||
if (isquit)
|
||
return -1;
|
||
|
||
while (true)
|
||
{
|
||
while (MS->audio_pkt_size > 0)
|
||
{
|
||
if (isquit)
|
||
return -1;
|
||
|
||
int got_frame = 0;
|
||
len1 = avcodec_decode_audio4(MS->acct, MS->frame, &got_frame, &MS->pkt);
|
||
if (len1 < 0) // 出错,跳过
|
||
{
|
||
MS->audio_pkt_size = 0;
|
||
break;
|
||
}
|
||
|
||
MS->audio_pkt_data += len1;
|
||
MS->audio_pkt_size -= len1;
|
||
data_size = 0;
|
||
if (got_frame)
|
||
{
|
||
data_size = av_samples_get_buffer_size(nullptr, MS->acct->channels,MS-> frame->nb_samples, MS->acct->sample_fmt, 1);
|
||
}
|
||
|
||
if (MS->frame->channels > 0 && MS->frame->channel_layout == 0)
|
||
MS->frame->channel_layout = av_get_default_channel_layout(MS->frame->channels);
|
||
else if (MS->frame->channels == 0 && MS->frame->channel_layout > 0)
|
||
MS->frame->channels = av_get_channel_layout_nb_channels(MS->frame->channel_layout);
|
||
|
||
if (MS->swr_ctx)
|
||
{
|
||
swr_free(&MS->swr_ctx);
|
||
MS->swr_ctx = nullptr;
|
||
}
|
||
MS->swr_ctx = swr_alloc_set_opts(nullptr, MS->wanted_frame->channel_layout,
|
||
(AVSampleFormat)MS->wanted_frame->format,
|
||
MS->wanted_frame->sample_rate,
|
||
MS->frame->channel_layout,
|
||
(AVSampleFormat)MS->frame->format,
|
||
MS->frame->sample_rate, 0, nullptr);
|
||
|
||
if (!MS->swr_ctx || swr_init(MS->swr_ctx) < 0)
|
||
{
|
||
cout << "swr_init failed:" << endl;
|
||
break;
|
||
}
|
||
int dst_nb_samples = av_rescale_rnd(swr_get_delay(MS->swr_ctx, MS->frame->sample_rate) + MS->frame->nb_samples, MS->frame->sample_rate, MS->frame->sample_rate, AVRounding(1));
|
||
int len2 = swr_convert(MS->swr_ctx, &audio_buf, dst_nb_samples,(const uint8_t**)MS->frame->data, MS->frame->nb_samples);
|
||
if (len2 < 0)
|
||
{
|
||
cout << "swr_convert failed\n";
|
||
break;
|
||
}
|
||
return MS->wanted_frame->channels * len2 * av_get_bytes_per_sample((AVSampleFormat)MS->wanted_frame->format);
|
||
|
||
if (data_size <= 0)
|
||
continue; // No data yet,get more frames
|
||
|
||
return data_size; // we have data,return it and come back for more later
|
||
}
|
||
if (MS->pkt.data)
|
||
av_free_packet(&MS->pkt);
|
||
|
||
if (packet_queue_get(&MS->audioq, &MS->pkt, true) < 0)
|
||
return -1;
|
||
|
||
MS->audio_pkt_data =MS->pkt.data;
|
||
MS->audio_pkt_size = MS->pkt.size;
|
||
}
|
||
}
|
||
|
||
// 解码后的回调函数
|
||
void audio_callback(void* userdata, Uint8* stream, int len)
|
||
{
|
||
mediaState* MS = (mediaState*)userdata;
|
||
int len1, audio_size;
|
||
uint8_t audio_buff[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
|
||
SDL_memset(stream, 0, len);
|
||
if (isquit)
|
||
return;
|
||
while (len > 0)
|
||
{
|
||
if (MS->audio_buf_index >= MS->audio_buf_size)
|
||
{
|
||
audio_size = audio_decode_frame(MS, audio_buff, sizeof(audio_buff));
|
||
if (isquit)
|
||
return;
|
||
if (audio_size < 0)
|
||
{
|
||
MS->audio_buf_size = 1024;
|
||
memset(audio_buff, 0, MS->audio_buf_size);
|
||
}
|
||
else
|
||
MS->audio_buf_size = audio_size;
|
||
|
||
MS->audio_buf_index = 0;
|
||
}
|
||
len1 = MS->audio_buf_size - MS->audio_buf_index;
|
||
if (len1 > len)
|
||
len1 = len;
|
||
|
||
SDL_MixAudio(stream, audio_buff + MS->audio_buf_index, len, SDL_MIX_MAXVOLUME);
|
||
|
||
len -= len1;
|
||
stream += len1;
|
||
MS->audio_buf_index += len1;
|
||
}
|
||
}
|
||
|
||
void FFmpegPlayer::FreeAllocSpace() //存在内在
|
||
{
|
||
pause();
|
||
SDL_CloseAudio();//Close SDL
|
||
SDL_Quit();
|
||
|
||
if(m_MS.wanted_frame)
|
||
av_frame_free(&m_MS.wanted_frame);
|
||
if(m_MS.frame)
|
||
av_frame_free(&m_MS.frame);
|
||
if(m_MS.acct)
|
||
avcodec_close(m_MS.acct);
|
||
if(m_MS.afct)
|
||
avformat_close_input(&m_MS.afct);
|
||
if(m_MS.swr_ctx)
|
||
swr_free(&m_MS.swr_ctx);
|
||
if(m_MS.audio_pkt_data)
|
||
av_freep(m_MS.audio_pkt_data);
|
||
if(m_MS.audioq.first_pkt)
|
||
packet_queue_flush(&m_MS.audioq);
|
||
m_MS={0};//自动对能初始化为0的都初始化了
|
||
//使用memset(m_MS,0,sizeof(m_MS));会出现bug!!
|
||
}
|
||
|
||
|
||
FFmpegPlayer::FFmpegPlayer(QObject *parent) : QThread(parent)
|
||
{
|
||
av_register_all();
|
||
avformat_network_init();
|
||
m_MS={0};//自动对能初始化为0的都初始化了
|
||
//使用memset(m_MS,0,sizeof(m_MS));会出现bug!!
|
||
}
|
||
|
||
void FFmpegPlayer::setMedia(const QString url)
|
||
{
|
||
stop();
|
||
m_url=url;
|
||
start();
|
||
setPriority(QThread::HighestPriority);
|
||
}
|
||
|
||
void FFmpegPlayer::stop()
|
||
{
|
||
isquit=1;
|
||
QQTSleep(100);
|
||
m_url="";
|
||
}
|
||
|
||
void FFmpegPlayer::pause()
|
||
{
|
||
SDL_PauseAudio(1);
|
||
}
|
||
|
||
void FFmpegPlayer::play()
|
||
{
|
||
SDL_PauseAudio(0);
|
||
}
|
||
|
||
playerStatus FFmpegPlayer::getPlayerStatus()
|
||
{
|
||
if(SDL_AUDIO_PAUSED==SDL_GetAudioStatus())
|
||
return pausing;
|
||
if(SDL_AUDIO_PLAYING==SDL_GetAudioStatus())
|
||
return playing;
|
||
return stopping;
|
||
}
|
||
|
||
|
||
void FFmpegPlayer::run()
|
||
{
|
||
isquit=0;
|
||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
|
||
|
||
// 读取文件头,将格式相关信息存放在AVFormatContext结构体中
|
||
if (avformat_open_input(&m_MS.afct, m_url.toUtf8().data(), nullptr, nullptr) != 0)
|
||
{
|
||
FreeAllocSpace();
|
||
return; // 打开失败
|
||
}
|
||
// 检测文件的流信息
|
||
if (avformat_find_stream_info(m_MS.afct, nullptr) < 0)
|
||
{
|
||
FreeAllocSpace();
|
||
return; // 没有检测到流信息 stream infomation
|
||
}
|
||
//查找第一个视频流 video stream
|
||
int audioStream = -1;
|
||
for (unsigned int i = 0; i < m_MS.afct->nb_streams; i++)
|
||
{
|
||
if (m_MS.afct->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
||
{
|
||
audioStream = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 3. 根据读取到的流信息查找相应的解码器并打开
|
||
if (audioStream == -1)
|
||
{
|
||
FreeAllocSpace();
|
||
return; // 没有检测到流信息 stream infomation
|
||
}
|
||
m_MS.acct = m_MS.afct->streams[audioStream]->codec; // codec context
|
||
AVCodec* pCodec = avcodec_find_decoder(m_MS.acct->codec_id);
|
||
|
||
if (!pCodec)
|
||
{
|
||
cout << "Unsupported codec!" << endl;
|
||
FreeAllocSpace();
|
||
return;
|
||
}
|
||
// Set audio settings from codec info
|
||
SDL_AudioSpec wanted_spec, spec;
|
||
wanted_spec.freq = m_MS.acct->sample_rate;
|
||
wanted_spec.format = AUDIO_S16SYS;
|
||
wanted_spec.channels = m_MS.acct->channels;
|
||
wanted_spec.silence = 0;
|
||
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
|
||
wanted_spec.callback = audio_callback;
|
||
wanted_spec.userdata = &m_MS;
|
||
// CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||
if (SDL_OpenAudio(&wanted_spec, &spec) < 0)
|
||
{
|
||
cout << "Open audio failed:" << SDL_GetError() << endl;
|
||
FreeAllocSpace();
|
||
return ;
|
||
}
|
||
m_MS.wanted_frame=av_frame_alloc();
|
||
m_MS.frame=av_frame_alloc();
|
||
|
||
m_MS.wanted_frame->format = AV_SAMPLE_FMT_S16;
|
||
m_MS.wanted_frame->sample_rate = spec.freq;
|
||
m_MS.wanted_frame->channel_layout = av_get_default_channel_layout(spec.channels);
|
||
m_MS.wanted_frame->channels = spec.channels;
|
||
|
||
avcodec_open2(m_MS.acct, pCodec, nullptr);
|
||
packet_queue_init(&m_MS.audioq);
|
||
SDL_PauseAudio(0);
|
||
|
||
AVPacket packet;
|
||
while (true) //这里有一个顺序!先判断退出,再 读 再写入
|
||
{
|
||
SDL_Delay(10);
|
||
if (isquit)
|
||
break;
|
||
if (m_MS.audioq.size > MAX_AUDIO_SIZE)
|
||
{
|
||
continue; //这个才是关键!
|
||
}
|
||
av_read_frame(m_MS.afct, &packet);
|
||
if (packet.stream_index == audioStream)
|
||
packet_queue_put(&m_MS.audioq, &packet);
|
||
else
|
||
av_free_packet(&packet);
|
||
}
|
||
FreeAllocSpace();
|
||
}
|