Initial setup for CLion
This commit is contained in:
917
MediaClient/media/file_player.cpp
Normal file
917
MediaClient/media/file_player.cpp
Normal file
@@ -0,0 +1,917 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* 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()
|
||||
: CVideoPlayer()
|
||||
, 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(std::string fileName)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
close();
|
||||
CVideoPlayer::open(fileName);
|
||||
ret = openFile(fileName.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()
|
||||
{
|
||||
m_bPaused = FALSE;
|
||||
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);
|
||||
|
||||
// Start the video decoder thread — required for getImage() to return frames.
|
||||
// Without this, the decoder stays stopped and g_frameQueue remains empty.
|
||||
// (Matches CRtspPlayer::play() which calls StartVideoDecoder() after rtsp_start().)
|
||||
CVideoPlayer::StartVideoDecoder();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CFilePlayer::stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
BOOL CFilePlayer::pause()
|
||||
{
|
||||
|
||||
m_bPaused = TRUE;
|
||||
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;
|
||||
}
|
||||
void CFilePlayer::setBbox(cv::Rect bbox) {
|
||||
CVideoPlayer::setBbox(bbox);
|
||||
}
|
||||
void CFilePlayer::setCrop(bool crop) {
|
||||
CVideoPlayer::setCrop(crop);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BOOL CFilePlayer::onRecord()
|
||||
{
|
||||
AVICTX * p_avictx = m_pAviCtx;
|
||||
|
||||
int vcodec = getVideoCodec();
|
||||
int v_extra_len = 0;
|
||||
uint8 * v_extra = NULL;
|
||||
|
||||
if (VIDEO_CODEC_H264 == vcodec)
|
||||
{
|
||||
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
|
||||
|
||||
v_extra = codecpar->extradata;
|
||||
v_extra_len = codecpar->extradata_size;
|
||||
|
||||
avi_set_video_info(p_avictx, 0, 0, 0, "H264");
|
||||
|
||||
avi_set_video_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
|
||||
}
|
||||
else if (VIDEO_CODEC_H265 == vcodec)
|
||||
{
|
||||
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar;
|
||||
|
||||
v_extra = codecpar->extradata;
|
||||
v_extra_len = codecpar->extradata_size;
|
||||
|
||||
avi_set_video_info(p_avictx, 0, 0, 0, "H265");
|
||||
|
||||
avi_set_video_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
|
||||
}
|
||||
else if (VIDEO_CODEC_JPEG == vcodec)
|
||||
{
|
||||
avi_set_video_info(p_avictx, 0, 0, 0, "JPEG");
|
||||
}
|
||||
else if (VIDEO_CODEC_MP4 == vcodec)
|
||||
{
|
||||
avi_set_video_info(p_avictx, 0, 0, 0, "MP4V");
|
||||
}
|
||||
|
||||
int acodec = getAudioCodec();
|
||||
int sr = 0;
|
||||
int ch = 0;
|
||||
|
||||
if (m_nAudioIndex >= 0 && m_pFormatContext)
|
||||
{
|
||||
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
|
||||
|
||||
sr = codecpar->sample_rate;
|
||||
ch = codecpar->channels;
|
||||
}
|
||||
|
||||
if (AUDIO_CODEC_G711A == acodec)
|
||||
{
|
||||
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_ALAW);
|
||||
}
|
||||
else if (AUDIO_CODEC_G711U == acodec)
|
||||
{
|
||||
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_MULAW);
|
||||
}
|
||||
else if (AUDIO_CODEC_G726 == acodec)
|
||||
{
|
||||
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G726);
|
||||
}
|
||||
else if (AUDIO_CODEC_G722 == acodec)
|
||||
{
|
||||
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_G722);
|
||||
}
|
||||
else if (AUDIO_CODEC_AAC == acodec)
|
||||
{
|
||||
AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar;
|
||||
|
||||
avi_set_audio_info(p_avictx, ch, sr, AUDIO_FORMAT_AAC);
|
||||
avi_set_audio_extra_info(p_avictx, codecpar->extradata, codecpar->extradata_size);
|
||||
}
|
||||
|
||||
avi_update_header(p_avictx);
|
||||
|
||||
if (p_avictx->ctxf_video)
|
||||
{
|
||||
if (v_extra && v_extra_len > 0)
|
||||
{
|
||||
recordVideo(v_extra, v_extra_len, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user