/*************************************************************************************** * * 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-2022, 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 "file_demux.h" #include "audio_encoder.h" #include "video_encoder.h" #include "media_format.h" #include "base64.h" #include "media_util.h" #include "media_codec.h" #include "h264.h" #include "h265.h" #include "avcodec_mutex.h" /*********************************************************************************/ /** * audio decode callback */ void AudioDecodeCallback(AVFrame * frame, void *pUserdata) { CFileDemux * pDemux = (CFileDemux *)pUserdata; pDemux->audioEncode(frame); } /** * video decode callback */ void VideoDecodeCallback(AVFrame * frame, void *pUserdata) { CFileDemux * pDemux = (CFileDemux *)pUserdata; pDemux->videoEncode(frame); } /** * audio encode callback */ void AudioEncodeCallback(uint8 *data, int size, int nbsamples, void *pUserdata) { CFileDemux * pDemux = (CFileDemux *)pUserdata; pDemux->dataCallback(data, size, DATA_TYPE_AUDIO, nbsamples, 1); } /** * video encode callback */ void VideoEncodeCallback(uint8 *data, int size, int waitnext, void *pUserdata) { CFileDemux * pDemux = (CFileDemux *)pUserdata; pDemux->dataCallback(data, size, DATA_TYPE_VIDEO, 0, waitnext); } CFileDemux::CFileDemux(const char * filename, int loopnums) { m_nAudioIndex = -1; m_nVideoIndex = -1; m_nDuration = 0; m_nCurPos = 0; m_nNalLength = 0; m_pFormatContext = NULL; m_pVideoDecoder = NULL; m_pAudioDecoder = NULL; m_pVideoEncoder = NULL; m_pAudioEncoder = NULL; m_nPreNalu = -1; m_bVideoFirst = TRUE; m_bAudioFirst = TRUE; m_pCallback = NULL; m_pUserdata = NULL; m_nLoopNums = 0; m_nMaxLoopNums = loopnums; memset(m_pFilename, 0, sizeof(m_pFilename)); strncpy(m_pFilename, filename, sizeof(m_pFilename)-1); init(filename); } CFileDemux::~CFileDemux() { uninit(); } /** * Init file demux * * @param filename the file name * @return TRUE on success, FALSE on error */ BOOL CFileDemux::init(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; } void CFileDemux::uninit() { if (m_pVideoDecoder) { delete m_pVideoDecoder; m_pVideoDecoder = NULL; } if (m_pAudioDecoder) { delete m_pAudioDecoder; m_pAudioDecoder = NULL; } if (m_pVideoEncoder) { delete m_pVideoEncoder; m_pVideoEncoder = NULL; } if (m_pAudioEncoder) { delete m_pAudioEncoder; m_pAudioEncoder = NULL; } if (m_pFormatContext) { avformat_close_input(&m_pFormatContext); } } enum AVCodecID CFileDemux::getVideoCodec() { if (m_nVideoIndex != -1) { return m_pFormatContext->streams[m_nVideoIndex]->codecpar->codec_id; } return AV_CODEC_ID_NONE; } int CFileDemux::getWidth() { if (m_nVideoIndex != -1) { return m_pFormatContext->streams[m_nVideoIndex]->codecpar->width; } return 0; } int CFileDemux::getHeight() { if (m_nVideoIndex != -1) { return m_pFormatContext->streams[m_nVideoIndex]->codecpar->height; } return 0; } double CFileDemux::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; } enum AVCodecID CFileDemux::getAudioCodec() { if (m_nAudioIndex != -1) { return m_pFormatContext->streams[m_nAudioIndex]->codecpar->codec_id; } return AV_CODEC_ID_NONE; } int CFileDemux::getSamplerate() { if (m_nAudioIndex != -1) { return m_pFormatContext->streams[m_nAudioIndex]->codecpar->sample_rate; } return 8000; } int CFileDemux::getChannels() { if (m_nAudioIndex != -1) { return m_pFormatContext->streams[m_nAudioIndex]->codecpar->channels; } return 2; } /** * set audio output parameters * * @param codec audio output codec * @param samplerate audio output sample rate * @param channels audio output channels * @return TRUE on success, FALSE on error */ BOOL CFileDemux::setAudioFormat(int codec, int samplerate, int channels, int bitrate) { if (m_nAudioIndex == -1) { return FALSE; } AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar; #if 0 if (codecpar->codec_id != to_audio_avcodecid(codec) || codecpar->channels != channels || codecpar->sample_rate != samplerate || codecpar->format != AV_SAMPLE_FMT_S16) #else if (1) #endif { m_pAudioDecoder = new CAudioDecoder; if (NULL == m_pAudioDecoder) { return FALSE; } if (FALSE == m_pAudioDecoder->init(codecpar->codec_id, codecpar->sample_rate, codecpar->channels, codecpar->bits_per_coded_sample)) { return FALSE; } m_pAudioDecoder->setCallback(AudioDecodeCallback, this); m_pAudioEncoder = new CAudioEncoder; if (NULL == m_pAudioEncoder) { return FALSE; } AudioEncoderParam params; memset(¶ms, 0, sizeof(params)); params.SrcChannels = codecpar->channels; params.SrcSamplerate = codecpar->sample_rate; params.SrcSamplefmt = AV_SAMPLE_FMT_S16; params.DstChannels = channels; params.DstSamplefmt = AV_SAMPLE_FMT_S16; params.DstSamplerate = samplerate; params.DstBitrate = bitrate; params.DstCodec = codec; if (FALSE == m_pAudioEncoder->init(¶ms)) { return FALSE; } m_pAudioEncoder->addCallback(AudioEncodeCallback, this); } return TRUE; } BOOL CFileDemux::setVideoFormat(int codec, int width, int height, double framerate, int bitrate) { if (m_nVideoIndex == -1) { return FALSE; } AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->codec_id != to_video_avcodecid(codec) || codecpar->width != width || codecpar->height != height) { m_pVideoDecoder = new CVideoDecoder; if (NULL == m_pVideoDecoder) { return FALSE; } if (FALSE == m_pVideoDecoder->init(codecpar->codec_id, codecpar->extradata, codecpar->extradata_size)) { return FALSE; } m_pVideoDecoder->setCallback(VideoDecodeCallback, this); m_pVideoEncoder = new CVideoEncoder; if (NULL == m_pVideoEncoder) { return FALSE; } VideoEncoderParam params; memset(¶ms, 0, sizeof(params)); params.SrcWidth = codecpar->width; params.SrcHeight = codecpar->height; params.SrcPixFmt = (AVPixelFormat)codecpar->format; params.DstCodec = codec; params.DstWidth = width; params.DstHeight = height; params.DstFramerate = framerate; params.DstBitrate = bitrate; if (FALSE == m_pVideoEncoder->init(¶ms)) { return FALSE; } m_pVideoEncoder->addCallback(VideoEncodeCallback, this); } if (m_pVideoDecoder && codecpar->extradata_size > 0) { m_pVideoDecoder->decode(codecpar->extradata, codecpar->extradata_size, 0); } return TRUE; } void CFileDemux::videoDecode(AVPacket * pkt) { if (m_pVideoDecoder) { m_pVideoDecoder->decode(pkt); } } void CFileDemux::audioDecode(AVPacket * pkt) { if (m_pAudioDecoder) { m_pAudioDecoder->decode(pkt); } } void CFileDemux::videoEncode(AVFrame * frame) { if (m_pVideoEncoder) { m_pVideoEncoder->encode(frame); } } void CFileDemux::audioEncode(AVFrame * frame) { if (m_pAudioEncoder) { m_pAudioEncoder->encode(frame); } } void CFileDemux::videoData(uint8 * data, int size, uint64_t pts, int waitnext) { if (NULL == m_pVideoDecoder) { dataCallback(data, size, DATA_TYPE_VIDEO, 0, waitnext); } else { m_pVideoDecoder->decode(data, size, pts); } } BOOL CFileDemux::readFrame() { int rret = 0; AVPacket pkt; av_init_packet(&pkt); pkt.data = 0; pkt.size = 0; rret = av_read_frame(m_pFormatContext, &pkt); if (AVERROR_EOF == rret) { if (++m_nLoopNums >= m_nMaxLoopNums) { return FALSE; } if (m_pFormatContext) { avformat_close_input(&m_pFormatContext); } return init(m_pFilename); } else if (0 != rret) { return FALSE; } int64 pts = AV_NOPTS_VALUE; if (pkt.dts != AV_NOPTS_VALUE) { pts = pkt.dts; 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) { AVRational q = {1, AV_TIME_BASE}; if (pts != AV_NOPTS_VALUE) { m_nCurPos = av_rescale_q (pts, m_pFormatContext->streams[m_nVideoIndex]->time_base, q); m_nCurPos /= 1000; } 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) { AVRational q = {1, AV_TIME_BASE}; if (pts != AV_NOPTS_VALUE) { m_nCurPos = av_rescale_q (pts, m_pFormatContext->streams[m_nAudioIndex]->time_base, q); m_nCurPos /= 1000; } if (NULL == m_pAudioEncoder) { dataCallback(pkt.data, pkt.size, DATA_TYPE_AUDIO, 1024, 1); } else { audioDecode(&pkt); } } av_packet_unref(&pkt); return TRUE; } char * CFileDemux::getH264AuxSDPLine(int rtp_pt) { uint8 * sps = NULL; uint32 spsSize = 0; uint8 * pps = NULL; uint32 ppsSize = 0; AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->extradata_size <= 8) { return NULL; } uint8 *r, *end = codecpar->extradata + codecpar->extradata_size; r = avc_find_startcode(codecpar->extradata, end); while (r < end) { uint8 *r1; while (!*(r++)); r1 = avc_find_startcode(r, end); int nal_type = (r[0] & 0x1F); if (H264_NAL_PPS == nal_type) { pps = r; ppsSize = r1 - r; } else if (H264_NAL_SPS == nal_type) { sps = r; spsSize = r1 - r; } r = r1; } if (NULL == sps || spsSize == 0 || NULL == pps || ppsSize == 0) { return NULL; } // Set up the "a=fmtp:" SDP line for this stream: uint8* spsWEB = new uint8[spsSize]; // "WEB" means "Without Emulation Bytes" uint32 spsWEBSize = remove_emulation_bytes(spsWEB, spsSize, sps, spsSize); if (spsWEBSize < 4) { // Bad SPS size => assume our source isn't ready delete[] spsWEB; return NULL; } uint32 profileLevelId = (spsWEB[1]<<16) | (spsWEB[2]<<8) | spsWEB[3]; delete[] spsWEB; char* sps_base64 = new char[spsSize*2+1]; char* pps_base64 = new char[ppsSize*2+1]; base64_encode(sps, spsSize, sps_base64, spsSize*2+1); base64_encode(pps, ppsSize, pps_base64, ppsSize*2+1); char const* fmtpFmt = "a=fmtp:%d packetization-mode=1" ";profile-level-id=%06X" ";sprop-parameter-sets=%s,%s"; uint32 fmtpFmtSize = strlen(fmtpFmt) + 3 /* max char len */ + 6 /* 3 bytes in hex */ + strlen(sps_base64) + strlen(pps_base64); char* fmtp = new char[fmtpFmtSize+1]; memset(fmtp, 0, fmtpFmtSize+1); sprintf(fmtp, fmtpFmt, rtp_pt, profileLevelId, sps_base64, pps_base64); delete[] sps_base64; delete[] pps_base64; return fmtp; } char * CFileDemux::getH265AuxSDPLine(int rtp_pt) { uint8* vps = NULL; uint32 vpsSize = 0; uint8* sps = NULL; uint32 spsSize = 0; uint8* pps = NULL; uint32 ppsSize = 0; AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->extradata_size < 23) { return NULL; } uint8 *r, *end = codecpar->extradata + codecpar->extradata_size; r = avc_find_startcode(codecpar->extradata, end); while (r < end) { uint8 *r1; while (!*(r++)); r1 = avc_find_startcode(r, end); int nal_type = (r[0] >> 1) & 0x3F; if (HEVC_NAL_VPS == nal_type) { vps = r; vpsSize = r1 - r; } else if (HEVC_NAL_PPS == nal_type) { pps = r; ppsSize = r1 - r; } else if (HEVC_NAL_SPS == nal_type) { sps = r; spsSize = r1 - r; } r = r1; } if (NULL == vps || vpsSize == 0 || NULL == sps || spsSize == 0 || NULL == pps || ppsSize == 0) { return NULL; } // Set up the "a=fmtp:" SDP line for this stream. uint8* vpsWEB = new uint8[vpsSize]; // "WEB" means "Without Emulation Bytes" uint32 vpsWEBSize = remove_emulation_bytes(vpsWEB, vpsSize, vps, vpsSize); if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) { // Bad VPS size => assume our source isn't ready delete[] vpsWEB; return NULL; } uint8 const* profileTierLevelHeaderBytes = &vpsWEB[6]; uint32 profileSpace = profileTierLevelHeaderBytes[0]>>6; // general_profile_space uint32 profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc uint32 tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1;// general_tier_flag uint32 levelId = profileTierLevelHeaderBytes[11]; // general_level_idc uint8 const* interop_constraints = &profileTierLevelHeaderBytes[5]; char interopConstraintsStr[100]; sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X", interop_constraints[0], interop_constraints[1], interop_constraints[2], interop_constraints[3], interop_constraints[4], interop_constraints[5]); delete[] vpsWEB; char* sprop_vps = new char[vpsSize*2+1]; char* sprop_sps = new char[spsSize*2+1]; char* sprop_pps = new char[ppsSize*2+1]; base64_encode(vps, vpsSize, sprop_vps, vpsSize*2+1); base64_encode(sps, spsSize, sprop_sps, spsSize*2+1); base64_encode(pps, ppsSize, sprop_pps, ppsSize*2+1); char const* fmtpFmt = "a=fmtp:%d profile-space=%u" ";profile-id=%u" ";tier-flag=%u" ";level-id=%u" ";interop-constraints=%s" ";sprop-vps=%s" ";sprop-sps=%s" ";sprop-pps=%s"; uint32 fmtpFmtSize = strlen(fmtpFmt) + 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */ + 20 /* max num chars: profile_id */ + 20 /* max num chars: tier_flag */ + 20 /* max num chars: level_id */ + strlen(interopConstraintsStr) + strlen(sprop_vps) + strlen(sprop_sps) + strlen(sprop_pps); char* fmtp = new char[fmtpFmtSize+1]; memset(fmtp, 0, fmtpFmtSize+1); sprintf(fmtp, fmtpFmt, rtp_pt, profileSpace, profileId, tierFlag, levelId, interopConstraintsStr, sprop_vps, sprop_sps, sprop_pps); delete[] sprop_vps; delete[] sprop_sps; delete[] sprop_pps; return fmtp; } char * CFileDemux::getMP4AuxSDPLine(int rtp_pt) { AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->extradata_size == 0) { return NULL; } char const* fmtpFmt = "a=fmtp:%d " "profile-level-id=%d;" "config="; uint32 fmtpFmtSize = strlen(fmtpFmt) + 3 /* max char len */ + 3 /* max char len */ + 2*codecpar->extradata_size; /* 2*, because each byte prints as 2 chars */ char* fmtp = new char[fmtpFmtSize+1]; memset(fmtp, 0, fmtpFmtSize+1); sprintf(fmtp, fmtpFmt, rtp_pt, 1); char* endPtr = &fmtp[strlen(fmtp)]; for (int i = 0; i < codecpar->extradata_size; ++i) { sprintf(endPtr, "%02X", codecpar->extradata[i]); endPtr += 2; } return fmtp; } char * CFileDemux::getVideoAuxSDPLine(int rtp_pt) { if (m_pVideoEncoder) { return m_pVideoEncoder->getAuxSDPLine(rtp_pt); } if (m_nVideoIndex < 0) { return NULL; } AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->codec_id == AV_CODEC_ID_H264) { return getH264AuxSDPLine(rtp_pt); } else if (codecpar->codec_id == AV_CODEC_ID_MPEG4) { return getMP4AuxSDPLine(rtp_pt); } else if (codecpar->codec_id == AV_CODEC_ID_HEVC) { return getH265AuxSDPLine(rtp_pt); } return NULL; } char * CFileDemux::getAACAuxSDPLine(int rtp_pt) { AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar; if (codecpar->extradata_size == 0) { return NULL; } char const* fmtpFmt = "a=fmtp:%d " "streamtype=5;profile-level-id=1;" "mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;" "config="; /* streamtype, 4 : video, 5 : audio */ uint32 fmtpFmtSize = strlen(fmtpFmt) + 3 /* max char len */ + 2*codecpar->extradata_size; /* 2*, because each byte prints as 2 chars */ char* fmtp = new char[fmtpFmtSize+1]; memset(fmtp, 0, fmtpFmtSize+1); sprintf(fmtp, fmtpFmt, rtp_pt); char* endPtr = &fmtp[strlen(fmtp)]; for (int i = 0; i < codecpar->extradata_size; ++i) { sprintf(endPtr, "%02X", codecpar->extradata[i]); endPtr += 2; } return fmtp; } char * CFileDemux::getAudioAuxSDPLine(int rtp_pt) { if (m_pAudioEncoder) { return m_pAudioEncoder->getAuxSDPLine(rtp_pt); } if (m_nAudioIndex < 0) { return NULL; } AVCodecParameters * codecpar = m_pFormatContext->streams[m_nAudioIndex]->codecpar; if (codecpar->codec_id == AV_CODEC_ID_AAC) { return getAACAuxSDPLine(rtp_pt); } return NULL; } BOOL CFileDemux::seekStream(double pos) { if (pos < 0) { return FALSE; } if (pos * 1000 == m_nCurPos) { return TRUE; } int stream = -1; int64 seekpos = pos * 1000000; if (m_nVideoIndex >= 0) { stream = m_nVideoIndex; } else if (m_nAudioIndex >= 0) { stream = m_nAudioIndex; } 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 * 1000; return TRUE; } void CFileDemux::dataCallback(uint8 * data, int size, int type, int nbsamples, BOOL waitnext) { if (m_pCallback) { uint8 * buff = data; int len = size; if (DATA_TYPE_VIDEO == type && NULL == m_pVideoEncoder && m_pFormatContext && m_nVideoIndex >= 0) { AVCodecParameters * codecpar = m_pFormatContext->streams[m_nVideoIndex]->codecpar; if (codecpar->codec_id == AV_CODEC_ID_H264 && codecpar->extradata_size > 8) { uint8 naltype = avc_h264_nalu_type(data, size); if (naltype == H264_NAL_IDR) { len = codecpar->extradata_size + size; buff = (uint8 *)malloc(len); if (buff) { memcpy(buff, codecpar->extradata, codecpar->extradata_size); memcpy(buff + codecpar->extradata_size, data, size); } else { buff = data; len = size; } } } else if (codecpar->codec_id == AV_CODEC_ID_HEVC && codecpar->extradata_size > 8) { uint8 naltype = avc_h265_nalu_type(data, size); if (naltype >= HEVC_NAL_BLA_W_LP && naltype <= HEVC_NAL_CRA_NUT) { len = codecpar->extradata_size + size; buff = (uint8 *)malloc(len); if (buff) { memcpy(buff, codecpar->extradata, codecpar->extradata_size); memcpy(buff + codecpar->extradata_size, data, size); } else { buff = data; len = size; } } } } m_pCallback(buff, len, type, nbsamples, waitnext, m_pUserdata); if (buff != data) { free(buff); } } } void CFileDemux::setCallback(DemuxCallback pCallback, void * pUserdata) { m_pCallback = pCallback; m_pUserdata = pUserdata; }