Files

832 lines
17 KiB
C++
Raw Permalink Normal View History

2026-03-28 11:39:04 +11:00
/***************************************************************************************
*
* 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 "file_player.h"
#include "utils.h"
#include "media_codec.h"
void * readThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->readThread();
return NULL;
}
void * videoThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->videoThread();
return NULL;
}
void * audioThread(void * argv)
{
CFilePlayer * pPlayer = (CFilePlayer *) argv;
pPlayer->audioThread();
return NULL;
}
CFilePlayer::CFilePlayer(QObject * parent)
: CVideoPlayer(parent)
, m_nAudioIndex(-1)
, m_nVideoIndex(-1)
, m_nDuration(0)
, m_nCurPos(0)
, m_bSeek(0)
, m_dSeekPos(0)
, m_nNalLength(0)
, m_pFormatContext(NULL)
, m_hReadThread(0)
, m_hVideoThread(0)
, m_hAudioThread(0)
, m_pVideoQueue(NULL)
, m_pAudioQueue(NULL)
{
}
CFilePlayer::~CFilePlayer()
{
close();
}
BOOL CFilePlayer::openFile(const char * filename)
{
if (avformat_open_input(&m_pFormatContext, filename, NULL, NULL) != 0)
{
log_print(HT_LOG_ERR, "avformat_open_input failed, %s\r\n", filename);
return FALSE;
}
avformat_find_stream_info(m_pFormatContext, NULL);
if (m_pFormatContext->duration != AV_NOPTS_VALUE)
{
m_nDuration = m_pFormatContext->duration;
}
// find audio & video stream index
for (uint32 i=0; i < m_pFormatContext->nb_streams; i++)
{
if (m_pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_nVideoIndex = i;
if (m_nDuration < m_pFormatContext->streams[i]->duration)
{
m_nDuration = m_pFormatContext->streams[i]->duration;
}
}
else if (m_pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_nAudioIndex = i;
if (m_nDuration < m_pFormatContext->streams[i]->duration)
{
m_nDuration = m_pFormatContext->streams[i]->duration;
}
}
}
m_nDuration /= 1000; // to millisecond
// has video stream
if (m_nVideoIndex != -1)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
if (codecpar->extradata[0] == 1)
{
// Store right nal length size that will be used to parse all other nals
m_nNalLength = (codecpar->extradata[4] & 0x03) + 1;
}
}
const AVBitStreamFilter * bsfc = av_bsf_get_by_name("h264_mp4toannexb");
if (bsfc)
{
int ret;
AVBSFContext *bsf;
av_bsf_alloc(bsfc, &bsf);
ret = avcodec_parameters_copy(bsf->par_in, codecpar);
if (ret < 0)
{
return FALSE;
}
ret = av_bsf_init(bsf);
if (ret < 0)
{
return FALSE;
}
ret = avcodec_parameters_copy(codecpar, bsf->par_out);
if (ret < 0)
{
return FALSE;
}
av_bsf_free(&bsf);
}
}
else if (codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
if (codecpar->extradata[0] || codecpar->extradata[1] || codecpar->extradata[2] > 1)
{
m_nNalLength = 4;
}
}
const AVBitStreamFilter * bsfc = av_bsf_get_by_name("hevc_mp4toannexb");
if (bsfc)
{
int ret;
AVBSFContext *bsf;
av_bsf_alloc(bsfc, &bsf);
ret = avcodec_parameters_copy(bsf->par_in, codecpar);
if (ret < 0)
{
return FALSE;
}
ret = av_bsf_init(bsf);
if (ret < 0)
{
return FALSE;
}
ret = avcodec_parameters_copy(codecpar, bsf->par_out);
if (ret < 0)
{
return FALSE;
}
av_bsf_free(&bsf);
}
}
}
return TRUE;
}
BOOL CFilePlayer::open(QString fileName, WId hWnd)
{
BOOL ret = FALSE;
close();
CVideoPlayer::open(fileName, hWnd);
ret = openFile(fileName.toStdString().c_str());
if (ret)
{
if (m_nVideoIndex >= 0)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
openVideo(codecpar->codec_id, codecpar->extradata, codecpar->extradata_size);
}
if (m_nAudioIndex >= 0)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
openAudio(codecpar->codec_id, codecpar->sample_rate, codecpar->channels, codecpar->bits_per_coded_sample);
}
}
return ret;
}
void CFilePlayer::close()
{
m_bPlaying = FALSE;
HTPACKET packet;
memset(&packet, 0, sizeof(packet));
clearQueue(m_pAudioQueue);
clearQueue(m_pVideoQueue);
hqBufPut(m_pAudioQueue, (char *)&packet);
hqBufPut(m_pVideoQueue, (char *)&packet);
while (m_hAudioThread)
{
usleep(100*1000);
}
while (m_hVideoThread)
{
usleep(100*1000);
}
while (m_hReadThread)
{
usleep(100*1000);
}
clearQueue(m_pAudioQueue);
clearQueue(m_pVideoQueue);
hqDelete(m_pAudioQueue);
m_pAudioQueue = NULL;
hqDelete(m_pVideoQueue);
m_pVideoQueue = NULL;
if (m_pFormatContext)
{
avformat_close_input(&m_pFormatContext);
}
CVideoPlayer::close();
}
BOOL CFilePlayer::play()
{
if (m_bPlaying)
{
return TRUE;
}
m_bPlaying = TRUE;
if (m_nVideoIndex >= 0)
{
m_pVideoQueue = hqCreate(30, sizeof(HTPACKET), HQ_PUT_WAIT | HQ_GET_WAIT);
m_hVideoThread = sys_os_create_thread((void *)::videoThread, this);
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (codecpar->extradata && codecpar->extradata_size > 8)
{
playVideo(codecpar->extradata, codecpar->extradata_size, 0, 0);
}
}
}
if (m_nAudioIndex >= 0)
{
m_pAudioQueue = hqCreate(30, sizeof(HTPACKET), HQ_PUT_WAIT | HQ_GET_WAIT);
m_hAudioThread = sys_os_create_thread((void *)::audioThread, this);
}
m_hReadThread = sys_os_create_thread((void *)::readThread, this);
return TRUE;
}
void CFilePlayer::stop()
{
close();
}
BOOL CFilePlayer::pause()
{
if (!m_bPaused)
{
m_bPaused = TRUE;
}
else
{
m_bPaused = FALSE;
}
return m_bPaused;
}
BOOL CFilePlayer::seek(int pos)
{
if (pos < 0 || pos > 100)
{
return FALSE;
}
m_bSeek = 1;
m_dSeekPos = m_nDuration / 100 * pos;
return TRUE;
}
int CFilePlayer::getVideoCodec()
{
int codec = VIDEO_CODEC_NONE;
if (m_nVideoIndex >= 0 && m_pFormatContext)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
codec = to_video_codec(codecpar->codec_id);
}
return codec;
}
int CFilePlayer::getAudioCodec()
{
int codec = AUDIO_CODEC_NONE;
if (m_nAudioIndex >= 0 && m_pFormatContext)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
codec = to_audio_codec(codecpar->codec_id);
}
return codec;
}
void CFilePlayer::videoData(uint8 * data, int size, int64 pts, int waitnext)
{
HTPACKET packet;
packet.data = (uint8 *) malloc(size);
if (packet.data)
{
memcpy(packet.data, data, size);
packet.size = size;
packet.ts = pts;
packet.waitnext = waitnext;
if (!hqBufPut(m_pVideoQueue, (char *)&packet))
{
free(packet.data);
}
}
}
void CFilePlayer::audioData(uint8 * data, int size, int64 pts)
{
HTPACKET packet;
packet.data = (uint8 *) malloc(size);
if (packet.data)
{
memcpy(packet.data, data, size);
packet.size = size;
packet.ts = pts;
packet.waitnext = 0;
if (!hqBufPut(m_pAudioQueue, (char *)&packet))
{
free(packet.data);
}
}
}
BOOL CFilePlayer::readFrame()
{
int rret = 0;
AVPacket pkt;
if (NULL == m_pFormatContext)
{
return FALSE;
}
av_init_packet(&pkt);
pkt.data = 0;
pkt.size = 0;
rret = av_read_frame(m_pFormatContext, &pkt);
if (AVERROR_EOF == rret)
{
rret = av_seek_frame(m_pFormatContext, 0, m_pFormatContext->streams[0]->start_time, 0);
if (rret < 0)
{
rret = av_seek_frame(m_pFormatContext, 0, 0, AVSEEK_FLAG_BYTE | AVSEEK_FLAG_BACKWARD);
if (rret < 0)
{
return FALSE;
}
}
if (av_read_frame(m_pFormatContext, &pkt) != 0)
{
return FALSE;
}
}
else if (0 != rret)
{
return FALSE;
}
int64 pts = AV_NOPTS_VALUE;
if (pkt.pts != AV_NOPTS_VALUE)
{
pts = pkt.pts;
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
pts -= m_pFormatContext->start_time;
}
}
else if (pkt.dts != AV_NOPTS_VALUE)
{
pts = pkt.dts;
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
pts -= m_pFormatContext->start_time;
}
}
if (pkt.stream_index == m_nVideoIndex)
{
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
if (codecpar->codec_id == AV_CODEC_ID_H264 || codecpar->codec_id == AV_CODEC_ID_HEVC)
{
if (m_nNalLength)
{
uint8 * data = pkt.data;
int size = pkt.size;
while (pkt.size >= m_nNalLength)
{
int len = 0;
int nal_length = m_nNalLength;
uint8 * pdata = pkt.data;
while (nal_length--)
{
len = (len << 8) | *pdata++;
}
if (len > pkt.size - m_nNalLength || len <= 0)
{
log_print(HT_LOG_DBG, "len=%d, pkt.size=%d\r\n", len, pkt.size);
break;
}
nal_length = m_nNalLength;
pkt.data[nal_length-1] = 1;
nal_length--;
while (nal_length--)
{
pkt.data[nal_length] = 0;
}
pkt.data += len + m_nNalLength;
pkt.size -= len + m_nNalLength;
}
videoData(data, size, pts, 1);
}
else if (pkt.data[0] == 0 && pkt.data[1] == 0 && pkt.data[2] == 0 && pkt.data[3] == 1)
{
videoData(pkt.data, pkt.size, pts, 1);
}
else if (pkt.data[0] == 0 && pkt.data[1] == 0 && pkt.data[2] == 1)
{
videoData(pkt.data, pkt.size, pts, 1);
}
else
{
log_print(HT_LOG_ERR, "%s, unknown format\r\n", __FUNCTION__);
}
}
else
{
videoData(pkt.data, pkt.size, pts, 1);
}
}
else if (pkt.stream_index == m_nAudioIndex)
{
audioData(pkt.data, pkt.size, pts);
}
av_packet_unref(&pkt);
return TRUE;
}
void CFilePlayer::readThread()
{
while (m_bPlaying)
{
if (m_bPaused)
{
usleep(100*1000);
continue;
}
if (m_bSeek)
{
if (seekStream(m_dSeekPos))
{
clearQueue(m_pVideoQueue);
clearQueue(m_pAudioQueue);
}
m_bSeek = 0;
}
if (!readFrame())
{
break;
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hReadThread = 0;
}
void CFilePlayer::videoThread()
{
HTPACKET packet;
int64 pts = 0;
int64 cur_delay = 0;
int64 pre_delay = 0;
uint32 cur_time = 0;
uint32 pre_time = 0;
int timeout = 1000000.0 / getFramerate();
while (m_bPlaying)
{
if (m_bPaused)
{
pre_time = 0;
usleep(100*1000);
continue;
}
if (hqBufGet(m_pVideoQueue, (char *)&packet))
{
if (NULL == packet.data || 0 == packet.size)
{
break;
}
if (packet.ts != AV_NOPTS_VALUE)
{
AVRational q = {1, AV_TIME_BASE};
pts = av_rescale_q (packet.ts, m_pFormatContext->streams[m_nVideoIndex]->time_base, q);
pts /= 1000;
m_nCurPos = pts;
}
playVideo(packet.data, packet.size, pts, 0);
free(packet.data);
if (packet.waitnext)
{
cur_time = sys_os_get_ms();
cur_delay = timeout;
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);
}
}
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hVideoThread = 0;
}
void CFilePlayer::audioThread()
{
HTPACKET packet;
int64 pts = 0;
while (m_bPlaying)
{
if (m_bPaused)
{
usleep(100*1000);
continue;
}
if (hqBufGet(m_pAudioQueue, (char *)&packet))
{
if (NULL == packet.data || 0 == packet.size)
{
break;
}
if (packet.ts != AV_NOPTS_VALUE)
{
AVRational q = {1, AV_TIME_BASE};
pts = av_rescale_q (packet.ts, m_pFormatContext->streams[m_nAudioIndex]->time_base, q);
pts /= 1000;
m_nCurPos = pts;
}
playAudio(packet.data, packet.size, pts, 0);
free(packet.data);
}
}
log_print(HT_LOG_INFO, "%s, exit!\r\n", __FUNCTION__);
m_hAudioThread = 0;
}
void CFilePlayer::clearQueue(HQUEUE * queue)
{
HTPACKET packet;
while (!hqBufIsEmpty(queue))
{
if (hqBufGet(queue, (char *)&packet))
{
if (packet.data != NULL && packet.size != 0)
{
free(packet.data);
}
}
else
{
// should be not to here
log_print(HT_LOG_ERR, "%s, hqBufGet failed\r\n", __FUNCTION__);
break;
}
}
}
BOOL CFilePlayer::seekStream(double pos)
{
if (pos < 0)
{
return FALSE;
}
if (pos == m_nCurPos)
{
return TRUE;
}
int stream = -1;
int64 seekpos = pos * 1000;
if (m_nAudioIndex >= 0)
{
stream = m_nAudioIndex;
}
else if (m_nVideoIndex >= 0)
{
stream = m_nVideoIndex;
}
if (m_pFormatContext->start_time != AV_NOPTS_VALUE)
{
seekpos += m_pFormatContext->start_time;
}
if (stream >= 0)
{
AVRational q = {1, AV_TIME_BASE};
seekpos = av_rescale_q(seekpos, q, m_pFormatContext->streams[stream]->time_base);
}
if (av_seek_frame(m_pFormatContext, stream, seekpos, AVSEEK_FLAG_BACKWARD) < 0)
{
return FALSE;
}
// Accurate seek to the specified position
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = 0;
pkt.size = 0;
while (av_read_frame(m_pFormatContext, &pkt) == 0)
{
if (pkt.stream_index != stream)
{
av_packet_unref(&pkt);
continue;
}
if (pkt.pts != AV_NOPTS_VALUE)
{
if (pkt.pts < seekpos)
{
av_packet_unref(&pkt);
continue;
}
else
{
break;
}
}
else if (pkt.dts != AV_NOPTS_VALUE)
{
if (pkt.dts < seekpos)
{
av_packet_unref(&pkt);
continue;
}
else
{
break;
}
}
else
{
break;
}
av_packet_unref(&pkt);
}
av_packet_unref(&pkt);
m_nCurPos = pos;
return TRUE;
}
double CFilePlayer::getFramerate()
{
double framerate = 25;
if (m_nVideoIndex != -1)
{
if (m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.den > 0)
{
framerate = m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.num /
(double)m_pFormatContext->streams[m_nVideoIndex]->r_frame_rate.den;
}
if ((framerate < 1 || framerate > 60) &&
m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.den > 0)
{
framerate = m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.num /
(double)m_pFormatContext->streams[m_nVideoIndex]->avg_frame_rate.den;
}
if (framerate < 1 || framerate > 60)
{
framerate = 25;
}
}
return framerate;
}