STDMETHODIMP CDecAvcodec::Decode(const BYTE *buffer, int buflen, REFERENCE_TIME rtStartIn, REFERENCE_TIME rtStopIn, BOOL bSyncPoint, BOOL bDiscontinuity) { int got_picture = 0; int used_bytes = 0; BOOL bParserFrame = FALSE; BOOL bFlush = (buffer == NULL); BOOL bEndOfSequence = FALSE; AVPacket avpkt; av_init_packet(&avpkt); if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { if (!m_bFFReordering) { m_tcThreadBuffer[m_CurrentThread].rtStart = rtStartIn; m_tcThreadBuffer[m_CurrentThread].rtStop = rtStopIn; } m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } else if (m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos].rtStart = rtStartIn; m_tcBFrameDelay[m_nBFramePos].rtStop = rtStopIn; m_nBFramePos = !m_nBFramePos; } uint8_t *pDataBuffer = NULL; if (!bFlush && buflen > 0) { if (!m_bInputPadded && (!(m_pAVCtx->active_thread_type & FF_THREAD_FRAME) || m_pParser)) { // Copy bitstream into temporary buffer to ensure overread protection // Verify buffer size if (buflen > m_nFFBufferSize) { m_nFFBufferSize = buflen; m_pFFBuffer = (BYTE *)av_realloc_f(m_pFFBuffer, m_nFFBufferSize + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer) { m_nFFBufferSize = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer, buffer, buflen); memset(m_pFFBuffer+buflen, 0, FF_INPUT_BUFFER_PADDING_SIZE); pDataBuffer = m_pFFBuffer; } else { pDataBuffer = (uint8_t *)buffer; } if (m_nCodecId == AV_CODEC_ID_H264) { BOOL bRecovered = m_h264RandomAccess.searchRecoveryPoint(pDataBuffer, buflen); if (!bRecovered) { return S_OK; } } else if (m_nCodecId == AV_CODEC_ID_VP8 && m_bWaitingForKeyFrame) { if (!(pDataBuffer[0] & 1)) { DbgLog((LOG_TRACE, 10, L"::Decode(): Found VP8 key-frame, resuming decoding")); m_bWaitingForKeyFrame = FALSE; } else { return S_OK; } } } while (buflen > 0 || bFlush) { REFERENCE_TIME rtStart = rtStartIn, rtStop = rtStopIn; if (!bFlush) { avpkt.data = pDataBuffer; avpkt.size = buflen; avpkt.pts = rtStartIn; if (rtStartIn != AV_NOPTS_VALUE && rtStopIn != AV_NOPTS_VALUE) avpkt.duration = (int)(rtStopIn - rtStartIn); else avpkt.duration = 0; avpkt.flags = AV_PKT_FLAG_KEY; if (m_bHasPalette) { m_bHasPalette = FALSE; uint32_t *pal = (uint32_t *)av_packet_new_side_data(&avpkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); int pal_size = FFMIN((1 << m_pAVCtx->bits_per_coded_sample) << 2, m_pAVCtx->extradata_size); uint8_t *pal_src = m_pAVCtx->extradata + m_pAVCtx->extradata_size - pal_size; for (int i = 0; i < pal_size/4; i++) pal[i] = 0xFF<<24 | AV_RL32(pal_src+4*i); } } else { avpkt.data = NULL; avpkt.size = 0; } // Parse the data if a parser is present // This is mandatory for MPEG-1/2 if (m_pParser) { BYTE *pOut = NULL; int pOut_size = 0; used_bytes = av_parser_parse2(m_pParser, m_pAVCtx, &pOut, &pOut_size, avpkt.data, avpkt.size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (used_bytes == 0 && pOut_size == 0 && !bFlush) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } // Update start time cache // If more data was read then output, update the cache (incomplete frame) // If output is bigger, a frame was completed, update the actual rtStart with the cached value, and then overwrite the cache if (used_bytes > pOut_size) { if (rtStartIn != AV_NOPTS_VALUE) m_rtStartCache = rtStartIn; } else if (used_bytes == pOut_size || ((used_bytes + 9) == pOut_size)) { // Why +9 above? // Well, apparently there are some broken MKV muxers that like to mux the MPEG-2 PICTURE_START_CODE block (which is 9 bytes) in the package with the previous frame // This would cause the frame timestamps to be delayed by one frame exactly, and cause timestamp reordering to go wrong. // So instead of failing on those samples, lets just assume that 9 bytes are that case exactly. m_rtStartCache = rtStartIn = AV_NOPTS_VALUE; } else if (pOut_size > used_bytes) { rtStart = m_rtStartCache; m_rtStartCache = rtStartIn; // The value was used once, don't use it for multiple frames, that ends up in weird timings rtStartIn = AV_NOPTS_VALUE; } bParserFrame = (pOut_size > 0); if (pOut_size > 0 || bFlush) { if (pOut && pOut_size > 0) { if (pOut_size > m_nFFBufferSize2) { m_nFFBufferSize2 = pOut_size; m_pFFBuffer2 = (BYTE *)av_realloc_f(m_pFFBuffer2, m_nFFBufferSize2 + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer2) { m_nFFBufferSize2 = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer2, pOut, pOut_size); memset(m_pFFBuffer2+pOut_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); avpkt.data = m_pFFBuffer2; avpkt.size = pOut_size; avpkt.pts = rtStart; avpkt.duration = 0; const uint8_t *eosmarker = CheckForEndOfSequence(m_nCodecId, avpkt.data, avpkt.size, &m_MpegParserState); if (eosmarker) { bEndOfSequence = TRUE; } } else { avpkt.data = NULL; avpkt.size = 0; } int ret2 = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); if (ret2 < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - decoding failed despite successfull parsing")); got_picture = 0; } } else { got_picture = 0; } } else { used_bytes = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); } if (FAILED(PostDecode())) { av_frame_unref(m_pFrame); return E_FAIL; } // Decoding of this frame failed ... oh well! if (used_bytes < 0) { av_frame_unref(m_pFrame); return S_OK; } // When Frame Threading, we won't know how much data has been consumed, so it by default eats everything. // In addition, if no data got consumed, and no picture was extracted, the frame probably isn't all that useufl. // The MJPEB decoder is somewhat buggy and doesn't let us know how much data was consumed really... if ((!m_pParser && (m_pAVCtx->active_thread_type & FF_THREAD_FRAME || (!got_picture && used_bytes == 0))) || m_bNoBufferConsumption || bFlush) { buflen = 0; } else { buflen -= used_bytes; pDataBuffer += used_bytes; } // Judge frame usability // This determines if a frame is artifact free and can be delivered // For H264 this does some wicked magic hidden away in the H264RandomAccess class // MPEG-2 and VC-1 just wait for a keyframe.. if (m_nCodecId == AV_CODEC_ID_H264 && (bParserFrame || !m_pParser || got_picture)) { m_h264RandomAccess.judgeFrameUsability(m_pFrame, &got_picture); } else if (m_bResumeAtKeyFrame) { if (m_bWaitingForKeyFrame && got_picture) { if (m_pFrame->key_frame) { DbgLog((LOG_TRACE, 50, L"::Decode() - Found Key-Frame, resuming decoding at %I64d", m_pFrame->pkt_pts)); m_bWaitingForKeyFrame = FALSE; } else { got_picture = 0; } } } // Handle B-frame delay for frame threading codecs if ((m_pAVCtx->active_thread_type & FF_THREAD_FRAME) && m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos] = m_tcThreadBuffer[m_CurrentThread]; m_nBFramePos = !m_nBFramePos; } if (!got_picture || !m_pFrame->data[0]) { if (!avpkt.size) bFlush = FALSE; // End flushing, no more frames av_frame_unref(m_pFrame); continue; } /////////////////////////////////////////////////////////////////////////////////////////////// // Determine the proper timestamps for the frame, based on different possible flags. /////////////////////////////////////////////////////////////////////////////////////////////// if (m_bFFReordering) { rtStart = m_pFrame->pkt_pts; if (m_pFrame->pkt_duration) rtStop = m_pFrame->pkt_pts + m_pFrame->pkt_duration; else rtStop = AV_NOPTS_VALUE; } else if (m_bBFrameDelay && m_pAVCtx->has_b_frames) { rtStart = m_tcBFrameDelay[m_nBFramePos].rtStart; rtStop = m_tcBFrameDelay[m_nBFramePos].rtStop; } else if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { unsigned index = m_CurrentThread; rtStart = m_tcThreadBuffer[index].rtStart; rtStop = m_tcThreadBuffer[index].rtStop; } if (m_bRVDropBFrameTimings && m_pFrame->pict_type == AV_PICTURE_TYPE_B) { rtStart = AV_NOPTS_VALUE; } if (m_bCalculateStopTime) rtStop = AV_NOPTS_VALUE; /////////////////////////////////////////////////////////////////////////////////////////////// // All required values collected, deliver the frame /////////////////////////////////////////////////////////////////////////////////////////////// LAVFrame *pOutFrame = NULL; AllocateFrame(&pOutFrame); AVRational display_aspect_ratio; int64_t num = (int64_t)m_pFrame->sample_aspect_ratio.num * m_pFrame->width; int64_t den = (int64_t)m_pFrame->sample_aspect_ratio.den * m_pFrame->height; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, num, den, 1 << 30); pOutFrame->width = m_pFrame->width; pOutFrame->height = m_pFrame->height; pOutFrame->aspect_ratio = display_aspect_ratio; pOutFrame->repeat = m_pFrame->repeat_pict; pOutFrame->key_frame = m_pFrame->key_frame; pOutFrame->frame_type = av_get_picture_type_char(m_pFrame->pict_type); pOutFrame->ext_format = GetDXVA2ExtendedFlags(m_pAVCtx, m_pFrame); if (m_pFrame->interlaced_frame || (!m_pAVCtx->progressive_sequence && (m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO))) m_iInterlaced = 1; else if (m_pAVCtx->progressive_sequence) m_iInterlaced = 0; pOutFrame->interlaced = (m_pFrame->interlaced_frame || (m_iInterlaced == 1 && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pOutFrame->tff = (fo == DeintFieldOrder_Auto) ? m_pFrame->top_field_first : (fo == DeintFieldOrder_TopFieldFirst); pOutFrame->rtStart = rtStart; pOutFrame->rtStop = rtStop; PixelFormatMapping map = getPixFmtMapping((AVPixelFormat)m_pFrame->format); pOutFrame->format = map.lavpixfmt; pOutFrame->bpp = map.bpp; if (m_nCodecId == AV_CODEC_ID_MPEG2VIDEO || m_nCodecId == AV_CODEC_ID_MPEG1VIDEO) pOutFrame->avgFrameDuration = GetFrameDuration(); if (map.conversion) { ConvertPixFmt(m_pFrame, pOutFrame); } else { for (int i = 0; i < 4; i++) { pOutFrame->data[i] = m_pFrame->data[i]; pOutFrame->stride[i] = m_pFrame->linesize[i]; } pOutFrame->priv_data = av_frame_alloc(); av_frame_ref((AVFrame *)pOutFrame->priv_data, m_pFrame); pOutFrame->destruct = lav_avframe_free; } if (bEndOfSequence) pOutFrame->flags |= LAV_FRAME_FLAG_END_OF_SEQUENCE; if (pOutFrame->format == LAVPixFmt_DXVA2) { pOutFrame->data[0] = m_pFrame->data[4]; HandleDXVA2Frame(pOutFrame); } else { Deliver(pOutFrame); } if (bEndOfSequence) { bEndOfSequence = FALSE; if (pOutFrame->format == LAVPixFmt_DXVA2) { HandleDXVA2Frame(m_pCallback->GetFlushFrame()); } else { Deliver(m_pCallback->GetFlushFrame()); } } if (bFlush) { m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } av_frame_unref(m_pFrame); } return S_OK; }
STDMETHODIMP CDecAvcodec::Decode(const BYTE *buffer, int buflen, REFERENCE_TIME rtStartIn, REFERENCE_TIME rtStopIn, BOOL bSyncPoint, BOOL bDiscontinuity) { CheckPointer(m_pAVCtx, E_UNEXPECTED); int got_picture = 0; int used_bytes = 0; BOOL bFlush = (buffer == nullptr); BOOL bEndOfSequence = FALSE; AVPacket avpkt; av_init_packet(&avpkt); if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { if (!m_bFFReordering) { m_tcThreadBuffer[m_CurrentThread].rtStart = rtStartIn; m_tcThreadBuffer[m_CurrentThread].rtStop = rtStopIn; } m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } else if (m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos].rtStart = rtStartIn; m_tcBFrameDelay[m_nBFramePos].rtStop = rtStopIn; m_nBFramePos = !m_nBFramePos; } uint8_t *pDataBuffer = nullptr; if (!bFlush && buflen > 0) { if (!m_bInputPadded && (!(m_pAVCtx->active_thread_type & FF_THREAD_FRAME) || m_pParser)) { // Copy bitstream into temporary buffer to ensure overread protection // Verify buffer size if (buflen > m_nFFBufferSize) { m_nFFBufferSize = buflen; m_pFFBuffer = (BYTE *)av_realloc_f(m_pFFBuffer, m_nFFBufferSize + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer) { m_nFFBufferSize = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer, buffer, buflen); memset(m_pFFBuffer+buflen, 0, FF_INPUT_BUFFER_PADDING_SIZE); pDataBuffer = m_pFFBuffer; } else { pDataBuffer = (uint8_t *)buffer; } if (m_nCodecId == AV_CODEC_ID_VP8 && m_bWaitingForKeyFrame) { if (!(pDataBuffer[0] & 1)) { DbgLog((LOG_TRACE, 10, L"::Decode(): Found VP8 key-frame, resuming decoding")); m_bWaitingForKeyFrame = FALSE; } else { return S_OK; } } } while (buflen > 0 || bFlush) { REFERENCE_TIME rtStart = rtStartIn, rtStop = rtStopIn; if (!bFlush) { avpkt.data = pDataBuffer; avpkt.size = buflen; avpkt.pts = rtStartIn; if (rtStartIn != AV_NOPTS_VALUE && rtStopIn != AV_NOPTS_VALUE) avpkt.duration = (int)(rtStopIn - rtStartIn); else avpkt.duration = 0; avpkt.flags = AV_PKT_FLAG_KEY; if (m_bHasPalette) { m_bHasPalette = FALSE; uint32_t *pal = (uint32_t *)av_packet_new_side_data(&avpkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); int pal_size = FFMIN((1 << m_pAVCtx->bits_per_coded_sample) << 2, m_pAVCtx->extradata_size); uint8_t *pal_src = m_pAVCtx->extradata + m_pAVCtx->extradata_size - pal_size; for (int i = 0; i < pal_size/4; i++) pal[i] = 0xFF<<24 | AV_RL32(pal_src+4*i); } } else { avpkt.data = nullptr; avpkt.size = 0; } // Parse the data if a parser is present // This is mandatory for MPEG-1/2 if (m_pParser) { BYTE *pOut = nullptr; int pOut_size = 0; used_bytes = av_parser_parse2(m_pParser, m_pAVCtx, &pOut, &pOut_size, avpkt.data, avpkt.size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (used_bytes == 0 && pOut_size == 0 && !bFlush) { DbgLog((LOG_TRACE, 50, L"::Decode() - could not process buffer, starving?")); break; } else if (used_bytes > 0) { buflen -= used_bytes; pDataBuffer += used_bytes; } // Update start time cache // If more data was read then output, update the cache (incomplete frame) // If output is bigger, a frame was completed, update the actual rtStart with the cached value, and then overwrite the cache if (used_bytes > pOut_size) { if (rtStartIn != AV_NOPTS_VALUE) m_rtStartCache = rtStartIn; } else if (used_bytes == pOut_size || ((used_bytes + 9) == pOut_size)) { // Why +9 above? // Well, apparently there are some broken MKV muxers that like to mux the MPEG-2 PICTURE_START_CODE block (which is 9 bytes) in the package with the previous frame // This would cause the frame timestamps to be delayed by one frame exactly, and cause timestamp reordering to go wrong. // So instead of failing on those samples, lets just assume that 9 bytes are that case exactly. m_rtStartCache = rtStartIn = AV_NOPTS_VALUE; } else if (pOut_size > used_bytes) { rtStart = m_rtStartCache; m_rtStartCache = rtStartIn; // The value was used once, don't use it for multiple frames, that ends up in weird timings rtStartIn = AV_NOPTS_VALUE; } if (pOut_size > 0 || bFlush) { if (pOut && pOut_size > 0) { if (pOut_size > m_nFFBufferSize2) { m_nFFBufferSize2 = pOut_size; m_pFFBuffer2 = (BYTE *)av_realloc_f(m_pFFBuffer2, m_nFFBufferSize2 + FF_INPUT_BUFFER_PADDING_SIZE, 1); if (!m_pFFBuffer2) { m_nFFBufferSize2 = 0; return E_OUTOFMEMORY; } } memcpy(m_pFFBuffer2, pOut, pOut_size); memset(m_pFFBuffer2+pOut_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); avpkt.data = m_pFFBuffer2; avpkt.size = pOut_size; avpkt.pts = rtStart; avpkt.duration = 0; const uint8_t *eosmarker = CheckForEndOfSequence(m_nCodecId, avpkt.data, avpkt.size, &m_MpegParserState); if (eosmarker) { bEndOfSequence = TRUE; } } else { avpkt.data = nullptr; avpkt.size = 0; } int ret2 = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); if (ret2 < 0) { DbgLog((LOG_TRACE, 50, L"::Decode() - decoding failed despite successfull parsing")); got_picture = 0; } } else { got_picture = 0; } } else { used_bytes = avcodec_decode_video2 (m_pAVCtx, m_pFrame, &got_picture, &avpkt); buflen = 0; } if (FAILED(PostDecode())) { av_frame_unref(m_pFrame); return E_FAIL; } // Decoding of this frame failed ... oh well! if (used_bytes < 0) { av_frame_unref(m_pFrame); return S_OK; } // Judge frame usability // This determines if a frame is artifact free and can be delivered. if (m_bResumeAtKeyFrame) { if (m_bWaitingForKeyFrame && got_picture) { if (m_pFrame->key_frame) { DbgLog((LOG_TRACE, 50, L"::Decode() - Found Key-Frame, resuming decoding at %I64d", m_pFrame->pkt_pts)); m_bWaitingForKeyFrame = FALSE; } else { got_picture = 0; } } } // Handle B-frame delay for frame threading codecs if ((m_pAVCtx->active_thread_type & FF_THREAD_FRAME) && m_bBFrameDelay) { m_tcBFrameDelay[m_nBFramePos] = m_tcThreadBuffer[m_CurrentThread]; m_nBFramePos = !m_nBFramePos; } if (!got_picture || !m_pFrame->data[0]) { if (!avpkt.size) bFlush = FALSE; // End flushing, no more frames av_frame_unref(m_pFrame); continue; } /////////////////////////////////////////////////////////////////////////////////////////////// // Determine the proper timestamps for the frame, based on different possible flags. /////////////////////////////////////////////////////////////////////////////////////////////// if (m_bFFReordering) { rtStart = m_pFrame->pkt_pts; if (m_pFrame->pkt_duration) rtStop = m_pFrame->pkt_pts + m_pFrame->pkt_duration; else rtStop = AV_NOPTS_VALUE; } else if (m_bBFrameDelay && m_pAVCtx->has_b_frames) { rtStart = m_tcBFrameDelay[m_nBFramePos].rtStart; rtStop = m_tcBFrameDelay[m_nBFramePos].rtStop; } else if (m_pAVCtx->active_thread_type & FF_THREAD_FRAME) { unsigned index = m_CurrentThread; rtStart = m_tcThreadBuffer[index].rtStart; rtStop = m_tcThreadBuffer[index].rtStop; } if (m_bRVDropBFrameTimings && m_pFrame->pict_type == AV_PICTURE_TYPE_B) { rtStart = AV_NOPTS_VALUE; } if (m_bCalculateStopTime) rtStop = AV_NOPTS_VALUE; /////////////////////////////////////////////////////////////////////////////////////////////// // All required values collected, deliver the frame /////////////////////////////////////////////////////////////////////////////////////////////// LAVFrame *pOutFrame = nullptr; AllocateFrame(&pOutFrame); AVRational display_aspect_ratio; int64_t num = (int64_t)m_pFrame->sample_aspect_ratio.num * m_pFrame->width; int64_t den = (int64_t)m_pFrame->sample_aspect_ratio.den * m_pFrame->height; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, num, den, INT_MAX); pOutFrame->width = m_pFrame->width; pOutFrame->height = m_pFrame->height; pOutFrame->aspect_ratio = display_aspect_ratio; pOutFrame->repeat = m_pFrame->repeat_pict; pOutFrame->key_frame = m_pFrame->key_frame; pOutFrame->frame_type = av_get_picture_type_char(m_pFrame->pict_type); pOutFrame->ext_format = GetDXVA2ExtendedFlags(m_pAVCtx, m_pFrame); if (m_pFrame->interlaced_frame || (!m_pAVCtx->progressive_sequence && (m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO))) m_iInterlaced = 1; else if (m_pAVCtx->progressive_sequence) m_iInterlaced = 0; if ((m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_MPEG2VIDEO) && m_pFrame->repeat_pict) m_nSoftTelecine = 2; else if (m_nSoftTelecine > 0) m_nSoftTelecine--; // Don't apply aggressive deinterlacing to content that looks soft-telecined, as it would destroy the content bool bAggressiveFlag = (m_iInterlaced == 1 && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) && !m_nSoftTelecine; pOutFrame->interlaced = (m_pFrame->interlaced_frame || bAggressiveFlag || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pOutFrame->tff = (fo == DeintFieldOrder_Auto) ? m_pFrame->top_field_first : (fo == DeintFieldOrder_TopFieldFirst); pOutFrame->rtStart = rtStart; pOutFrame->rtStop = rtStop; PixelFormatMapping map = getPixFmtMapping((AVPixelFormat)m_pFrame->format); pOutFrame->format = map.lavpixfmt; pOutFrame->bpp = map.bpp; if (m_nCodecId == AV_CODEC_ID_MPEG2VIDEO || m_nCodecId == AV_CODEC_ID_MPEG1VIDEO) pOutFrame->avgFrameDuration = GetFrameDuration(); AVFrameSideData * sdHDR = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_HDR_MASTERING_INFO); if (sdHDR) { if (sdHDR->size == 24) { MediaSideDataHDR * hdr = (MediaSideDataHDR *)AddLAVFrameSideData(pOutFrame, IID_MediaSideDataHDR, sizeof(MediaSideDataHDR)); if (hdr) { CByteParser hdrParser(sdHDR->data, sdHDR->size); for (int i = 0; i < 3; i++) { hdr->display_primaries_x[i] = hdrParser.BitRead(16) * 0.00002; hdr->display_primaries_y[i] = hdrParser.BitRead(16) * 0.00002; } hdr->white_point_x = hdrParser.BitRead(16) * 0.00002; hdr->white_point_y = hdrParser.BitRead(16) * 0.00002; hdr->max_display_mastering_luminance = hdrParser.BitRead(32) * 0.0001; hdr->min_display_mastering_luminance = hdrParser.BitRead(32) * 0.0001; } } else { DbgLog((LOG_TRACE, 10, L"::Decode(): Found HDR data of an unexpected size (%d)", sdHDR->size)); } } if (map.conversion) { ConvertPixFmt(m_pFrame, pOutFrame); } else { AVFrame *pFrameRef = av_frame_alloc(); av_frame_ref(pFrameRef, m_pFrame); for (int i = 0; i < 4; i++) { pOutFrame->data[i] = pFrameRef->data[i]; pOutFrame->stride[i] = pFrameRef->linesize[i]; } pOutFrame->priv_data = pFrameRef; pOutFrame->destruct = lav_avframe_free; // Check alignment on rawvideo, which can be off depending on the source file if (m_nCodecId == AV_CODEC_ID_RAWVIDEO) { for (int i = 0; i < 4; i++) { if ((intptr_t)pOutFrame->data[i] % 16u || pOutFrame->stride[i] % 16u) { // copy the frame, its not aligned properly and would crash later CopyLAVFrameInPlace(pOutFrame); break; } } } } if (bEndOfSequence) pOutFrame->flags |= LAV_FRAME_FLAG_END_OF_SEQUENCE; if (pOutFrame->format == LAVPixFmt_DXVA2) { pOutFrame->data[0] = m_pFrame->data[4]; HandleDXVA2Frame(pOutFrame); } else { Deliver(pOutFrame); } if (bEndOfSequence) { bEndOfSequence = FALSE; if (pOutFrame->format == LAVPixFmt_DXVA2) { HandleDXVA2Frame(m_pCallback->GetFlushFrame()); } else { Deliver(m_pCallback->GetFlushFrame()); } } if (bFlush) { m_CurrentThread = (m_CurrentThread + 1) % m_pAVCtx->thread_count; } av_frame_unref(m_pFrame); } return S_OK; }