Files
ANSCORE/modules/ANSCV/VideoPlayer.cpp

1094 lines
26 KiB
C++

/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
//#include "utils.h"
#include "media_util.h"
#include "media_parse.h"
#include "media_codec.h"
#include "h264.h"
#include "h265.h"
#include "VideoPlayer.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/avstring.h>
#include <libavutil/base64.h>
#include <libavutil/imgutils.h>
}
#if __WINDOWS_OS__
//#include "video_render_d3d.h"
//#include "video_render_gdi.h"
#include "audio_play_win.h"
#elif defined(IOS)
#include "video_render_sdl.h"
#include "audio_play_mac.h"
#elif __LINUX_OS__
#include "video_render_sdl.h"
#include "audio_play_qt.h"
#endif
void VideoDecoderCallback(AVFrame* frame, void* userdata)
{
CVideoPlayer* pPlayer = (CVideoPlayer*)userdata;
pPlayer->onVideoFrame(frame);
}
void AudioDecoderCallback(AVFrame* frame, void* userdata)
{
CVideoPlayer* pPlayer = (CVideoPlayer*)userdata;
pPlayer->onAudioFrame(frame);
}
void* AudioPlayThread(void* argv)
{
CVideoPlayer* pPlayer = (CVideoPlayer*)argv;
pPlayer->audioPlayThread();
return NULL;
}
void* VideoPlayThread(void* argv)
{
CVideoPlayer* pPlayer = (CVideoPlayer*)argv;
pPlayer->videoPlayThread();
return NULL;
}
CVideoPlayer::CVideoPlayer():
m_bVideoInited(FALSE)
, m_bAudioInited(FALSE)
, m_pVideoDecoder(NULL)
, m_pAudioDecoder(NULL)
//, m_pVideoRender(NULL)
, m_pAudioPlay(NULL)
, m_bPlaying(FALSE)
, m_bPaused(FALSE)
, m_bSizeChanged(FALSE)
//, m_nRenderMode(RENDER_MODE_KEEP)
, m_nHWDecoding(HW_DECODING_DISABLE) // Software decode by default — saves VRAM
, m_nDstVideoFmt(AV_PIX_FMT_YUV420P)
, m_bUpdown(FALSE)
, m_bSnapshot(FALSE)
, m_nSnapVideoFmt(VIDEO_FMT_BGR24)
, m_nVideoCodec(VIDEO_CODEC_NONE)
, m_nAudioCodec(AUDIO_CODEC_NONE)
, m_nSampleRate(0)
, m_nChannel(0)
, m_nBitPerSample(0)
, m_pSnapFrame(NULL)
, m_pRenderFrame(NULL)
, m_bRecording(FALSE)
, m_bNalFlag(FALSE)
, m_pAviCtx(NULL)
, m_pAudioListMutex(NULL)
, m_audioPlayFlag(FALSE)
, m_audioPlayThread(0)
, m_pVideoListMutex(NULL)
, m_videoPlayFlag(FALSE)
, m_videoPlayThread(0)
, m_nLastAudioPts(AV_NOPTS_VALUE)
, m_lastAudioTS(0)
{
m_pRecordMutex = sys_os_create_mutex();
memset(&m_h26XParamSets, 0, sizeof(H26XParamSets));
memset(&m_audioClock, 0, sizeof(HTCLOCK));
memset(&m_videoClock, 0, sizeof(HTCLOCK));
}
CVideoPlayer::~CVideoPlayer()
{
close();
}
BOOL CVideoPlayer::open(std::string fileName)
{
m_sFileName = fileName;
return TRUE;
}
void CVideoPlayer::close()
{
closeVideo();
closeAudio();
if (m_pSnapFrame)
{
av_frame_free(&m_pSnapFrame);
}
if (m_pRenderFrame)
{
av_frame_free(&m_pRenderFrame);
}
stopRecord();
sys_os_destroy_sig_mutex(m_pRecordMutex);
m_pRecordMutex = NULL;
}
void CVideoPlayer::setVolume(int volume)
{
if (m_pAudioPlay)
{
m_pAudioPlay->setVolume(volume);
}
}
void CVideoPlayer::snapshot(int videofmt)
{
m_bSnapshot = TRUE;
m_nSnapVideoFmt = videofmt;
}
BOOL CVideoPlayer::record(std::string baseName)
{
if (m_bRecording)
{
return TRUE;
}
//std::string path = getRecordPath();
std::string file = baseName;// path + "/" + getTempFile(baseName, ".avi");
m_sBaseName = baseName;
m_pAviCtx = avi_write_open(file.c_str());
if (NULL == m_pAviCtx)
{
log_print(HT_LOG_ERR, "%s, avi_write_open failed. %s\r\n",
__FUNCTION__, file.c_str());
return FALSE;
}
if (!onRecord())
{
avi_write_close(m_pAviCtx);
m_pAviCtx = NULL;
return FALSE;
}
m_bRecording = TRUE;
return m_bRecording;
}
void CVideoPlayer::stopRecord()
{
sys_os_mutex_enter(m_pRecordMutex);
m_bRecording = FALSE;
m_bNalFlag = FALSE;
memset(&m_h26XParamSets, 0, sizeof(H26XParamSets));
if (m_pAviCtx)
{
avi_write_close(m_pAviCtx);
m_pAviCtx = NULL;
}
sys_os_mutex_leave(m_pRecordMutex);
}
void CVideoPlayer::recordVideo(uint8* data, int len, uint32 ts, uint16 seq)
{
int codec = VIDEO_CODEC_NONE;
if (!memcmp(m_pAviCtx->v_fcc, "H264", 4))
{
codec = VIDEO_CODEC_H264;
}
else if (!memcmp(m_pAviCtx->v_fcc, "H265", 4))
{
codec = VIDEO_CODEC_H265;
}
if ((VIDEO_CODEC_H264 == codec || VIDEO_CODEC_H265 == codec) && !m_bNalFlag)
{
if (avc_get_h26x_paramsets(data, len, codec, &m_h26XParamSets))
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
m_bNalFlag = 1;
}
}
recordVideoEx(data, len, ts, seq);
if (recordSwitchCheck())
{
recordFileSwitch();
}
}
void CVideoPlayer::recordVideoEx(uint8* data, int len, uint32 ts, uint16 seq)
{
AVICTX* p_avictx = m_pAviCtx;
if (p_avictx->v_width == 0 || p_avictx->v_height == 0)
{
int codec = VIDEO_CODEC_NONE;
if (memcmp(p_avictx->v_fcc, "H264", 4) == 0)
{
codec = VIDEO_CODEC_H264;
}
else if (memcmp(p_avictx->v_fcc, "H265", 4) == 0)
{
codec = VIDEO_CODEC_H265;
}
else if (memcmp(p_avictx->v_fcc, "JPEG", 4) == 0)
{
codec = VIDEO_CODEC_JPEG;
}
else if (memcmp(p_avictx->v_fcc, "MP4V", 4) == 0)
{
codec = VIDEO_CODEC_MP4;
}
avc_parse_video_size(codec, data, len, &p_avictx->v_width, &p_avictx->v_height);
if (p_avictx->v_width && p_avictx->v_height)
{
avi_update_header(p_avictx);
}
}
int key = 0;
if (memcmp(p_avictx->v_fcc, "H264", 4) == 0)
{
uint8 nalu_t = (data[4] & 0x1F);
key = (nalu_t == 5 || nalu_t == 7 || nalu_t == 8);
}
else if (memcmp(p_avictx->v_fcc, "H265", 4) == 0)
{
uint8 nalu_t = (data[4] >> 1) & 0x3F;
key = ((nalu_t >= 16 && nalu_t <= 21) || nalu_t == 32 || nalu_t == 33 || nalu_t == 34);
}
else if (memcmp(p_avictx->v_fcc, "MP4V", 4) == 0)
{
key = 1;
}
else if (memcmp(p_avictx->v_fcc, "JPEG", 4) == 0)
{
key = 1;
}
avi_write_video(p_avictx, data, len, ts, key);
}
void CVideoPlayer::recordAudio(uint8* data, int len, uint32 ts, uint16 seq)
{
AVICTX* p_avictx = m_pAviCtx;
avi_write_audio(p_avictx, data, len, ts);
if (recordSwitchCheck())
{
recordFileSwitch();
}
}
BOOL CVideoPlayer::recordSwitchCheck()
{
uint64 tlen = avi_get_file_length(m_pAviCtx);
uint32 mtime = avi_get_media_time(m_pAviCtx);
uint32 recordSize = 0;// getRecordSize();
if (recordSize == 0)
{
recordSize = 1048576; // max 1G file size
}
// Switch according to the recording size
if (tlen > recordSize * 1024)
{
return TRUE;
}
uint32 recordTime = 0;// getRecordTime();
// Switch according to the recording duration
if (recordTime > 0 && mtime > recordTime * 1000)
{
return TRUE;
}
return FALSE;
}
void CVideoPlayer::recordFileSwitch()
{
AVICTX* p_ctx;
AVICTX* p_oldctx = m_pAviCtx;
//std::string path = getRecordPath();
std::string file = m_sBaseName;// path + "/" + getTempFile(m_sBaseName, ".avi");
p_ctx = avi_write_open(file.c_str());
if (NULL == p_ctx)
{
return;
}
p_ctx->ctxf_video = p_oldctx->ctxf_video;
p_ctx->ctxf_audio = p_oldctx->ctxf_audio;
if (p_ctx->ctxf_video)
{
avi_calc_fps(p_oldctx);
avi_set_video_info(p_ctx, p_oldctx->v_fps, p_oldctx->v_width, p_oldctx->v_height, p_oldctx->v_fcc);
avi_set_video_extra_info(p_ctx, p_oldctx->v_extra, p_oldctx->v_extra_len);
}
if (p_ctx->ctxf_audio)
{
avi_set_audio_info(p_ctx, p_oldctx->a_chns, p_oldctx->a_rate, p_oldctx->a_fmt);
avi_set_audio_extra_info(p_ctx, p_oldctx->a_extra, p_oldctx->a_extra_len);
}
avi_write_close(p_oldctx);
avi_update_header(p_ctx);
m_pAviCtx = p_ctx;
if (m_h26XParamSets.vps_size > 0 ||
m_h26XParamSets.sps_size > 0 ||
m_h26XParamSets.pps_size > 0)
{
avi_write_nalu(m_pAviCtx,
m_h26XParamSets.vps, m_h26XParamSets.vps_size,
m_h26XParamSets.sps, m_h26XParamSets.sps_size,
m_h26XParamSets.pps, m_h26XParamSets.pps_size);
}
}
BOOL CVideoPlayer::openVideo(enum AVCodecID codec, uint8* extradata, int extradata_size)
{
if (m_bVideoInited)
{
return TRUE;
}
m_pVideoDecoder = new CVideoDecoder();
if (m_pVideoDecoder)
{
m_bVideoInited = m_pVideoDecoder->init(codec, extradata, extradata_size, m_nHWDecoding);
}
if (m_bVideoInited)
{
m_pVideoDecoder->setCallback(VideoDecoderCallback, this);
m_pVideoListMutex = sys_os_create_mutex();
m_videoPlayFlag = TRUE;
m_videoPlayThread = sys_os_create_thread((void*)VideoPlayThread, this);
}
m_nVideoCodec = to_video_codec(codec);
return m_bVideoInited;
}
BOOL CVideoPlayer::openVideo(int codec, uint8* extradata, int extradata_size)
{
return openVideo(to_video_avcodecid(codec), extradata, extradata_size);
}
void CVideoPlayer::closeVideo()
{
m_videoPlayFlag = FALSE;
// Wait for the video playback thread to exit
while (m_videoPlayThread)
{
usleep(10 * 1000);
}
if (m_pVideoDecoder)
{
delete m_pVideoDecoder;
m_pVideoDecoder = NULL;
}
/* if (m_pVideoRender)
{
delete m_pVideoRender;
m_pVideoRender = NULL;
}*/
// Released video frame
while (m_videoFrameList.size() > 0)
{
AVFrame* pFrame = m_videoFrameList.front();
m_videoFrameList.pop_front();
av_frame_free(&pFrame);
}
if (m_pVideoListMutex)
{
sys_os_destroy_sig_mutex(m_pVideoListMutex);
m_pVideoListMutex = NULL;
}
m_bVideoInited = FALSE;
}
BOOL CVideoPlayer::openAudio(enum AVCodecID codec, int samplerate, int channels, int bitpersample)
{
if (m_bAudioInited)
{
return TRUE;
}
m_pAudioDecoder = new CAudioDecoder();
if (m_pAudioDecoder)
{
m_bAudioInited = m_pAudioDecoder->init(codec, samplerate, channels, bitpersample);
}
if (m_bAudioInited)
{
m_pAudioDecoder->setCallback(AudioDecoderCallback, this);
#if __WINDOWS_OS__
m_pAudioPlay = new CWAudioPlay();
#elif defined(IOS)
m_pAudioPlay = new CMAudioPlay();
#elif __LINUX_OS__
m_pAudioPlay = new CQAudioPlay();
#endif
if (m_pAudioPlay)
{
m_pAudioPlay->startPlay(samplerate, channels);
}
m_pAudioListMutex = sys_os_create_mutex();
m_audioPlayFlag = TRUE;
m_audioPlayThread = sys_os_create_thread((void*)AudioPlayThread, this);
}
m_nAudioCodec = to_audio_codec(codec);
m_nSampleRate = samplerate;
m_nChannel = channels;
m_nBitPerSample = bitpersample;
return m_bAudioInited;
}
BOOL CVideoPlayer::openAudio(int codec, int samplerate, int channels, int bitpersample)
{
return openAudio(to_audio_avcodecid(codec), samplerate, channels, bitpersample);
}
void CVideoPlayer::closeAudio()
{
m_audioPlayFlag = FALSE;
// Wait for the audio playback thread to exit
while (m_audioPlayThread)
{
usleep(10 * 1000);
}
if (m_pAudioDecoder)
{
delete m_pAudioDecoder;
m_pAudioDecoder = NULL;
}
if (m_pAudioPlay)
{
delete m_pAudioPlay;
m_pAudioPlay = NULL;
}
// Released audio frame
while (m_audioFrameList.size() > 0)
{
AVFrame* pFrame = m_audioFrameList.front();
m_audioFrameList.pop_front();
av_frame_free(&pFrame);
}
if (m_pAudioListMutex)
{
sys_os_destroy_sig_mutex(m_pAudioListMutex);
m_pAudioListMutex = NULL;
}
m_bAudioInited = FALSE;
}
void CVideoPlayer::setWindowSize(int size)
{
m_bSizeChanged = TRUE;
m_size = size;
}
int CVideoPlayer::getVideoWidth()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getWidth();
}
return 0;
}
int CVideoPlayer::getVideoHeight()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getHeight();
}
return 0;
}
double CVideoPlayer::getFrameRate()
{
if (m_pVideoDecoder)
{
return m_pVideoDecoder->getFrameRate();
}
return 0;
}
void CVideoPlayer::playVideo(uint8* data, int len, uint32 ts, uint16 seq)
{
if (m_bRecording)
{
sys_os_mutex_enter(m_pRecordMutex);
recordVideo(data, len, ts, seq);
sys_os_mutex_leave(m_pRecordMutex);
}
updateClock(&m_videoClock, ts, getVideoClock());
if (m_bVideoInited)
{
m_pVideoDecoder->decode(data, len, m_videoClock.SyncTime.tv_sec * 1000000 + m_videoClock.SyncTime.tv_usec);
}
}
void CVideoPlayer::playAudio(uint8* data, int len, uint32 ts, uint16 seq)
{
if (m_bRecording)
{
sys_os_mutex_enter(m_pRecordMutex);
recordAudio(data, len, ts, seq);
sys_os_mutex_leave(m_pRecordMutex);
}
updateClock(&m_audioClock, ts, getAudioClock());
if (m_bAudioInited)
{
m_pAudioDecoder->decode(data, len, m_audioClock.SyncTime.tv_sec * 1000000 + m_audioClock.SyncTime.tv_usec);
}
}
void CVideoPlayer::updateClock(HTCLOCK* clock, uint32 ts, int frequency)
{
if (ts == 0)
{
return;
}
if (clock->SyncTime.tv_sec == 0 && clock->SyncTime.tv_usec == 0)
{
clock->SyncTimestamp = ts;
gettimeofday(&clock->SyncTime, NULL);
}
int timestampDiff = ts - clock->SyncTimestamp;
// Divide this by the timestamp frequency to get real time:
double timeDiff = timestampDiff / (double)frequency;
uint32 const million = 1000000;
uint32 seconds, uSeconds;
if (timeDiff >= 0.0)
{
seconds = clock->SyncTime.tv_sec + (uint32)(timeDiff);
uSeconds = clock->SyncTime.tv_usec + (uint32)((timeDiff - (uint32)timeDiff) * million);
if (uSeconds >= million)
{
uSeconds -= million;
++seconds;
}
}
else
{
timeDiff = -timeDiff;
seconds = clock->SyncTime.tv_sec - (uint32)(timeDiff);
uSeconds = clock->SyncTime.tv_usec - (uint32)((timeDiff - (uint32)timeDiff) * million);
if ((int)uSeconds < 0)
{
uSeconds += million;
--seconds;
}
}
// Save these as the new synchronization timestamp & time:
clock->SyncTimestamp = ts;
clock->SyncTime.tv_sec = seconds;
clock->SyncTime.tv_usec = uSeconds;
}
BOOL CVideoPlayer::initFrame(AVFrame*& frame, int width, int height, AVPixelFormat pixfmt)
{
if (width == 0 || height == 0 || pixfmt == AV_PIX_FMT_NONE)
{
return FALSE;
}
if (NULL == frame || frame->width != width || frame->height != height || frame->format != pixfmt)
{
if (frame)
{
av_frame_free(&frame);
}
frame = av_frame_alloc();
if (NULL == frame)
{
return FALSE;
}
frame->format = pixfmt;
frame->width = width;
frame->height = height;
if (0 != av_frame_get_buffer(frame, 0))
{
av_frame_free(&frame);
return FALSE;
}
av_frame_make_writable(frame);
}
return TRUE;
}
//BOOL CVideoPlayer::initVideoRender(int width, int height)
//{
// BOOL init = FALSE;
//
//#if __WINDOWS_OS__
// m_pVideoRender = new CD3DVideoRender();
//#elif __LINUX_OS__
// m_pVideoRender = new CSDLVideoRender();
//#endif
// if (m_pVideoRender)
// {
// init = m_pVideoRender->init(m_nVideoWnd, width, height, VIDEO_FMT_YUV420P);
//#ifdef IOS
// if (init)
// {
// m_pVideoRender->setWindowTitle(m_sFileName.toStdString().c_str());
// }
//#endif
// }
//
//#if __WINDOWS_OS__
// if (!init)
// {
// if (m_pVideoRender)
// {
// delete m_pVideoRender;
// }
//
// m_pVideoRender = new CGDIVideoRender();
// if (m_pVideoRender)
// {
// init = m_pVideoRender->init(m_nVideoWnd, width, height, VIDEO_FMT_BGR24);
// if (init)
// {
// m_bUpdown = TRUE;
// m_nDstVideoFmt = to_avpixelformat(VIDEO_FMT_BGR24);
// log_print(HT_LOG_INFO, "%s, use GDI to render video\r\n", __FUNCTION__);
// }
// else
// {
// delete m_pVideoRender;
// m_pVideoRender = NULL;
// }
// }
// }
// else
// {
// log_print(HT_LOG_INFO, "%s, use D3D to render video\r\n", __FUNCTION__);
// }
//#endif
//
// return init;
//}
BOOL CVideoPlayer::doSnapshot(AVFrame* frame)
{
if (!initFrame(m_pSnapFrame, frame->width, frame->height, to_avpixelformat(m_nSnapVideoFmt)))
{
return FALSE;
}
if (NULL == convertFrame(frame, m_pSnapFrame, FALSE))
{
return FALSE;
}
//emit snapshoted(m_pSnapFrame);
return TRUE;
}
AVFrame* CVideoPlayer::convertFrame(AVFrame* srcframe, AVFrame* dstframe, BOOL updown)
{
if (NULL == srcframe || NULL == dstframe)
{
return NULL;
}
SwsContext* swsctx = sws_getContext(srcframe->width, srcframe->height, (enum AVPixelFormat)srcframe->format,
srcframe->width, srcframe->height, (enum AVPixelFormat)dstframe->format, SWS_BICUBIC, NULL, NULL, NULL);
if (swsctx)
{
if (updown)
{
srcframe->data[0] += srcframe->linesize[0] * (srcframe->height - 1);
srcframe->linesize[0] *= -1;
srcframe->data[1] += srcframe->linesize[1] * (srcframe->height / 2 - 1);
srcframe->linesize[1] *= -1;
srcframe->data[2] += srcframe->linesize[2] * (srcframe->height / 2 - 1);
srcframe->linesize[2] *= -1;
}
int ret = sws_scale(swsctx, srcframe->data, srcframe->linesize, 0, srcframe->height, dstframe->data, dstframe->linesize);
if (ret > 0)
{
dstframe->pts = srcframe->pts;
dstframe->pkt_dts = srcframe->pkt_dts;
sws_freeContext(swsctx);
return dstframe;
}
else
{
log_print(HT_LOG_ERR, "%s, sws_scale failed\r\n", __FUNCTION__);
sws_freeContext(swsctx);
return NULL;
}
}
return NULL;
}
void CVideoPlayer::onVideoFrame(AVFrame* frame)
{
// Perform snapshot request
if (m_bSnapshot)
{
if (doSnapshot(frame))
{
m_bSnapshot = FALSE;
}
}
AVFrame* dst = NULL;
if (m_nDstVideoFmt != frame->format)
{
if (initFrame(m_pRenderFrame, frame->width, frame->height, m_nDstVideoFmt))
{
if (NULL != convertFrame(frame, m_pRenderFrame, m_bUpdown))
{
dst = av_frame_clone(m_pRenderFrame);
}
}
}
else
{
dst = av_frame_clone(frame);
}
if (dst)
{
sys_os_mutex_enter(m_pVideoListMutex);
if (m_videoFrameList.size() >= 10)
{
AVFrame* frame = m_videoFrameList.front();
if (frame)
{
av_frame_free(&frame);
}
m_videoFrameList.pop_front();
}
m_videoFrameList.push_back(dst);
sys_os_mutex_leave(m_pVideoListMutex);
}
}
void CVideoPlayer::onAudioFrame(AVFrame* frame)
{
AVFrame* dst = av_frame_clone(frame);
if (dst)
{
while (m_audioPlayFlag && m_audioFrameList.size() >= 10)
{
usleep(10 * 1000);
}
sys_os_mutex_enter(m_pAudioListMutex);
m_audioFrameList.push_back(dst);
sys_os_mutex_leave(m_pAudioListMutex);
}
}
void CVideoPlayer::audioPlayThread()
{
while (m_audioPlayFlag)
{
AVFrame* pFrame = NULL;
sys_os_mutex_enter(m_pAudioListMutex);
if (m_audioFrameList.size() > 0)
{
pFrame = m_audioFrameList.front();
m_audioFrameList.pop_front();
}
sys_os_mutex_leave(m_pAudioListMutex);
if (pFrame)
{
// Save audio PTS and play time stamp
m_lastAudioTS = sys_os_get_ms();
m_nLastAudioPts = pFrame->pts;
if (m_pAudioPlay)
{
// Will wait for the playback to complete before returning
m_pAudioPlay->playAudio(pFrame->data[0], pFrame->nb_samples * pFrame->channels * 2);
}
else
{
usleep(1000000 * pFrame->nb_samples / m_nSampleRate);
}
av_frame_free(&pFrame);
}
else
{
// Fill the silence data
uint8 buff[1024] = { 0 };
m_pAudioPlay->playAudio(buff, 1024);
}
}
m_audioPlayThread = 0;
}
void CVideoPlayer::videoPlayThread()
{
int64 cur_delay = 0;
int64 pre_delay = 0;
uint32 cur_time = 0;
uint32 pre_time = 0;
AVFrame* pLastFrame = NULL;
while (m_videoPlayFlag)
{
int size, fast = 0, slow = 0;
AVFrame* pFrame = NULL;
sys_os_mutex_enter(m_pVideoListMutex);
size = m_videoFrameList.size();
if (size > 0)
{
pFrame = m_videoFrameList.front();
m_videoFrameList.pop_front();
}
else
{
pFrame = pLastFrame;
}
sys_os_mutex_leave(m_pVideoListMutex);
if (pFrame)
{
// D3D objects initialized in the same thread can recover when the device is lost
/* if (NULL == m_pVideoRender)
{
initVideoRender(pFrame->width, pFrame->height);
}*/
if (pFrame->format != m_nDstVideoFmt)
{
av_frame_free(&pFrame);
continue;
}
// If there is audio, synchronize with audio
if (m_nLastAudioPts != (uint64)AV_NOPTS_VALUE)
{
int diff = sys_os_get_ms() - m_lastAudioTS;
uint64 vpts = pFrame->pts;
uint64 epts = m_nLastAudioPts + diff * 1000;
int ptsdiff = vpts - epts;
// Video ahead of audio 200ms
// If the video is 3 minutes ahead of the audio, the RTP timestamp may be incorrect, so skip it
if (ptsdiff >= 200 * 1000 && ptsdiff <= 180 * 1000 * 1000)
{
fast = 1;
}
// Video is behind audio 200ms
// If the video is 3 minutes behind the audio, the RTP timestamp may be inaccurate, so skip it
else if (ptsdiff <= -200 * 1000 && ptsdiff >= -180 * 1000 * 1000)
{
slow = 1;
}
}
//if (m_pVideoRender)
//{
// if (m_bSizeChanged)
// {
// m_pVideoRender->setWindowSize(m_size);
// m_bSizeChanged = FALSE;
// }
// m_pVideoRender->render(pFrame, m_nRenderMode);
//}
// Drawing here
// Keep the last frame
if (size > 1)
{
av_frame_free(&pFrame);
av_frame_free(&pLastFrame);
pLastFrame = NULL;
}
else if (pLastFrame != pFrame)
{
av_frame_free(&pLastFrame);
pLastFrame = pFrame;
}
if (fast)
{
cur_delay = 1000 / 10.0 * 1000; // The rendering fps is 10
}
else if (slow)
{
// don't sleep
cur_delay = 0;
}
else
{
float fps = 30.0;
if (m_pVideoDecoder)
{
fps = m_pVideoDecoder->getFrameRate();
if (fps == 0)
{
fps = 30.0;
}
}
cur_delay = 1000 / fps * 1000; // The default rendering fps
}
cur_time = sys_os_get_ms();
if (pre_time > 0)
{
cur_delay += pre_delay - (cur_time - pre_time) * 1000;
if (cur_delay < 1000)
{
cur_delay = 0;
}
}
pre_time = cur_time;
pre_delay = cur_delay;
if (cur_delay > 0)
{
usleep(cur_delay);
}
}
else
{
usleep(1000);
}
}
if (pLastFrame)
{
av_frame_free(&pLastFrame);
}
m_videoPlayThread = 0;
}