void Decoder::Write(const char* aBuffer, uint32_t aCount) { PROFILER_LABEL("ImageDecoder", "Write"); // We're strict about decoder errors NS_ABORT_IF_FALSE(!HasDecoderError(), "Not allowed to make more decoder calls after error!"); // If a data error occured, just ignore future data if (HasDataError()) return; if (IsSizeDecode() && HasSize()) { // More data came in since we found the size. We have nothing to do here. return; } // Pass the data along to the implementation WriteInternal(aBuffer, aCount); // If we're a synchronous decoder and we need a new frame to proceed, let's // create one and call it again. while (mSynchronous && NeedsNewFrame() && !HasDataError()) { nsresult rv = AllocateFrame(); if (NS_SUCCEEDED(rv)) { // Tell the decoder to use the data it saved when it asked for a new frame. WriteInternal(nullptr, 0); } } }
HRESULT CDecMSDKMVC::DeliverOutput(MVCBuffer * pBaseView, MVCBuffer * pExtraView) { mfxStatus sts = MFX_ERR_NONE; ASSERT(pBaseView->surface.Info.FrameId.ViewId == 0 && pExtraView->surface.Info.FrameId.ViewId > 0); ASSERT(pBaseView->surface.Data.FrameOrder == pExtraView->surface.Data.FrameOrder); // Sync base view do { sts = MFXVideoCORE_SyncOperation(m_mfxSession, pBaseView->sync, 1000); } while (sts == MFX_WRN_IN_EXECUTION); pBaseView->sync = nullptr; // Sync extra view do { sts = MFXVideoCORE_SyncOperation(m_mfxSession, pExtraView->sync, 1000); } while (sts == MFX_WRN_IN_EXECUTION); pExtraView->sync = nullptr; LAVFrame *pFrame = nullptr; AllocateFrame(&pFrame); pFrame->width = pBaseView->surface.Info.CropW; pFrame->height = pBaseView->surface.Info.CropH; pFrame->data[0] = pBaseView->surface.Data.Y; pFrame->data[1] = pBaseView->surface.Data.UV; pFrame->stereo[0] = pExtraView->surface.Data.Y; pFrame->stereo[1] = pExtraView->surface.Data.UV; pFrame->data[2] = (uint8_t *)pBaseView; pFrame->data[3] = (uint8_t *)pExtraView; pFrame->stride[0] = pBaseView->surface.Data.PitchLow; pFrame->stride[1] = pBaseView->surface.Data.PitchLow; pFrame->format = LAVPixFmt_NV12; pFrame->bpp = 8; pFrame->flags |= LAV_FRAME_FLAG_MVC; if (!(pBaseView->surface.Data.DataFlag & MFX_FRAMEDATA_ORIGINAL_TIMESTAMP)) pBaseView->surface.Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; if (pBaseView->surface.Data.TimeStamp != MFX_TIMESTAMP_UNKNOWN) { pFrame->rtStart = pBaseView->surface.Data.TimeStamp; pFrame->rtStart -= TIMESTAMP_OFFSET; } else { pFrame->rtStart = AV_NOPTS_VALUE; } int64_t num = (int64_t)pBaseView->surface.Info.AspectRatioW * pFrame->width; int64_t den = (int64_t)pBaseView->surface.Info.AspectRatioH * pFrame->height; av_reduce(&pFrame->aspect_ratio.num, &pFrame->aspect_ratio.den, num, den, INT_MAX); pFrame->destruct = msdk_buffer_destruct; pFrame->priv_data = this; GetOffsetSideData(pFrame, pBaseView->surface.Data.TimeStamp); return Deliver(pFrame); }
void Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { PROFILER_LABEL("ImageDecoder", "Write", js::ProfileEntry::Category::GRAPHICS); MOZ_ASSERT(NS_IsMainThread() || aStrategy == DecodeStrategy::ASYNC); // We're strict about decoder errors MOZ_ASSERT(!HasDecoderError(), "Not allowed to make more decoder calls after error!"); // Begin recording telemetry data. TimeStamp start = TimeStamp::Now(); mChunkCount++; // Keep track of the total number of bytes written. mBytesDecoded += aCount; // If we're flushing data, clear the flag. if (aBuffer == nullptr && aCount == 0) { MOZ_ASSERT(mNeedsToFlushData, "Flushing when we don't need to"); mNeedsToFlushData = false; } // If a data error occured, just ignore future data. if (HasDataError()) return; if (IsSizeDecode() && HasSize()) { // More data came in since we found the size. We have nothing to do here. return; } // Pass the data along to the implementation WriteInternal(aBuffer, aCount, aStrategy); // If we're a synchronous decoder and we need a new frame to proceed, let's // create one and call it again. if (aStrategy == DecodeStrategy::SYNC) { while (NeedsNewFrame() && !HasDataError()) { nsresult rv = AllocateFrame(); if (NS_SUCCEEDED(rv)) { // Use the data we saved when we asked for a new frame. WriteInternal(nullptr, 0, aStrategy); } mNeedsToFlushData = false; } } // Finish telemetry. mDecodeTime += (TimeStamp::Now() - start); }
//****************************************************************************** nsresult nsGIFDecoder2::BeginImageFrame(uint16_t aDepth) { MOZ_ASSERT(HasSize()); gfx::SurfaceFormat format; if (mGIFStruct.is_transparent) { format = gfx::SurfaceFormat::B8G8R8A8; PostHasTransparency(); } else { format = gfx::SurfaceFormat::B8G8R8X8; } nsIntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height); // Use correct format, RGB for first frame, PAL for following frames // and include transparency to allow for optimization of opaque images nsresult rv = NS_OK; if (mGIFStruct.images_decoded) { // Image data is stored with original depth and palette. rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(), frameRect, format, aDepth); } else { if (!nsIntRect(nsIntPoint(), GetSize()).IsEqualEdges(frameRect)) { // We need padding on the first frame, which means that we don't draw into // part of the image at all. Report that as transparency. PostHasTransparency(); } // Regardless of depth of input, the first frame is decoded into 24bit RGB. rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(), frameRect, format); } mCurrentFrameIndex = mGIFStruct.images_decoded; return rv; }
void nsJPEGDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) { mSegment = (const JOCTET*)aBuffer; mSegmentLen = aCount; MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!"); // Return here if there is a fatal error within libjpeg. nsresult error_code; // This cast to nsresult makes sense because setjmp() returns whatever we // passed to longjmp(), which was actually an nsresult. if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) { if (error_code == NS_ERROR_FAILURE) { PostDataError(); // Error due to corrupt stream - return NS_OK and consume silently // so that ImageLib doesn't throw away a partial image load mState = JPEG_SINK_NON_JPEG_TRAILER; MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (setjmp returned NS_ERROR_FAILURE)")); return; } else { // Error due to reasons external to the stream (probably out of // memory) - let ImageLib attempt to clean up, even though // mozilla is seconds away from falling flat on its face. PostDecoderError(error_code); mState = JPEG_ERROR; MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (setjmp returned an error)")); return; } } MOZ_LOG(GetJPEGLog(), LogLevel::Debug, ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); switch (mState) { case JPEG_HEADER: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER" " case"); // Step 3: read file parameters with jpeg_read_header() if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (JPEG_SUSPENDED)")); return; // I/O suspension } // If we have a sample size specified for -moz-sample-size, use it. if (mSampleSize > 0) { mInfo.scale_num = 1; mInfo.scale_denom = mSampleSize; } // Used to set up image size so arrays can be allocated jpeg_calc_output_dimensions(&mInfo); // Post our size to the superclass PostSize(mInfo.output_width, mInfo.output_height, ReadOrientationFromEXIF()); if (HasError()) { // Setting the size led to an error. mState = JPEG_ERROR; return; } // If we're doing a metadata decode, we're done. if (IsMetadataDecode()) { return; } // We're doing a full decode. if (mCMSMode != eCMSMode_Off && (mInProfile = GetICCProfile(mInfo)) != nullptr) { uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); bool mismatch = false; #ifdef DEBUG_tor fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); #endif switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else if (profileSpace != icSigGrayData) { mismatch = true; } break; case JCS_RGB: if (profileSpace != icSigRgbData) { mismatch = true; } break; case JCS_YCbCr: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else { // qcms doesn't support ycbcr mismatch = true; } break; case JCS_CMYK: case JCS_YCCK: // qcms doesn't support cmyk mismatch = true; break; default: mState = JPEG_ERROR; PostDataError(); MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (unknown colorpsace (1))")); return; } if (!mismatch) { qcms_data_type type; switch (mInfo.out_color_space) { case JCS_GRAYSCALE: type = QCMS_DATA_GRAY_8; break; case JCS_RGB: type = QCMS_DATA_RGB_8; break; default: mState = JPEG_ERROR; PostDataError(); MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (unknown colorpsace (2))")); return; } #if 0 // We don't currently support CMYK profiles. The following // code dealt with lcms types. Add something like this // back when we gain support for CMYK. // Adobe Photoshop writes YCCK/CMYK files with inverted data if (mInfo.out_color_space == JCS_CMYK) { type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); } #endif if (gfxPlatform::GetCMSOutputProfile()) { // Calculate rendering intent. int intent = gfxPlatform::GetRenderingIntent(); if (intent == -1) { intent = qcms_profile_get_rendering_intent(mInProfile); } // Create the color management transform. mTransform = qcms_transform_create(mInProfile, type, gfxPlatform::GetCMSOutputProfile(), QCMS_DATA_RGB_8, (qcms_intent)intent); } } else { #ifdef DEBUG_tor fprintf(stderr, "ICM profile colorspace mismatch\n"); #endif } } if (!mTransform) { switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // if we're not color managing we can decode directly to // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB if (mCMSMode != eCMSMode_All) { mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; mInfo.out_color_components = 4; } else { mInfo.out_color_space = JCS_RGB; } break; case JCS_CMYK: case JCS_YCCK: // libjpeg can convert from YCCK to CMYK, but not to RGB mInfo.out_color_space = JCS_CMYK; break; default: mState = JPEG_ERROR; PostDataError(); MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (unknown colorpsace (3))")); return; } } // Don't allocate a giant and superfluous memory buffer // when not doing a progressive decode. mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); MOZ_ASSERT(!mImageData, "Already have a buffer allocated?"); nsIntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize(); nsresult rv = AllocateFrame(0, targetSize, nsIntRect(nsIntPoint(), targetSize), gfx::SurfaceFormat::B8G8R8A8); if (NS_FAILED(rv)) { mState = JPEG_ERROR; MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (could not initialize image frame)")); return; } MOZ_ASSERT(mImageData, "Should have a buffer now"); if (mDownscaler) { nsresult rv = mDownscaler->BeginFrame(GetSize(), mImageData, /* aHasAlpha = */ false); if (NS_FAILED(rv)) { mState = JPEG_ERROR; return; } } MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, (" JPEGDecoderAccounting: nsJPEGDecoder::" "Write -- created image frame with %ux%u pixels", mInfo.output_width, mInfo.output_height)); mState = JPEG_START_DECOMPRESS; } case JPEG_START_DECOMPRESS: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering" " JPEG_START_DECOMPRESS case"); // Step 4: set parameters for decompression // FIXME -- Should reset dct_method and dither mode // for final pass of progressive JPEG mInfo.dct_method = JDCT_ISLOW; mInfo.dither_mode = JDITHER_FS; mInfo.do_fancy_upsampling = TRUE; mInfo.enable_2pass_quant = FALSE; mInfo.do_block_smoothing = TRUE; // Step 5: Start decompressor if (jpeg_start_decompress(&mInfo) == FALSE) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after jpeg_start_decompress())")); return; // I/O suspension } // If this is a progressive JPEG ... mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; } case JPEG_DECOMPRESS_SEQUENTIAL: { if (mState == JPEG_DECOMPRESS_SEQUENTIAL) { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- " "JPEG_DECOMPRESS_SEQUENTIAL case"); bool suspend; OutputScanlines(&suspend); if (suspend) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); return; // I/O suspension } // If we've completed image output ... NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); mState = JPEG_DONE; } } case JPEG_DECOMPRESS_PROGRESSIVE: { if (mState == JPEG_DECOMPRESS_PROGRESSIVE) { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); int status; do { status = jpeg_consume_input(&mInfo); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (mInfo.output_scanline == 0) { int scan = mInfo.input_scan_number; // if we haven't displayed anything yet (output_scan_number==0) // and we have enough data for a complete scan, force output // of the last full scan if ((mInfo.output_scan_number == 0) && (scan > 1) && (status != JPEG_REACHED_EOI)) scan--; if (!jpeg_start_output(&mInfo, scan)) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after jpeg_start_output() -" " PROGRESSIVE)")); return; // I/O suspension } } if (mInfo.output_scanline == 0xffffff) { mInfo.output_scanline = 0; } bool suspend; OutputScanlines(&suspend); if (suspend) { if (mInfo.output_scanline == 0) { // didn't manage to read any lines - flag so we don't call // jpeg_start_output() multiple times for the same scan mInfo.output_scanline = 0xffffff; } MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); return; // I/O suspension } if (mInfo.output_scanline == mInfo.output_height) { if (!jpeg_finish_output(&mInfo)) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after jpeg_finish_output() -" " PROGRESSIVE)")); return; // I/O suspension } if (jpeg_input_complete(&mInfo) && (mInfo.input_scan_number == mInfo.output_scan_number)) break; mInfo.output_scanline = 0; if (mDownscaler) { mDownscaler->ResetForNextProgressivePass(); } } } mState = JPEG_DONE; } } case JPEG_DONE: { LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering" " JPEG_DONE case"); // Step 7: Finish decompression if (jpeg_finish_decompress(&mInfo) == FALSE) { MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); return; // I/O suspension } mState = JPEG_SINK_NON_JPEG_TRAILER; // we're done dude break; } case JPEG_SINK_NON_JPEG_TRAILER: MOZ_LOG(GetJPEGLog(), LogLevel::Debug, ("[this=%p] nsJPEGDecoder::ProcessData -- entering" " JPEG_SINK_NON_JPEG_TRAILER case\n", this)); break; case JPEG_ERROR: MOZ_ASSERT(false, "Should always return immediately after error and not re-enter " "decoder"); } MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug, ("} (end of function)")); return; }
STDMETHODIMP CDecWMV9::ProcessOutput() { HRESULT hr = S_OK; DWORD dwStatus = 0; BYTE *pBuffer = GetBuffer(m_pRawBufferSize); CMediaBuffer *pOutBuffer = new CMediaBuffer(pBuffer, m_pRawBufferSize, true); pOutBuffer->SetLength(0); DMO_OUTPUT_DATA_BUFFER OutputBufferStructs[1]; memset(&OutputBufferStructs[0], 0, sizeof(DMO_OUTPUT_DATA_BUFFER)); OutputBufferStructs[0].pBuffer = pOutBuffer; hr = m_pDMO->ProcessOutput(0, 1, OutputBufferStructs, &dwStatus); if (FAILED(hr)) { ReleaseBuffer(pBuffer); DbgLog((LOG_TRACE, 10, L"-> ProcessOutput failed with hr: %x", hr)); return S_FALSE; } if (hr == S_FALSE) { ReleaseBuffer(pBuffer); return S_FALSE; } LAVFrame *pFrame = NULL; AllocateFrame(&pFrame); BITMAPINFOHEADER *pBMI = NULL; videoFormatTypeHandler(mtOut, &pBMI); pFrame->width = pBMI->biWidth; pFrame->height = pBMI->biHeight; pFrame->format = m_OutPixFmt; pFrame->key_frame = (OutputBufferStructs[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT); AVRational display_aspect_ratio; int64_t num = (int64_t)m_StreamAR.num * pBMI->biWidth; int64_t den = (int64_t)m_StreamAR.den * pBMI->biHeight; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, num, den, 1 << 30); BYTE contentType = 0; DWORD dwPropSize = 1; pOutBuffer->GetProperty(WM_SampleExtensionGUID_ContentType, &contentType, &dwPropSize); pFrame->interlaced = !!(contentType & WM_CT_INTERLACED); pFrame->repeat = !!(contentType & WM_CT_REPEAT_FIRST_FIELD); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pFrame->tff = (fo == DeintFieldOrder_Auto) ? !!(contentType & WM_CT_TOP_FIELD_FIRST) : (fo == DeintFieldOrder_TopFieldFirst); if (pFrame->interlaced && !m_bInterlaced) m_bInterlaced = TRUE; pFrame->interlaced = (pFrame->interlaced || (m_bInterlaced && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); if (m_bManualReorder) { if (!m_timestampQueue.empty()) { pFrame->rtStart = m_timestampQueue.front(); m_timestampQueue.pop(); if (OutputBufferStructs[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH) { pFrame->rtStop = pFrame->rtStart + OutputBufferStructs[0].rtTimelength; } } } else { if (OutputBufferStructs[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIME) { pFrame->rtStart = OutputBufferStructs[0].rtTimestamp; if (OutputBufferStructs[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH) { pFrame->rtStop = pFrame->rtStart + OutputBufferStructs[0].rtTimelength; } } } // Check alignment // If not properly aligned, we need to make the data aligned. int alignment = (m_OutPixFmt == LAVPixFmt_NV12) ? 16 : 32; if ((pFrame->width % alignment) != 0) { AllocLAVFrameBuffers(pFrame); size_t ySize = pFrame->width * pFrame->height; memcpy_plane(pFrame->data[0], pBuffer, pFrame->width, pFrame->stride[0], pFrame->height); if (m_OutPixFmt == LAVPixFmt_NV12) { memcpy_plane(pFrame->data[1], pBuffer+ySize, pFrame->width, pFrame->stride[1], pFrame->height / 2); } else if (m_OutPixFmt == LAVPixFmt_YUV420) { size_t uvSize = ySize / 4; memcpy_plane(pFrame->data[2], pBuffer+ySize, pFrame->width / 2, pFrame->stride[2], pFrame->height / 2); memcpy_plane(pFrame->data[1], pBuffer+ySize+uvSize, pFrame->width / 2, pFrame->stride[1], pFrame->height / 2); } ReleaseBuffer(pBuffer); } else { if (m_OutPixFmt == LAVPixFmt_NV12) { pFrame->data[0] = pBuffer; pFrame->data[1] = pBuffer + pFrame->width * pFrame->height; pFrame->stride[0] = pFrame->stride[1] = pFrame->width; } else if (m_OutPixFmt == LAVPixFmt_YUV420) { pFrame->data[0] = pBuffer; pFrame->data[2] = pBuffer + pFrame->width * pFrame->height; pFrame->data[1] = pFrame->data[2] + (pFrame->width / 2) * (pFrame->height / 2); pFrame->stride[0] = pFrame->width; pFrame->stride[1] = pFrame->stride[2] = pFrame->width / 2; } pFrame->destruct = wmv9_buffer_destruct; pFrame->priv_data = this; } pFrame->flags |= LAV_FRAME_FLAG_BUFFER_MODIFY; Deliver(pFrame); SafeRelease(&pOutBuffer); if (OutputBufferStructs[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE) return ProcessOutput(); return hr; }
STDMETHODIMP CDecWMV9MFT::ProcessOutput() { HRESULT hr = S_OK; DWORD dwStatus = 0; MFT_OUTPUT_STREAM_INFO outputInfo = {0}; m_pMFT->GetOutputStreamInfo(0, &outputInfo); IMFMediaBuffer *pMFBuffer = nullptr; ASSERT(!(outputInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)); MFT_OUTPUT_DATA_BUFFER OutputBuffer = {0}; if (!(outputInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) { pMFBuffer = GetBuffer(outputInfo.cbSize); if (!pMFBuffer) { DbgLog((LOG_TRACE, 10, L"Unable to allocate media buffere")); return E_FAIL; } IMFSample *pSampleOut = nullptr; hr = MF.CreateSample(&pSampleOut); if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"Unable to allocate MF sample, hr: 0x%x", hr)); ReleaseBuffer(pMFBuffer); return E_FAIL; } pSampleOut->AddBuffer(pMFBuffer); OutputBuffer.pSample = pSampleOut; } hr = m_pMFT->ProcessOutput(0, 1, &OutputBuffer, &dwStatus); // We don't process events, just release them SafeRelease(&OutputBuffer.pEvents); // handle stream format changes if (hr == MF_E_TRANSFORM_STREAM_CHANGE || OutputBuffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE ) { SafeRelease(&OutputBuffer.pSample); ReleaseBuffer(pMFBuffer); hr = SelectOutputType(); if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"-> Failed to handle stream change, hr: %x", hr)); return E_FAIL; } // try again with the new type, it should work now! return ProcessOutput(); } // the MFT generated no output, discard the sample and return if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || OutputBuffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) { SafeRelease(&OutputBuffer.pSample); ReleaseBuffer(pMFBuffer); return S_FALSE; } // unknown error condition if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"-> ProcessOutput failed with hr: %x", hr)); SafeRelease(&OutputBuffer.pSample); ReleaseBuffer(pMFBuffer); return E_FAIL; } LAVFrame *pFrame = nullptr; AllocateFrame(&pFrame); IMFMediaType *pMTOut = nullptr; m_pMFT->GetOutputCurrentType(0, &pMTOut); MFGetAttributeSize(pMTOut, MF_MT_FRAME_SIZE, (UINT32 *)&pFrame->width, (UINT32 *)&pFrame->height); pFrame->format = m_OutPixFmt; AVRational pixel_aspect_ratio = {1, 1}; MFGetAttributeRatio(pMTOut, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&pixel_aspect_ratio.num, (UINT32*)&pixel_aspect_ratio.den); AVRational display_aspect_ratio = {0, 0}; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, (int64_t)pixel_aspect_ratio.num * pFrame->width, (int64_t)pixel_aspect_ratio.den * pFrame->height, INT_MAX); pFrame->aspect_ratio = display_aspect_ratio; pFrame->interlaced = MFGetAttributeUINT32(OutputBuffer.pSample, MFSampleExtension_Interlaced, FALSE); pFrame->repeat = MFGetAttributeUINT32(OutputBuffer.pSample, MFSampleExtension_RepeatFirstField, FALSE); LAVDeintFieldOrder fo = m_pSettings->GetDeintFieldOrder(); pFrame->tff = (fo == DeintFieldOrder_Auto) ? !MFGetAttributeUINT32(OutputBuffer.pSample, MFSampleExtension_BottomFieldFirst, FALSE) : (fo == DeintFieldOrder_TopFieldFirst); if (pFrame->interlaced && !m_bInterlaced) m_bInterlaced = TRUE; pFrame->interlaced = (pFrame->interlaced || (m_bInterlaced && m_pSettings->GetDeinterlacingMode() == DeintMode_Aggressive) || m_pSettings->GetDeinterlacingMode() == DeintMode_Force) && !(m_pSettings->GetDeinterlacingMode() == DeintMode_Disable); pFrame->ext_format.VideoPrimaries = MFGetAttributeUINT32(pMTOut, MF_MT_VIDEO_PRIMARIES, MFVideoPrimaries_Unknown); pFrame->ext_format.VideoTransferFunction = MFGetAttributeUINT32(pMTOut, MF_MT_TRANSFER_FUNCTION, MFVideoTransFunc_Unknown); pFrame->ext_format.VideoTransferMatrix = MFGetAttributeUINT32(pMTOut, MF_MT_YUV_MATRIX, MFVideoTransferMatrix_Unknown); pFrame->ext_format.VideoChromaSubsampling = MFGetAttributeUINT32(pMTOut, MF_MT_VIDEO_CHROMA_SITING, MFVideoChromaSubsampling_Unknown); pFrame->ext_format.NominalRange = MFGetAttributeUINT32(pMTOut, MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Unknown); // HACK: don't flag range=limited if its the only value set, since its also the implied default, this helps to avoid a reconnect // The MFT always sets this value, even if the bitstream says nothing about it, causing a reconnect on every vc1/wmv3 file if (pFrame->ext_format.value == 0x2000) pFrame->ext_format.value = 0; // Timestamps if (m_bManualReorder) { if (!m_timestampQueue.empty()) { pFrame->rtStart = m_timestampQueue.front(); m_timestampQueue.pop(); LONGLONG llDuration = 0; hr = OutputBuffer.pSample->GetSampleDuration(&llDuration); if (SUCCEEDED(hr) && llDuration > 0) { pFrame->rtStop = pFrame->rtStart + llDuration; } } } else { LONGLONG llTimestamp = 0; hr = OutputBuffer.pSample->GetSampleTime(&llTimestamp); if (SUCCEEDED(hr)) { pFrame->rtStart = llTimestamp; LONGLONG llDuration = 0; hr = OutputBuffer.pSample->GetSampleDuration(&llDuration); if (SUCCEEDED(hr) && llDuration > 0) { pFrame->rtStop = pFrame->rtStart + llDuration; } } } SafeRelease(&pMTOut); // Lock memory in the buffer BYTE *pBuffer = nullptr; pMFBuffer->Lock(&pBuffer, NULL, NULL); // Check alignment // If not properly aligned, we need to make the data aligned. int alignment = (m_OutPixFmt == LAVPixFmt_NV12) ? 16 : 32; if ((pFrame->width % alignment) != 0) { hr = AllocLAVFrameBuffers(pFrame); if (FAILED(hr)) { pMFBuffer->Unlock(); ReleaseBuffer(pMFBuffer); SafeRelease(&OutputBuffer.pSample); return hr; } size_t ySize = pFrame->width * pFrame->height; memcpy_plane(pFrame->data[0], pBuffer, pFrame->width, pFrame->stride[0], pFrame->height); if (m_OutPixFmt == LAVPixFmt_NV12) { memcpy_plane(pFrame->data[1], pBuffer + ySize, pFrame->width, pFrame->stride[1], pFrame->height / 2); } else if (m_OutPixFmt == LAVPixFmt_YUV420) { size_t uvSize = ySize / 4; memcpy_plane(pFrame->data[2], pBuffer + ySize, pFrame->width / 2, pFrame->stride[2], pFrame->height / 2); memcpy_plane(pFrame->data[1], pBuffer + ySize + uvSize, pFrame->width / 2, pFrame->stride[1], pFrame->height / 2); } pMFBuffer->Unlock(); ReleaseBuffer(pMFBuffer); } else { if (m_OutPixFmt == LAVPixFmt_NV12) { pFrame->data[0] = pBuffer; pFrame->data[1] = pBuffer + pFrame->width * pFrame->height; pFrame->stride[0] = pFrame->stride[1] = pFrame->width; } else if (m_OutPixFmt == LAVPixFmt_YUV420) { pFrame->data[0] = pBuffer; pFrame->data[2] = pBuffer + pFrame->width * pFrame->height; pFrame->data[1] = pFrame->data[2] + (pFrame->width / 2) * (pFrame->height / 2); pFrame->stride[0] = pFrame->width; pFrame->stride[1] = pFrame->stride[2] = pFrame->width / 2; } pFrame->data[3] = (BYTE *)pMFBuffer; pFrame->destruct = wmv9_buffer_destruct; pFrame->priv_data = this; } pFrame->flags |= LAV_FRAME_FLAG_BUFFER_MODIFY; Deliver(pFrame); SafeRelease(&OutputBuffer.pSample); if (OutputBuffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE) return ProcessOutput(); return hr; }
LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(const char* aData, size_t aLength) { mSegment = reinterpret_cast<const JOCTET*>(aData); mSegmentLen = aLength; // Return here if there is a fatal error within libjpeg. nsresult error_code; // This cast to nsresult makes sense because setjmp() returns whatever we // passed to longjmp(), which was actually an nsresult. if ((error_code = static_cast<nsresult>(setjmp(mErr.setjmp_buffer))) != NS_OK) { if (error_code == NS_ERROR_FAILURE) { // Error due to corrupt data. Make sure that we don't feed any more data // to libjpeg-turbo. mState = JPEG_SINK_NON_JPEG_TRAILER; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (setjmp returned NS_ERROR_FAILURE)")); } else { // Error for another reason. (Possibly OOM.) mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (setjmp returned an error)")); } return Transition::TerminateFailure(); } MOZ_LOG(sJPEGLog, LogLevel::Debug, ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this)); switch (mState) { case JPEG_HEADER: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering JPEG_HEADER" " case"); // Step 3: read file parameters with jpeg_read_header() if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (JPEG_SUSPENDED)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // Post our size to the superclass PostSize(mInfo.image_width, mInfo.image_height, ReadOrientationFromEXIF()); if (HasError()) { // Setting the size led to an error. mState = JPEG_ERROR; return Transition::TerminateFailure(); } // If we're doing a metadata decode, we're done. if (IsMetadataDecode()) { return Transition::TerminateSuccess(); } // We're doing a full decode. if (mCMSMode != eCMSMode_Off && (mInProfile = GetICCProfile(mInfo)) != nullptr) { uint32_t profileSpace = qcms_profile_get_color_space(mInProfile); bool mismatch = false; #ifdef DEBUG_tor fprintf(stderr, "JPEG profileSpace: 0x%08X\n", profileSpace); #endif switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else if (profileSpace != icSigGrayData) { mismatch = true; } break; case JCS_RGB: if (profileSpace != icSigRgbData) { mismatch = true; } break; case JCS_YCbCr: if (profileSpace == icSigRgbData) { mInfo.out_color_space = JCS_RGB; } else { // qcms doesn't support ycbcr mismatch = true; } break; case JCS_CMYK: case JCS_YCCK: // qcms doesn't support cmyk mismatch = true; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (1))")); return Transition::TerminateFailure(); } if (!mismatch) { qcms_data_type type; switch (mInfo.out_color_space) { case JCS_GRAYSCALE: type = QCMS_DATA_GRAY_8; break; case JCS_RGB: type = QCMS_DATA_RGB_8; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (2))")); return Transition::TerminateFailure(); } #if 0 // We don't currently support CMYK profiles. The following // code dealt with lcms types. Add something like this // back when we gain support for CMYK. // Adobe Photoshop writes YCCK/CMYK files with inverted data if (mInfo.out_color_space == JCS_CMYK) { type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); } #endif if (gfxPlatform::GetCMSOutputProfile()) { // Calculate rendering intent. int intent = gfxPlatform::GetRenderingIntent(); if (intent == -1) { intent = qcms_profile_get_rendering_intent(mInProfile); } // Create the color management transform. mTransform = qcms_transform_create(mInProfile, type, gfxPlatform::GetCMSOutputProfile(), QCMS_DATA_RGB_8, (qcms_intent)intent); } } else { #ifdef DEBUG_tor fprintf(stderr, "ICM profile colorspace mismatch\n"); #endif } } if (!mTransform) { switch (mInfo.jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: // if we're not color managing we can decode directly to // MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB if (mCMSMode != eCMSMode_All) { mInfo.out_color_space = MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB; mInfo.out_color_components = 4; } else { mInfo.out_color_space = JCS_RGB; } break; case JCS_CMYK: case JCS_YCCK: // libjpeg can convert from YCCK to CMYK, but not to RGB mInfo.out_color_space = JCS_CMYK; break; default: mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (unknown colorpsace (3))")); return Transition::TerminateFailure(); } } // Don't allocate a giant and superfluous memory buffer // when not doing a progressive decode. mInfo.buffered_image = mDecodeStyle == PROGRESSIVE && jpeg_has_multiple_scans(&mInfo); /* Used to set up image size so arrays can be allocated */ jpeg_calc_output_dimensions(&mInfo); MOZ_ASSERT(!mImageData, "Already have a buffer allocated?"); nsresult rv = AllocateFrame(/* aFrameNum = */ 0, OutputSize(), FullOutputFrame(), SurfaceFormat::B8G8R8X8); if (NS_FAILED(rv)) { mState = JPEG_ERROR; MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (could not initialize image frame)")); return Transition::TerminateFailure(); } MOZ_ASSERT(mImageData, "Should have a buffer now"); if (mDownscaler) { nsresult rv = mDownscaler->BeginFrame(Size(), Nothing(), mImageData, /* aHasAlpha = */ false); if (NS_FAILED(rv)) { mState = JPEG_ERROR; return Transition::TerminateFailure(); } } MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, (" JPEGDecoderAccounting: nsJPEGDecoder::" "Write -- created image frame with %ux%u pixels", mInfo.image_width, mInfo.image_height)); mState = JPEG_START_DECOMPRESS; MOZ_FALLTHROUGH; // to start decompressing. } case JPEG_START_DECOMPRESS: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering" " JPEG_START_DECOMPRESS case"); // Step 4: set parameters for decompression // FIXME -- Should reset dct_method and dither mode // for final pass of progressive JPEG mInfo.dct_method = JDCT_ISLOW; mInfo.dither_mode = JDITHER_FS; mInfo.do_fancy_upsampling = TRUE; mInfo.enable_2pass_quant = FALSE; mInfo.do_block_smoothing = TRUE; // Step 5: Start decompressor if (jpeg_start_decompress(&mInfo) == FALSE) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_start_decompress())")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // If this is a progressive JPEG ... mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; MOZ_FALLTHROUGH; // to decompress sequential JPEG. } case JPEG_DECOMPRESS_SEQUENTIAL: { if (mState == JPEG_DECOMPRESS_SEQUENTIAL) { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- " "JPEG_DECOMPRESS_SEQUENTIAL case"); bool suspend; OutputScanlines(&suspend); if (suspend) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // If we've completed image output ... NS_ASSERTION(mInfo.output_scanline == mInfo.output_height, "We didn't process all of the data!"); mState = JPEG_DONE; } MOZ_FALLTHROUGH; // to decompress progressive JPEG. } case JPEG_DECOMPRESS_PROGRESSIVE: { if (mState == JPEG_DECOMPRESS_PROGRESSIVE) { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case"); int status; do { status = jpeg_consume_input(&mInfo); } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); for (;;) { if (mInfo.output_scanline == 0) { int scan = mInfo.input_scan_number; // if we haven't displayed anything yet (output_scan_number==0) // and we have enough data for a complete scan, force output // of the last full scan if ((mInfo.output_scan_number == 0) && (scan > 1) && (status != JPEG_REACHED_EOI)) scan--; if (!jpeg_start_output(&mInfo, scan)) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_start_output() -" " PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } } if (mInfo.output_scanline == 0xffffff) { mInfo.output_scanline = 0; } bool suspend; OutputScanlines(&suspend); if (suspend) { if (mInfo.output_scanline == 0) { // didn't manage to read any lines - flag so we don't call // jpeg_start_output() multiple times for the same scan mInfo.output_scanline = 0xffffff; } MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } if (mInfo.output_scanline == mInfo.output_height) { if (!jpeg_finish_output(&mInfo)) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_finish_output() -" " PROGRESSIVE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } if (jpeg_input_complete(&mInfo) && (mInfo.input_scan_number == mInfo.output_scan_number)) break; mInfo.output_scanline = 0; if (mDownscaler) { mDownscaler->ResetForNextProgressivePass(); } } } mState = JPEG_DONE; } MOZ_FALLTHROUGH; // to finish decompressing. } case JPEG_DONE: { LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::ProcessData -- entering" " JPEG_DONE case"); // Step 7: Finish decompression if (jpeg_finish_decompress(&mInfo) == FALSE) { MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug, ("} (I/O suspension after jpeg_finish_decompress() - DONE)")); return Transition::ContinueUnbuffered(State::JPEG_DATA); // I/O suspension } // Make sure we don't feed any more data to libjpeg-turbo. mState = JPEG_SINK_NON_JPEG_TRAILER; // We're done. return Transition::TerminateSuccess(); } case JPEG_SINK_NON_JPEG_TRAILER: MOZ_LOG(sJPEGLog, LogLevel::Debug, ("[this=%p] nsJPEGDecoder::ProcessData -- entering" " JPEG_SINK_NON_JPEG_TRAILER case\n", this)); MOZ_ASSERT_UNREACHABLE("Should stop getting data after entering state " "JPEG_SINK_NON_JPEG_TRAILER"); return Transition::TerminateSuccess(); case JPEG_ERROR: MOZ_ASSERT_UNREACHABLE("Should stop getting data after entering state " "JPEG_ERROR"); return Transition::TerminateFailure(); } MOZ_ASSERT_UNREACHABLE("Escaped the JPEG decoder state machine"); return Transition::TerminateFailure(); }
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; }
void CGameClient::SetupPackInfo( CFrameSnapshot *pSnapshot ) { // Compute Vis for each client m_PackInfo.m_nPVSSize = (GetCollisionBSPData()->numclusters + 7) / 8; serverGameClients->ClientSetupVisibility( (edict_t *)m_pViewEntity, m_PackInfo.m_pClientEnt, m_PackInfo.m_PVS, m_PackInfo.m_nPVSSize ); // This is the frame we are creating, i.e., the next // frame after the last one that the client acknowledged m_pCurrentFrame = AllocateFrame(); m_pCurrentFrame->Init( pSnapshot ); m_PackInfo.m_pTransmitEdict = &m_pCurrentFrame->transmit_entity; // if this client is the HLTV client, add the nocheck PVS bit array // normal clients don't need that extra array #ifndef _XBOX if ( IsHLTV() ) { // the hltv client doesn't has a ClientFrame list m_pCurrentFrame->transmit_always = new CBitVec<MAX_EDICTS>; m_PackInfo.m_pTransmitAlways = m_pCurrentFrame->transmit_always; } else #endif { m_PackInfo.m_pTransmitAlways = NULL; } // Add frame to ClientFrame list int nMaxFrames = MAX_CLIENT_FRAMES; if ( sv_maxreplay.GetFloat() > 0 ) { // if the server has replay features enabled, allow a way bigger frame buffer nMaxFrames = max ( nMaxFrames, sv_maxreplay.GetFloat() / m_Server->GetTickInterval() ); } if ( nMaxFrames < AddClientFrame( m_pCurrentFrame ) ) { // If the client has more than 64 frames, the server will start to eat too much memory. RemoveOldestFrame(); } // Since area to area visibility is determined by each player's PVS, copy // the area network lookups into the ClientPackInfo_t m_PackInfo.m_AreasNetworked = 0; int areaCount = g_AreasNetworked.Count(); for ( int j = 0; j < areaCount; j++ ) { m_PackInfo.m_Areas[m_PackInfo.m_AreasNetworked] = g_AreasNetworked[ j ]; m_PackInfo.m_AreasNetworked++; // Msg("CGameClient::SetupPackInfo: too much areas (%i)", areaCount ); Assert( m_PackInfo.m_AreasNetworked < MAX_WORLD_AREAS ); } CM_SetupAreaFloodNums( m_PackInfo.m_AreaFloodNums, &m_PackInfo.m_nMapAreas ); }