1
0
mirror of https://gitee.com/drabel/LibQQt.git synced 2025-01-04 10:18:44 +08:00
LibQQt/test/qqtffmpegplayer/FFmpegPlayer.cpp
2017-11-25 17:55:19 +08:00

384 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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();
}