918 lines
20 KiB
C++
918 lines
20 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 "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;
|
|
}
|
|
|
|
|
|
|