/**
|
* Ò¶º£»Ô
|
* QQȺ121376426
|
* http://blog.yundiantech.com/
|
*/
|
|
#include "VideoPlayer.h"
|
|
#include "Audio/PcmVolumeControl.h"
|
|
#include <stdio.h>
|
#include <QDebug>
|
|
bool VideoPlayer::initPlayer()
|
{
|
mConditon_Video = new Cond;
|
mConditon_Audio = new Cond;
|
|
mPlayerState = VideoPlayer_Stop;
|
|
mVideoPlayerCallBack = nullptr;
|
|
mAudioID = 0;
|
mIsMute = false;
|
mVolume = 1;
|
|
seek_req = 0;
|
seek_pos = 0;
|
seek_flag_audio = 0;
|
seek_flag_video = 0;
|
seek_time = 0;
|
|
mIsNeedPause = false;
|
mIsPause = false;
|
mIsQuit = false;
|
mIsReadFinished = false;
|
mIsReadThreadFinished = false;
|
mIsVideoThreadFinished = false;
|
mIsAudioThreadFinished = false;
|
|
mVideoStartTime = 0;
|
mPauseStartTime = 0;
|
audio_clock = 0;
|
video_clock = 0;
|
|
mVideoStream = nullptr;
|
mAudioStream = nullptr;
|
|
pFormatCtx = nullptr;
|
pCodecCtx = nullptr;
|
pCodec = nullptr;
|
|
aCodecCtx = nullptr;
|
aCodec = nullptr;
|
aFrame = nullptr;
|
aFrame_ReSample = nullptr;
|
swrCtx = nullptr;
|
|
av_register_all(); //³õʼ»¯FFMPEG µ÷ÓÃÁËÕâ¸ö²ÅÄÜÕý³£Ê¹ÓñàÂëÆ÷ºÍ½âÂëÆ÷
|
avformat_network_init(); //Ö§³Ö´ò¿ªÍøÂçÎļþ
|
|
return true;
|
}
|
|
bool VideoPlayer::startPlay(const std::string &filePath, int videoType)
|
{
|
videoType = videoType;
|
if (mPlayerState != VideoPlayer_Stop)
|
{
|
return false;
|
}
|
|
mIsQuit = false;
|
mIsPause = false;
|
|
if (!filePath.empty())
|
mFilePath = filePath;
|
|
//Æô¶¯ÐµÄÏß³ÌʵÏÖ¶ÁÈ¡ÊÓÆµÎļþ
|
std::thread([&](VideoPlayer *pointer)
|
{
|
pointer->readVideoFile();
|
|
}, this).detach();
|
|
return true;
|
|
}
|
|
bool VideoPlayer::replay()
|
{
|
stop();
|
|
startPlay(mFilePath);
|
|
return true;
|
}
|
|
bool VideoPlayer::play()
|
{
|
mIsNeedPause = false;
|
mIsPause = false;
|
|
if (mPlayerState != VideoPlayer_Pause)
|
{
|
return false;
|
}
|
|
uint64_t pauseTime = av_gettime() - mVideoStartTime; //ÔÝÍ£Á˶೤ʱ¼ä
|
mVideoStartTime += pauseTime; //½«ÔÝÍ£µÄʱ¼ä¼Óµ½¿ªÊ¼²¥·ÅµÄʱ¼äÉÏ£¬±£Ö¤Í¬²½²»ÊÜÔÝÍ£µÄÓ°Ïì
|
|
mPlayerState = VideoPlayer_Playing;
|
doPlayerStateChanged(VideoPlayer_Playing, mVideoStream != nullptr, mAudioStream != nullptr);
|
|
return true;
|
}
|
|
bool VideoPlayer::pause()
|
{
|
fprintf(stderr, "%s mIsPause=%d \n", __FUNCTION__, mIsPause);
|
|
mIsPause = true;
|
|
if (mPlayerState != VideoPlayer_Playing)
|
{
|
return false;
|
}
|
|
mPauseStartTime = av_gettime();
|
|
mPlayerState = VideoPlayer_Pause;
|
|
emit doPlayerStateChanged(VideoPlayer_Pause, mVideoStream != nullptr, mAudioStream != nullptr);
|
|
return true;
|
}
|
|
bool VideoPlayer::stop(bool isWait)
|
{
|
if (mPlayerState == VideoPlayer_Stop)
|
{
|
return false;
|
}
|
|
mPlayerState = VideoPlayer_Stop;
|
mIsQuit = true;
|
|
if (isWait)
|
{
|
while(!mIsReadThreadFinished)
|
{
|
mSleep(3);
|
}
|
}
|
|
return true;
|
}
|
|
void VideoPlayer::seek(int64_t pos)
|
{
|
if(!seek_req)
|
{
|
seek_pos = pos;
|
seek_req = 1;
|
}
|
}
|
|
void VideoPlayer::setMute(bool isMute){
|
mIsMute = isMute;
|
}
|
|
float VideoPlayer::getVolume()
|
{
|
return mVolume;
|
}
|
|
void VideoPlayer::setVolume(float value)
|
{
|
mVolume = value;
|
}
|
|
double VideoPlayer::getCurrentTime()
|
{
|
return audio_clock;
|
}
|
|
int64_t VideoPlayer::getTotalTime()
|
{
|
return pFormatCtx->duration;
|
}
|
|
void VideoPlayer::setVideoPlayerCallBack(VideoPlayerCallBack *pointer){
|
mVideoPlayerCallBack=pointer;
|
}
|
|
int VideoPlayer::openSDL()
|
{
|
///´ò¿ªSDL£¬²¢ÉèÖò¥·ÅµÄ¸ñʽΪ:AUDIO_S16LSB Ë«ÉùµÀ£¬44100hz
|
///ºóÆÚʹÓÃffmpeg½âÂëÍêÒôƵºó£¬ÐèÒªÖØ²ÉÑù³ÉºÍÕâ¸öÒ»ÑùµÄ¸ñʽ£¬·ñÔò²¥·Å»áÓÐÔÓÒô
|
SDL_AudioSpec wanted_spec, spec;
|
int wanted_nb_channels = 2;
|
int samplerate = 44100;
|
|
wanted_spec.channels = wanted_nb_channels;
|
wanted_spec.freq = samplerate;
|
wanted_spec.format = AUDIO_S16SYS; // ¾ßÌ庬ÒåÇë²é¿´¡°SDLºê¶¨Ò塱²¿·Ö
|
wanted_spec.silence = 0; // 0ָʾ¾²Òô
|
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // ×Ô¶¨ÒåSDL»º³åÇø´óС
|
wanted_spec.callback = sdlAudioCallBackFunc; // »Øµ÷º¯Êý
|
wanted_spec.userdata = this; // ´«¸øÉÏÃæ»Øµ÷º¯ÊýµÄÍâ´øÊý¾Ý
|
|
int num = SDL_GetNumAudioDevices(0);
|
for (int i=0;i<num;i++)
|
{
|
mAudioID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i,0), false, &wanted_spec, &spec,0);
|
if (mAudioID > 0)
|
{
|
break;
|
}
|
}
|
|
/* ¼ì²éʵ¼ÊʹÓõÄÅäÖ㨱£´æÔÚspec,ÓÉSDL_OpenAudio()Ìî³ä£© */
|
// if (spec.format != AUDIO_S16SYS)
|
if (mAudioID <= 0)
|
{
|
mIsAudioThreadFinished = true;
|
return -1;
|
}
|
fprintf(stderr, "mAudioID=%d\n\n\n\n\n\n", mAudioID);
|
return 0;
|
}
|
|
void VideoPlayer::closeSDL()
|
{
|
if (mAudioID > 0)
|
{
|
SDL_LockAudioDevice(mAudioID);
|
SDL_PauseAudioDevice(mAudioID, 1);
|
SDL_UnlockAudioDevice(mAudioID);
|
|
SDL_CloseAudioDevice(mAudioID);
|
}
|
|
mAudioID = 0;
|
}
|
|
void VideoPlayer::readVideoFile()
|
{
|
///SDL³õʼ»¯ÐèÒª·ÅÈë×ÓÏß³ÌÖУ¬·ñÔòÓÐЩµçÄÔ»áÓÐÎÊÌâ¡£
|
if (SDL_Init(SDL_INIT_AUDIO))
|
{
|
doOpenSdlFailed(-100);
|
fprintf(stderr, "Could not initialize SDL - %s. \n", SDL_GetError());
|
return;
|
}
|
|
mIsReadThreadFinished = false;
|
mIsReadFinished = false;
|
|
const char * file_path = mFilePath.c_str();
|
|
pFormatCtx = nullptr;
|
pCodecCtx = nullptr;
|
pCodec = nullptr;
|
|
aCodecCtx = nullptr;
|
aCodec = nullptr;
|
aFrame = nullptr;
|
|
mAudioStream = nullptr;
|
mVideoStream = nullptr;
|
|
audio_clock = 0;
|
video_clock = 0;
|
|
int audioStream ,videoStream;
|
|
//Allocate an AVFormatContext.
|
pFormatCtx = avformat_alloc_context();
|
|
AVDictionary* opts = NULL;
|
av_dict_set(&opts, "rtsp_transport", "tcp", 0); //ÉèÖÃtcp or udp£¬Ä¬ÈÏÒ»°ãÓÅÏÈtcpÔÙ³¢ÊÔudp
|
av_dict_set(&opts, "stimeout", "60000000", 0);//ÉèÖó¬Ê±3Ãë
|
|
if (avformat_open_input(&pFormatCtx, file_path, nullptr, &opts) != 0)
|
{
|
fprintf(stderr, "can't open the file. \n");
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)
|
{
|
fprintf(stderr, "Could't find stream infomation.\n");
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
videoStream = -1;
|
audioStream = -1;
|
|
///Ñ»·²éÕÒÊÓÆµÖаüº¬µÄÁ÷ÐÅÏ¢£¬
|
for (int i = 0; i < pFormatCtx->nb_streams; i++)
|
{
|
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
|
{
|
videoStream = i;
|
}
|
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0)
|
{
|
audioStream = i;
|
}
|
}
|
|
doTotalTimeChanged(getTotalTime());
|
|
///´ò¿ªÊÓÆµ½âÂëÆ÷£¬²¢Æô¶¯ÊÓÆµÏß³Ì
|
if (videoStream >= 0)
|
{
|
///²éÕÒÊÓÆµ½âÂëÆ÷
|
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
|
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
|
|
if (pCodec == nullptr)
|
{
|
fprintf(stderr, "PCodec not found.\n");
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
///´ò¿ªÊÓÆµ½âÂëÆ÷
|
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
|
{
|
fprintf(stderr, "Could not open video codec.\n");
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
mVideoStream = pFormatCtx->streams[videoStream];
|
|
///´´½¨Ò»¸öÏß³ÌרÃÅÓÃÀ´½âÂëÊÓÆµ
|
std::thread([&](VideoPlayer *pointer)
|
{
|
pointer->decodeVideoThread();
|
|
}, this).detach();
|
|
}
|
|
if (audioStream >= 0)
|
{
|
///²éÕÒÒôƵ½âÂëÆ÷
|
aCodecCtx = pFormatCtx->streams[audioStream]->codec;
|
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
|
|
if (aCodec == NULL)
|
{
|
fprintf(stderr, "ACodec not found.\n");
|
audioStream = -1;
|
}
|
else
|
{
|
///´ò¿ªÒôƵ½âÂëÆ÷
|
if (avcodec_open2(aCodecCtx, aCodec, nullptr) < 0)
|
{
|
fprintf(stderr, "Could not open audio codec.\n");
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
///½âÂëÒôƵÏà¹Ø
|
aFrame = av_frame_alloc();
|
|
|
//ÖØ²ÉÑùÉèÖÃÑ¡Ïî-----------------------------------------------------------start
|
aFrame_ReSample = nullptr;
|
|
//frame->16bit 44100 PCM ͳһÒôƵ²ÉÑù¸ñʽÓë²ÉÑùÂÊ
|
swrCtx = nullptr;
|
|
//ÊäÈëµÄÉùµÀ²¼¾Ö
|
int in_ch_layout;
|
|
//Êä³öµÄÉùµÀ²¼¾Ö
|
int out_ch_layout = av_get_default_channel_layout(audio_tgt_channels); ///AV_CH_LAYOUT_STEREO
|
|
out_ch_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
|
|
/// ÕâÀïÒôƵ²¥·ÅʹÓÃÁ˹̶¨µÄ²ÎÊý
|
/// Ç¿ÖÆ½«ÒôÆµÖØ²ÉÑù³É44100 Ë«ÉùµÀ AV_SAMPLE_FMT_S16
|
/// SDL²¥·ÅÖÐÒ²ÊÇÓÃÁËͬÑùµÄ²¥·Å²ÎÊý
|
//ÖØ²ÉÑùÉèÖÃÑ¡Ïî----------------
|
//ÊäÈëµÄ²ÉÑù¸ñʽ
|
in_sample_fmt = aCodecCtx->sample_fmt;
|
//Êä³öµÄ²ÉÑù¸ñʽ 16bit PCM
|
out_sample_fmt = AV_SAMPLE_FMT_S16;
|
//ÊäÈëµÄ²ÉÑùÂÊ
|
in_sample_rate = aCodecCtx->sample_rate;
|
//ÊäÈëµÄÉùµÀ²¼¾Ö
|
in_ch_layout = aCodecCtx->channel_layout;
|
|
//Êä³öµÄ²ÉÑùÂÊ
|
out_sample_rate = 44100;
|
//Êä³öµÄÉùµÀ²¼¾Ö
|
|
audio_tgt_channels = 2; ///av_get_channel_layout_nb_channels(out_ch_layout);
|
out_ch_layout = av_get_default_channel_layout(audio_tgt_channels); ///AV_CH_LAYOUT_STEREO
|
|
out_ch_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
|
|
/// 2019-5-13Ìí¼Ó
|
/// wav/wmv Îļþ»ñÈ¡µ½µÄaCodecCtx->channel_layoutΪ0»áµ¼ÖºóÃæµÄ³õʼ»¯Ê§°Ü£¬Òò´ËÕâÀïÐèÒª¼Ó¸öÅжϡ£
|
if (in_ch_layout <= 0)
|
{
|
in_ch_layout = av_get_default_channel_layout(aCodecCtx->channels);
|
}
|
|
swrCtx = swr_alloc_set_opts(nullptr, out_ch_layout, out_sample_fmt, out_sample_rate,
|
in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr);
|
|
/** Open the resampler with the specified parameters. */
|
int ret = swr_init(swrCtx);
|
if (ret < 0)
|
{
|
char buff[128]={0};
|
av_strerror(ret, buff, 128);
|
|
fprintf(stderr, "Could not open resample context %s\n", buff);
|
swr_free(&swrCtx);
|
swrCtx = nullptr;
|
doOpenVideoFileFailed();
|
goto end;
|
}
|
|
//´æ´¢pcmÊý¾Ý
|
int out_linesize = out_sample_rate * audio_tgt_channels;
|
|
// out_linesize = av_samples_get_buffer_size(NULL, audio_tgt_channels, av_get_bytes_per_sample(out_sample_fmt), out_sample_fmt, 1);
|
out_linesize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
|
|
|
mAudioStream = pFormatCtx->streams[audioStream];
|
|
///´ò¿ªSDL²¥·ÅÉùÒô
|
int code = openSDL();
|
|
if (code == 0)
|
{
|
SDL_LockAudioDevice(mAudioID);
|
SDL_PauseAudioDevice(mAudioID,0);
|
SDL_UnlockAudioDevice(mAudioID);
|
|
mIsAudioThreadFinished = false;
|
}
|
else
|
{
|
doOpenSdlFailed(code);
|
}
|
}
|
|
}
|
|
// av_dump_format(pFormatCtx, 0, file_path, 0); //Êä³öÊÓÆµÐÅÏ¢
|
|
mPlayerState = VideoPlayer_Playing;
|
doPlayerStateChanged(VideoPlayer_Playing, mVideoStream != nullptr, mAudioStream != nullptr);
|
|
mVideoStartTime = av_gettime();
|
fprintf(stderr, "%s mIsQuit=%d mIsPause=%d \n", __FUNCTION__, mIsQuit, mIsPause);
|
while (1)
|
{
|
if (mIsQuit)
|
{
|
//Í£Ö¹²¥·ÅÁË
|
break;
|
}
|
|
if (seek_req)
|
{
|
int stream_index = -1;
|
int64_t seek_target = seek_pos;
|
|
if (videoStream >= 0)
|
stream_index = videoStream;
|
else if (audioStream >= 0)
|
stream_index = audioStream;
|
|
AVRational aVRational = {1, AV_TIME_BASE};
|
if (stream_index >= 0)
|
{
|
seek_target = av_rescale_q(seek_target, aVRational, pFormatCtx->streams[stream_index]->time_base);
|
}
|
|
if (av_seek_frame(pFormatCtx, stream_index, seek_target, AVSEEK_FLAG_BACKWARD) < 0)
|
{
|
fprintf(stderr, "%s: error while seeking\n",pFormatCtx->filename);
|
}
|
else
|
{
|
if (audioStream >= 0)
|
{
|
AVPacket packet;
|
av_new_packet(&packet, 10);
|
strcpy((char*)packet.data,FLUSH_DATA);
|
clearAudioQuene(); //Çå³ý¶ÓÁÐ
|
inputAudioQuene(packet); //Íù¶ÓÁÐÖдæÈëÓÃÀ´Çå³ýµÄ°ü
|
}
|
|
if (videoStream >= 0)
|
{
|
AVPacket packet;
|
av_new_packet(&packet, 10);
|
strcpy((char*)packet.data,FLUSH_DATA);
|
clearVideoQuene(); //Çå³ý¶ÓÁÐ
|
inputVideoQuene(packet); //Íù¶ÓÁÐÖдæÈëÓÃÀ´Çå³ýµÄ°ü
|
video_clock = 0;
|
}
|
|
mVideoStartTime = av_gettime() - seek_pos;
|
mPauseStartTime = av_gettime();
|
}
|
seek_req = 0;
|
seek_time = seek_pos / 1000000.0;
|
seek_flag_audio = 1;
|
seek_flag_video = 1;
|
|
if (mIsPause)
|
{
|
mIsNeedPause = true;
|
mIsPause = false;
|
}
|
|
}
|
|
//ÕâÀï×öÁ˸öÏÞÖÆ µ±¶ÓÁÐÀïÃæµÄÊý¾Ý³¬¹ýij¸ö´óСµÄʱºò ¾ÍÔÝÍ£¶ÁÈ¡ ·ÀÖ¹Ò»ÏÂ×ӾͰÑÊÓÆµ¶ÁÍêÁË£¬µ¼ÖµĿռä·ÖÅä²»×ã
|
//Õâ¸öÖµ¿ÉÒÔÉÔ΢д´óһЩ
|
if (mAudioPacktList.size() > MAX_AUDIO_SIZE || mVideoPacktList.size() > MAX_VIDEO_SIZE)
|
{
|
mSleep(10);
|
continue;
|
}
|
|
if (mIsPause == true)
|
{
|
mSleep(10);
|
continue;
|
}
|
|
AVPacket packet;
|
if (av_read_frame(pFormatCtx, &packet) < 0)
|
{
|
mIsReadFinished = true;
|
|
if (mIsQuit)
|
{
|
break; //½âÂëÏß³ÌÒ²Ö´ÐÐÍêÁË ¿ÉÒÔÍ˳öÁË
|
}
|
|
mSleep(10);
|
continue;
|
}
|
|
if (packet.stream_index == videoStream)
|
{
|
inputVideoQuene(packet);
|
//ÕâÀïÎÒÃǽ«Êý¾Ý´æÈë¶ÓÁÐ Òò´Ë²»µ÷Óà av_free_packet ÊÍ·Å
|
}
|
else if( packet.stream_index == audioStream )
|
{
|
if (mIsAudioThreadFinished)
|
{ ///SDLûÓдò¿ª£¬ÔòÒôƵÊý¾ÝÖ±½ÓÊÍ·Å
|
av_packet_unref(&packet);
|
}
|
else
|
{
|
inputAudioQuene(packet);
|
//ÕâÀïÎÒÃǽ«Êý¾Ý´æÈë¶ÓÁÐ Òò´Ë²»µ÷Óà av_free_packet ÊÍ·Å
|
}
|
|
}
|
else
|
{
|
// Free the packet that was allocated by av_read_frame
|
av_packet_unref(&packet);
|
}
|
}
|
|
///Îļþ¶ÁÈ¡½áÊø Ìø³öÑ»·µÄÇé¿ö
|
///µÈ´ý²¥·ÅÍê±Ï
|
while (!mIsQuit)
|
{
|
mSleep(100);
|
}
|
|
end:
|
|
clearAudioQuene();
|
clearVideoQuene();
|
|
if (mPlayerState != VideoPlayer_Stop) //²»ÊÇÍⲿµ÷ÓõÄstop ÊÇÕý³£²¥·Å½áÊø
|
{
|
stop();
|
}
|
|
while((mVideoStream != nullptr && !mIsVideoThreadFinished) || (mAudioStream != nullptr && !mIsAudioThreadFinished))
|
{
|
mSleep(10);
|
} //È·±£ÊÓÆµÏ߳̽áÊøºó ÔÙÏú»Ù¶ÓÁÐ
|
|
closeSDL();
|
|
if (swrCtx != nullptr)
|
{
|
swr_free(&swrCtx);
|
swrCtx = nullptr;
|
}
|
|
if (aFrame != nullptr)
|
{
|
av_frame_free(&aFrame);
|
aFrame = nullptr;
|
}
|
|
if (aFrame_ReSample != nullptr)
|
{
|
av_frame_free(&aFrame_ReSample);
|
aFrame_ReSample = nullptr;
|
}
|
|
if (aCodecCtx != nullptr)
|
{
|
avcodec_close(aCodecCtx);
|
aCodecCtx = nullptr;
|
}
|
|
if (pCodecCtx != nullptr)
|
{
|
avcodec_close(pCodecCtx);
|
pCodecCtx = nullptr;
|
}
|
|
avformat_close_input(&pFormatCtx);
|
avformat_free_context(pFormatCtx);
|
|
SDL_Quit();
|
|
doPlayerStateChanged(VideoPlayer_Stop, mVideoStream != nullptr, mAudioStream != nullptr);
|
|
mIsReadThreadFinished = true;
|
|
fprintf(stderr, "%s finished \n", __FUNCTION__);
|
}
|
|
bool VideoPlayer::inputVideoQuene(const AVPacket &pkt)
|
{
|
if (av_dup_packet((AVPacket*)&pkt) < 0)
|
{
|
return false;
|
}
|
|
mConditon_Video->Lock();
|
mVideoPacktList.push_back(pkt);
|
mConditon_Video->Signal();
|
mConditon_Video->Unlock();
|
|
return true;
|
}
|
|
void VideoPlayer::clearVideoQuene()
|
{
|
mConditon_Video->Lock();
|
for (AVPacket pkt : mVideoPacktList)
|
{
|
// av_free_packet(&pkt);
|
av_packet_unref(&pkt);
|
}
|
mVideoPacktList.clear();
|
mConditon_Video->Unlock();
|
}
|
|
bool VideoPlayer::inputAudioQuene(const AVPacket &pkt)
|
{
|
if (av_dup_packet((AVPacket*)&pkt) < 0)
|
{
|
return false;
|
}
|
|
mConditon_Audio->Lock();
|
mAudioPacktList.push_back(pkt);
|
mConditon_Audio->Signal();
|
mConditon_Audio->Unlock();
|
|
return true;
|
}
|
|
void VideoPlayer::clearAudioQuene()
|
{
|
mConditon_Audio->Lock();
|
for (AVPacket pkt : mAudioPacktList)
|
{
|
// av_free_packet(&pkt);
|
av_packet_unref(&pkt);
|
}
|
mAudioPacktList.clear();
|
mConditon_Audio->Unlock();
|
}
|
|
///µ±Ê¹ÓýçÃæÀà¼Ì³ÐÁ˱¾ÀàÖ®ºó£¬ÒÔϺ¯Êý²»»áÖ´ÐÐ
|
|
///´ò¿ªÎļþʧ°Ü
|
void VideoPlayer::doOpenVideoFileFailed(const int &code)
|
{
|
fprintf(stderr, "%s \n", __FUNCTION__);
|
|
if (mVideoPlayerCallBack != nullptr)
|
{
|
mVideoPlayerCallBack->onOpenVideoFileFailed(code);
|
}
|
|
}
|
|
///´ò¿ªsdlʧ°ÜµÄʱºò»Øµ÷´Ëº¯Êý
|
void VideoPlayer::doOpenSdlFailed(const int &code)
|
{
|
fprintf(stderr, "%s \n", __FUNCTION__);
|
|
if (mVideoPlayerCallBack != nullptr)
|
{
|
mVideoPlayerCallBack->onOpenSdlFailed(code);
|
}
|
}
|
|
///»ñÈ¡µ½ÊÓÆµÊ±³¤µÄʱºòµ÷Óô˺¯Êý
|
void VideoPlayer::doTotalTimeChanged(const int64_t &uSec)
|
{
|
fprintf(stderr, "%s \n", __FUNCTION__);
|
|
if (mVideoPlayerCallBack != nullptr)
|
{
|
mVideoPlayerCallBack->onTotalTimeChanged(uSec);
|
}
|
}
|
|
///²¥·ÅÆ÷״̬¸Ä±äµÄʱºò»Øµ÷´Ëº¯Êý
|
void VideoPlayer::doPlayerStateChanged(const VideoPlayerState &state, const bool &hasVideo, const bool &hasAudio)
|
{
|
fprintf(stderr, "%s \n", __FUNCTION__);
|
|
if (mVideoPlayerCallBack != nullptr)
|
{
|
mVideoPlayerCallBack->onPlayerStateChanged(state, hasVideo, hasAudio);
|
}
|
|
}
|
|
///ÏÔʾÊÓÆµÊý¾Ý£¬´Ëº¯Êý²»ÒË×öºÄʱ²Ù×÷£¬·ñÔò»áÓ°Ïì²¥·ÅµÄÁ÷³©ÐÔ¡£
|
void VideoPlayer::doDisplayVideo(const uint8_t *yuv420Buffer, const int &width, const int &height)
|
{
|
// fprintf(stderr, "%s \n", __FUNCTION__);
|
if (mVideoPlayerCallBack != nullptr)
|
{
|
VideoFramePtr videoFrame = std::make_shared<VideoFrame>();
|
|
VideoFrame * ptr = videoFrame.get();
|
|
ptr->initBuffer(width, height);
|
ptr->setYUVbuf(yuv420Buffer);
|
|
mVideoPlayerCallBack->onDisplayVideo(videoFrame);
|
}
|
}
|
|
void VideoPlayer::decodeVideoThread()
|
{
|
fprintf(stderr, "%s start \n", __FUNCTION__);
|
|
mIsVideoThreadFinished = false;
|
|
int videoWidth = 0;
|
int videoHeight = 0;
|
|
double video_pts = 0; //µ±Ç°ÊÓÆµµÄpts
|
double audio_pts = 0; //񙮵pts
|
|
///½âÂëÊÓÆµÏà¹Ø
|
AVFrame *pFrame = nullptr;
|
AVFrame *pFrameYUV = nullptr;
|
uint8_t *yuv420pBuffer = nullptr; //½âÂëºóµÄyuvÊý¾Ý
|
struct SwsContext *imgConvertCtx = nullptr; //ÓÃÓÚ½âÂëºóµÄÊÓÆµ¸ñʽת»»
|
|
AVCodecContext *pCodecCtx = mVideoStream->codec; //ÊÓÆµ½âÂëÆ÷
|
|
pFrame = av_frame_alloc();
|
|
while(1)
|
{
|
if (mIsQuit)
|
{
|
clearVideoQuene(); //Çå¿Õ¶ÓÁÐ
|
break;
|
}
|
|
if (mIsPause == true) //ÅжÏÔÝÍ£
|
{
|
mSleep(10);
|
continue;
|
}
|
|
mConditon_Video->Lock();
|
|
if (mVideoPacktList.size() <= 0)
|
{
|
mConditon_Video->Unlock();
|
if (mIsReadFinished)
|
{
|
//¶ÓÁÐÀïÃæÃ»ÓÐÊý¾ÝÁËÇÒ¶ÁÈ¡Íê±ÏÁË
|
break;
|
}
|
else
|
{
|
mSleep(1); //¶ÓÁÐÖ»ÊÇÔÝʱûÓÐÊý¾Ý¶øÒÑ
|
continue;
|
}
|
}
|
|
AVPacket pkt1 = mVideoPacktList.front();
|
mVideoPacktList.pop_front();
|
|
mConditon_Video->Unlock();
|
|
AVPacket *packet = &pkt1;
|
|
//ÊÕµ½Õâ¸öÊý¾Ý ˵Ã÷¸Õ¸ÕÖ´ÐйýÌø×ª ÏÖÔÚÐèÒª°Ñ½âÂëÆ÷µÄÊý¾Ý Çå³ýÒ»ÏÂ
|
if(strcmp((char*)packet->data, FLUSH_DATA) == 0)
|
{
|
avcodec_flush_buffers(mVideoStream->codec);
|
av_packet_unref(packet);
|
continue;
|
}
|
|
if (avcodec_send_packet(pCodecCtx, packet) != 0)
|
{
|
qDebug("input AVPacket to decoder failed!\n");
|
av_packet_unref(packet);
|
continue;
|
}
|
|
while (0 == avcodec_receive_frame(pCodecCtx, pFrame))
|
{
|
if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque&& *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE)
|
{
|
video_pts = *(uint64_t *) pFrame->opaque;
|
}
|
else if (packet->dts != AV_NOPTS_VALUE)
|
{
|
video_pts = packet->dts;
|
}
|
else
|
{
|
video_pts = 0;
|
}
|
|
video_pts *= av_q2d(mVideoStream->time_base);
|
video_clock = video_pts;
|
//OUTPUT("%s %f \n", __FUNCTION__, video_pts);
|
if (seek_flag_video)
|
{
|
//·¢ÉúÁËÌø×ª ÔòÌø¹ý¹Ø¼üÖ¡µ½Ä¿µÄʱ¼äµÄÕ⼸֡
|
if (video_pts < seek_time)
|
{
|
av_packet_unref(packet);
|
continue;
|
}
|
else
|
{
|
seek_flag_video = 0;
|
}
|
}
|
|
///ÒôÊÓÆµÍ¬²½£¬ÊµÏÖµÄÔÀí¾ÍÊÇ£¬ÅжÏÊÇ·ñµ½ÏÔʾ´Ë֡ͼÏñµÄʱ¼äÁË£¬Ã»µ½ÔòÐÝÃß5ms£¬È»ºó¼ÌÐøÅжÏ
|
while(1)
|
{
|
if (mIsQuit)
|
{
|
break;
|
}
|
|
if (mAudioStream != NULL && !mIsAudioThreadFinished)
|
{
|
if (mIsReadFinished && mAudioPacktList.size() <= 0)
|
{//¶ÁÈ¡ÍêÁË ÇÒÒôƵÊý¾ÝÒ²²¥·ÅÍêÁË ¾ÍÊ£ÏÂÊÓÆµÊý¾ÝÁË Ö±½ÓÏÔʾ³öÀ´ÁË ²»ÓÃͬ²½ÁË
|
break;
|
}
|
|
///ÓÐÒôƵµÄÇé¿öÏ£¬½«ÊÓÆµÍ¬²½µ½ÒôƵ
|
///¸úÒôƵµÄpts×ö¶Ô±È£¬±ÈÊÓÆµ¿ìÔò×öÑÓʱ
|
audio_pts = audio_clock;
|
}
|
else
|
{
|
///ûÓÐÒôƵµÄÇé¿öÏ£¬Ö±½Óͬ²½µ½ÍⲿʱÖÓ
|
audio_pts = (av_gettime() - mVideoStartTime) / 1000000.0;
|
audio_clock = audio_pts;
|
}
|
|
//OUTPUT("%s %f %f \n", __FUNCTION__, video_pts, audio_pts);
|
//Ö÷ÒªÊÇ Ìø×ªµÄʱºò ÎÒÃǰÑvideo_clockÉèÖóÉ0ÁË
|
//Òò´ËÕâÀïÐèÒª¸üÐÂvideo_pts
|
//·ñÔòµ±´ÓºóÃæÌø×ªµ½Ç°ÃæµÄʱºò »á¿¨ÔÚÕâÀï
|
video_pts = video_clock;
|
|
if (video_pts <= audio_pts) break;
|
|
int delayTime = (video_pts - audio_pts) * 1000;
|
|
delayTime = delayTime > 5 ? 5:delayTime;
|
|
if (!mIsNeedPause)
|
{
|
mSleep(delayTime);
|
}
|
}
|
|
if (pCodecCtx->width != videoWidth || pCodecCtx->height != videoHeight)
|
{
|
videoWidth = pFrame->width;
|
videoHeight = pFrame->height;
|
|
if (pFrameYUV != nullptr)
|
{
|
av_free(pFrameYUV);
|
}
|
|
if (yuv420pBuffer != nullptr)
|
{
|
av_free(yuv420pBuffer);
|
}
|
|
if (imgConvertCtx != nullptr)
|
{
|
sws_freeContext(imgConvertCtx);
|
}
|
|
pFrameYUV = av_frame_alloc();
|
|
int yuvSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1); //°´1×Ö½Ú½øÐÐÄÚ´æ¶ÔÆë,µÃµ½µÄÄÚ´æ´óС×î½Ó½üʵ¼Ê´óС
|
// int yuvSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 0); //°´0×Ö½Ú½øÐÐÄÚ´æ¶ÔÆë£¬µÃµ½µÄÄÚ´æ´óСÊÇ0
|
// int yuvSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 4); //°´4×Ö½Ú½øÐÐÄÚ´æ¶ÔÆë£¬µÃµ½µÄÄÚ´æ´óСÉÔ΢´óһЩ
|
|
unsigned int numBytes = static_cast<unsigned int>(yuvSize);
|
yuv420pBuffer = static_cast<uint8_t *>(av_malloc(numBytes * sizeof(uint8_t)));
|
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, yuv420pBuffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
|
|
///ÓÉÓÚ½âÂëºóµÄÊý¾Ý²»Ò»¶¨¶¼ÊÇyuv420p£¬Òò´ËÐèÒª½«½âÂëºóµÄÊý¾Ýͳһת»»³ÉYUV420P
|
imgConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
|
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
|
AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
|
|
}
|
|
sws_scale(imgConvertCtx,
|
(uint8_t const * const *) pFrame->data,
|
pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data,
|
pFrameYUV->linesize);
|
|
// FILE *fp_yuv = fopen("./frame.yuv", "wb");
|
// fwrite(yuv420pBuffer, 1, pCodecCtx->width * pCodecCtx->height * 3 /2, fp_yuv);
|
// fclose(fp_yuv);
|
doDisplayVideo(yuv420pBuffer, pCodecCtx->width, pCodecCtx->height);
|
|
if (mIsNeedPause)
|
{
|
mIsPause = true;
|
mIsNeedPause = false;
|
}
|
|
}
|
av_packet_unref(packet);
|
}
|
|
av_free(pFrame);
|
|
if (pFrameYUV != nullptr)
|
{
|
av_free(pFrameYUV);
|
}
|
|
if (yuv420pBuffer != nullptr)
|
{
|
av_free(yuv420pBuffer);
|
}
|
|
if (imgConvertCtx != nullptr)
|
{
|
sws_freeContext(imgConvertCtx);
|
}
|
|
if (!mIsQuit)
|
{
|
mIsQuit = true;
|
}
|
|
mIsVideoThreadFinished = true;
|
|
fprintf(stderr, "%s finished \n", __FUNCTION__);
|
|
return;
|
}
|
|
void VideoPlayer::sdlAudioCallBackFunc(void *userdata, Uint8 *stream, int len)
|
{
|
VideoPlayer *player = (VideoPlayer*)userdata;
|
player->sdlAudioCallBack(stream, len);
|
}
|
|
void VideoPlayer::sdlAudioCallBack(Uint8 *stream, int len)
|
{
|
int len1, audio_data_size;
|
|
/* lenÊÇÓÉSDL´«ÈëµÄSDL»º³åÇøµÄ´óС£¬Èç¹ûÕâ¸ö»º³åδÂú£¬ÎÒÃǾÍÒ»Ö±ÍùÀïÌî³äÊý¾Ý */
|
while (len > 0)
|
{
|
/* audio_buf_index ºÍ audio_buf_size ±êʾÎÒÃÇ×Ô¼ºÓÃÀ´·ÅÖýâÂë³öÀ´µÄÊý¾ÝµÄ»º³åÇø£¬*/
|
/* ÕâЩÊý¾Ý´ýcopyµ½SDL»º³åÇø£¬ µ±audio_buf_index >= audio_buf_sizeµÄʱºòÒâζ×ÅÎÒ*/
|
/* ÃǵĻº³åΪ¿Õ£¬Ã»ÓÐÊý¾Ý¿É¹©copy£¬ÕâʱºòÐèÒªµ÷ÓÃaudio_decode_frameÀ´½âÂë³ö¸ü
|
/* ¶àµÄèåÊý¾Ý */
|
if (audio_buf_index >= audio_buf_size)
|
{
|
audio_data_size = decodeAudioFrame();
|
|
/* audio_data_size < 0 ±êʾûÄܽâÂë³öÊý¾Ý£¬ÎÒÃÇĬÈϲ¥·Å¾²Òô */
|
if (audio_data_size <= 0)
|
{
|
/* silence */
|
audio_buf_size = 1024;
|
/* ÇåÁ㣬¾²Òô */
|
memset(audio_buf, 0, audio_buf_size);
|
}
|
else
|
{
|
audio_buf_size = audio_data_size;
|
}
|
audio_buf_index = 0;
|
}
|
/* ²é¿´stream¿ÉÓÿռ䣬¾ö¶¨Ò»´Îcopy¶àÉÙÊý¾Ý£¬Ê£ÏµÄÏ´μÌÐøcopy */
|
len1 = audio_buf_size - audio_buf_index;
|
|
if (len1 > len)
|
{
|
len1 = len;
|
}
|
|
if (audio_buf == NULL) return;
|
|
if (mIsMute || mIsNeedPause) //¾²Òô »òÕß ÊÇÔÚÔÝÍ£µÄʱºòÌø×ªÁË
|
{
|
memset(audio_buf + audio_buf_index, 0, len1);
|
}
|
else
|
{
|
PcmVolumeControl::RaiseVolume((char*)audio_buf + audio_buf_index, len1, 1, mVolume);
|
}
|
|
memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
|
|
// SDL_memset(stream, 0x0, len);// make sure this is silence.
|
// SDL_MixAudio(stream, (uint8_t *) audio_buf + audio_buf_index, len1, SDL_MIX_MAXVOLUME);
|
|
// SDL_MixAudio(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, len1, 50);
|
// SDL_MixAudioFormat(stream, (uint8_t * )is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, 50);
|
|
len -= len1;
|
stream += len1;
|
audio_buf_index += len1;
|
|
}
|
|
}
|
|
int VideoPlayer::decodeAudioFrame(bool isBlock)
|
{
|
int audioBufferSize = 0;
|
|
while(1)
|
{
|
if (mIsQuit)
|
{
|
mIsAudioThreadFinished = true;
|
clearAudioQuene(); //Çå¿Õ¶ÓÁÐ
|
break;
|
}
|
|
if (mIsPause == true) //ÅжÏÔÝÍ£
|
{
|
break;
|
}
|
|
mConditon_Audio->Lock();
|
|
if (mAudioPacktList.size() <= 0)
|
{
|
if (isBlock)
|
{
|
mConditon_Audio->Wait();
|
}
|
else
|
{
|
mConditon_Audio->Unlock();
|
break;
|
}
|
}
|
|
AVPacket packet = mAudioPacktList.front();
|
mAudioPacktList.pop_front();
|
qDebug()<<__FUNCTION__<<mAudioPacktList.size();
|
mConditon_Audio->Unlock();
|
|
AVPacket *pkt = &packet;
|
|
/* if update, update the audio clock w/pts */
|
if (pkt->pts != AV_NOPTS_VALUE)
|
{
|
audio_clock = av_q2d(mAudioStream->time_base) * pkt->pts;
|
}
|
|
//ÊÕµ½Õâ¸öÊý¾Ý ˵Ã÷¸Õ¸ÕÖ´ÐйýÌø×ª ÏÖÔÚÐèÒª°Ñ½âÂëÆ÷µÄÊý¾Ý Çå³ýÒ»ÏÂ
|
if(strcmp((char*)pkt->data,FLUSH_DATA) == 0)
|
{
|
avcodec_flush_buffers(mAudioStream->codec);
|
av_packet_unref(pkt);
|
continue;
|
}
|
|
if (seek_flag_audio)
|
{
|
//·¢ÉúÁËÌø×ª ÔòÌø¹ý¹Ø¼üÖ¡µ½Ä¿µÄʱ¼äµÄÕ⼸֡
|
if (audio_clock < seek_time)
|
{
|
continue;
|
}
|
else
|
{
|
seek_flag_audio = 0;
|
}
|
}
|
|
//½âÂëAVPacket->AVFrame
|
int got_frame = 0;
|
int size = avcodec_decode_audio4(aCodecCtx, aFrame, &got_frame, &packet);
|
|
//±£´æÖزÉÑù֮ǰµÄÒ»¸öÉùµÀµÄÊý¾Ý·½·¨
|
//size_t unpadded_linesize = aFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat) aFrame->format);
|
//static FILE * fp = fopen("out.pcm", "wb");
|
//fwrite(aFrame->extended_data[0], 1, unpadded_linesize, fp);
|
|
av_packet_unref(&packet);
|
|
if (got_frame)
|
{
|
/// ffmpeg½âÂëÖ®ºóµÃµ½µÄÒôƵÊý¾Ý²»ÊÇSDLÏëÒªµÄ£¬
|
/// Òò´ËÕâÀïÐèÒªÖØ²ÉÑù³É44100 Ë«ÉùµÀ AV_SAMPLE_FMT_S16
|
if (aFrame_ReSample == NULL)
|
{
|
aFrame_ReSample = av_frame_alloc();
|
}
|
|
if (aFrame_ReSample->nb_samples != aFrame->nb_samples)
|
{
|
aFrame_ReSample->nb_samples = av_rescale_rnd(swr_get_delay(swrCtx, out_sample_rate) + aFrame->nb_samples,
|
out_sample_rate, in_sample_rate, AV_ROUND_UP);
|
|
av_samples_fill_arrays(aFrame_ReSample->data, aFrame_ReSample->linesize, audio_buf, audio_tgt_channels, aFrame_ReSample->nb_samples, out_sample_fmt, 0);
|
|
}
|
|
int len2 = swr_convert(swrCtx, aFrame_ReSample->data, aFrame_ReSample->nb_samples, (const uint8_t**)aFrame->data, aFrame->nb_samples);
|
int resampled_data_size = len2 * audio_tgt_channels * av_get_bytes_per_sample(out_sample_fmt);
|
|
audioBufferSize = resampled_data_size;
|
break;
|
}
|
}
|
|
return audioBufferSize;
|
}
|