diff options
Diffstat (limited to '')
-rw-r--r-- | libfreerdp/codec/h264.c | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/libfreerdp/codec/h264.c b/libfreerdp/codec/h264.c new file mode 100644 index 0000000..718bd2c --- /dev/null +++ b/libfreerdp/codec/h264.c @@ -0,0 +1,777 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * H.264 Bitmap Compression + * + * Copyright 2014 Mike McDonald <Mike.McDonald@software.dell.com> + * Copyright 2017 David Fort <contact@hardening-consulting.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 <freerdp/config.h> + +#include <winpr/crt.h> +#include <winpr/print.h> +#include <winpr/library.h> +#include <winpr/bitstream.h> +#include <winpr/synch.h> + +#include <freerdp/primitives.h> +#include <freerdp/codec/h264.h> +#include <freerdp/codec/yuv.h> +#include <freerdp/log.h> + +#include "h264.h" + +#define TAG FREERDP_TAG("codec") + +static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight); + +BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT32 height) +{ + BOOL isNull = FALSE; + UINT32 pheight = height; + + if (!h264) + return FALSE; + + if (stride == 0) + stride = width; + + if (stride % 16 != 0) + stride += 16 - stride % 16; + + if (pheight % 16 != 0) + pheight += 16 - pheight % 16; + + for (size_t x = 0; x < 3; x++) + { + if (!h264->pYUVData[x] || !h264->pOldYUVData[x]) + isNull = TRUE; + } + + if (pheight == 0) + return FALSE; + if (stride == 0) + return FALSE; + + if (isNull || (width != h264->width) || (height != h264->height) || + (stride != h264->iStride[0])) + { + h264->iStride[0] = stride; + h264->iStride[1] = (stride + 1) / 2; + h264->iStride[2] = (stride + 1) / 2; + h264->width = width; + h264->height = height; + + for (size_t x = 0; x < 3; x++) + { + BYTE* tmp1 = winpr_aligned_recalloc(h264->pYUVData[x], h264->iStride[x], pheight, 16); + BYTE* tmp2 = + winpr_aligned_recalloc(h264->pOldYUVData[x], h264->iStride[x], pheight, 16); + if (tmp1) + h264->pYUVData[x] = tmp1; + if (tmp2) + h264->pOldYUVData[x] = tmp2; + if (!tmp1 || !tmp2) + return FALSE; + } + } + + return TRUE; +} + +INT32 avc420_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstData, + DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, + const RECTANGLE_16* regionRects, UINT32 numRegionRects) +{ + int status = 0; + const BYTE* pYUVData[3]; + + if (!h264 || h264->Compressor) + return -1001; + + status = h264->subsystem->Decompress(h264, pSrcData, SrcSize); + + if (status == 0) + return 1; + + if (status < 0) + return status; + + pYUVData[0] = h264->pYUVData[0]; + pYUVData[1] = h264->pYUVData[1]; + pYUVData[2] = h264->pYUVData[2]; + if (!yuv420_context_decode(h264->yuv, pYUVData, h264->iStride, h264->height, DstFormat, + pDstData, nDstStep, regionRects, numRegionRects)) + return -1002; + + return 1; +} + +static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles, + RDPGFX_H264_METABLOCK* meta, size_t count) +{ + /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */ + if (!meta || (QP > UINT8_MAX)) + { + free(rectangles); + return FALSE; + } + + meta->regionRects = rectangles; + if (count == 0) + return TRUE; + + if (count > UINT32_MAX) + return FALSE; + + meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY)); + + if (!meta->quantQualityVals || !meta->regionRects) + return FALSE; + meta->numRegionRects = (UINT32)count; + for (size_t x = 0; x < count; x++) + { + RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x]; + cur->qp = (UINT8)QP; + + /* qpVal bit 6 and 7 are flags, so mask them out here. + * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */ + cur->qualityVal = 100 - (QP & 0x3F); + } + return TRUE; +} + +static INLINE BOOL diff_tile(const RECTANGLE_16* regionRect, BYTE* pYUVData[3], + BYTE* pOldYUVData[3], UINT32 const iStride[3]) +{ + size_t size = 0; + + if (!regionRect || !pYUVData || !pOldYUVData || !iStride) + return FALSE; + size = regionRect->right - regionRect->left; + if (regionRect->right > iStride[0]) + return FALSE; + if (regionRect->right / 2u > iStride[1]) + return FALSE; + if (regionRect->right / 2u > iStride[2]) + return FALSE; + + for (UINT16 y = regionRect->top; y < regionRect->bottom; y++) + { + const BYTE* cur0 = &pYUVData[0][y * iStride[0]]; + const BYTE* cur1 = &pYUVData[1][y * iStride[1]]; + const BYTE* cur2 = &pYUVData[2][y * iStride[2]]; + const BYTE* old0 = &pOldYUVData[0][y * iStride[0]]; + const BYTE* old1 = &pOldYUVData[1][y * iStride[1]]; + const BYTE* old2 = &pOldYUVData[2][y * iStride[2]]; + + if (memcmp(&cur0[regionRect->left], &old0[regionRect->left], size) != 0) + return TRUE; + if (memcmp(&cur1[regionRect->left / 2], &old1[regionRect->left / 2], size / 2) != 0) + return TRUE; + if (memcmp(&cur2[regionRect->left / 2], &old2[regionRect->left / 2], size / 2) != 0) + return TRUE; + } + return FALSE; +} + +static BOOL detect_changes(BOOL firstFrameDone, const UINT32 QP, const RECTANGLE_16* regionRect, + BYTE* pYUVData[3], BYTE* pOldYUVData[3], UINT32 const iStride[3], + RDPGFX_H264_METABLOCK* meta) +{ + size_t count = 0; + size_t wc = 0; + size_t hc = 0; + RECTANGLE_16* rectangles = NULL; + + if (!regionRect || !pYUVData || !pOldYUVData || !iStride || !meta) + return FALSE; + + wc = (regionRect->right - regionRect->left) / 64 + 1; + hc = (regionRect->bottom - regionRect->top) / 64 + 1; + rectangles = calloc(wc * hc, sizeof(RECTANGLE_16)); + if (!rectangles) + return FALSE; + if (!firstFrameDone) + { + rectangles[0] = *regionRect; + count = 1; + } + else + { + for (size_t y = regionRect->top; y < regionRect->bottom; y += 64) + { + for (size_t x = regionRect->left; x < regionRect->right; x += 64) + { + RECTANGLE_16 rect; + rect.left = (UINT16)MIN(UINT16_MAX, regionRect->left + x); + rect.top = (UINT16)MIN(UINT16_MAX, regionRect->top + y); + rect.right = + (UINT16)MIN(UINT16_MAX, MIN(regionRect->left + x + 64, regionRect->right)); + rect.bottom = + (UINT16)MIN(UINT16_MAX, MIN(regionRect->top + y + 64, regionRect->bottom)); + if (diff_tile(&rect, pYUVData, pOldYUVData, iStride)) + rectangles[count++] = rect; + } + } + } + if (!allocate_h264_metablock(QP, rectangles, meta, count)) + return FALSE; + return TRUE; +} + +INT32 avc420_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep, + UINT32 nSrcWidth, UINT32 nSrcHeight, const RECTANGLE_16* regionRect, + BYTE** ppDstData, UINT32* pDstSize, RDPGFX_H264_METABLOCK* meta) +{ + INT32 rc = -1; + BYTE* pYUVData[3] = { 0 }; + const BYTE* pcYUVData[3] = { 0 }; + BYTE* pOldYUVData[3] = { 0 }; + + if (!h264 || !regionRect || !meta || !h264->Compressor) + return -1; + + if (!h264->subsystem->Compress) + return -1; + + if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight)) + return -1; + + if (h264->encodingBuffer) + { + for (size_t x = 0; x < 3; x++) + { + pYUVData[x] = h264->pYUVData[x]; + pOldYUVData[x] = h264->pOldYUVData[x]; + } + } + else + { + for (size_t x = 0; x < 3; x++) + { + pYUVData[x] = h264->pOldYUVData[x]; + pOldYUVData[x] = h264->pYUVData[x]; + } + } + h264->encodingBuffer = !h264->encodingBuffer; + + if (!yuv420_context_encode(h264->yuv, pSrcData, nSrcStep, SrcFormat, h264->iStride, pYUVData, + regionRect, 1)) + goto fail; + + if (!detect_changes(h264->firstLumaFrameDone, h264->QP, regionRect, pYUVData, pOldYUVData, + h264->iStride, meta)) + goto fail; + + if (meta->numRegionRects == 0) + { + rc = 0; + goto fail; + } + + for (size_t x = 0; x < 3; x++) + pcYUVData[x] = pYUVData[x]; + + rc = h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize); + if (rc >= 0) + h264->firstLumaFrameDone = TRUE; + +fail: + if (rc < 0) + free_h264_metablock(meta); + return rc; +} + +INT32 avc444_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep, + UINT32 nSrcWidth, UINT32 nSrcHeight, BYTE version, const RECTANGLE_16* region, + BYTE* op, BYTE** ppDstData, UINT32* pDstSize, BYTE** ppAuxDstData, + UINT32* pAuxDstSize, RDPGFX_H264_METABLOCK* meta, + RDPGFX_H264_METABLOCK* auxMeta) +{ + int rc = -1; + BYTE* coded = NULL; + UINT32 codedSize = 0; + BYTE** pYUV444Data = NULL; + BYTE** pOldYUV444Data = NULL; + BYTE** pYUVData = NULL; + BYTE** pOldYUVData = NULL; + + if (!h264 || !h264->Compressor) + return -1; + + if (!h264->subsystem->Compress) + return -1; + + if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight)) + return -1; + + if (!avc444_ensure_buffer(h264, nSrcHeight)) + return -1; + + if (h264->encodingBuffer) + { + pYUV444Data = h264->pOldYUV444Data; + pOldYUV444Data = h264->pYUV444Data; + pYUVData = h264->pOldYUVData; + pOldYUVData = h264->pYUVData; + } + else + { + pYUV444Data = h264->pYUV444Data; + pOldYUV444Data = h264->pOldYUV444Data; + pYUVData = h264->pYUVData; + pOldYUVData = h264->pOldYUVData; + } + h264->encodingBuffer = !h264->encodingBuffer; + + if (!yuv444_context_encode(h264->yuv, version, pSrcData, nSrcStep, SrcFormat, h264->iStride, + pYUV444Data, pYUVData, region, 1)) + goto fail; + + if (!detect_changes(h264->firstLumaFrameDone, h264->QP, region, pYUV444Data, pOldYUV444Data, + h264->iStride, meta)) + goto fail; + if (!detect_changes(h264->firstChromaFrameDone, h264->QP, region, pYUVData, pOldYUVData, + h264->iStride, auxMeta)) + goto fail; + + /* [MS-RDPEGFX] 2.2.4.5 RFX_AVC444_BITMAP_STREAM + * LC: + * 0 ... Luma & Chroma + * 1 ... Luma + * 2 ... Chroma + */ + if ((meta->numRegionRects > 0) && (auxMeta->numRegionRects > 0)) + *op = 0; + else if (meta->numRegionRects > 0) + *op = 1; + else if (auxMeta->numRegionRects > 0) + *op = 2; + else + { + WLog_INFO(TAG, "no changes detected for luma or chroma frame"); + rc = 0; + goto fail; + } + + if ((*op == 0) || (*op == 1)) + { + const BYTE* pcYUV444Data[3] = { pYUV444Data[0], pYUV444Data[1], pYUV444Data[2] }; + + if (h264->subsystem->Compress(h264, pcYUV444Data, h264->iStride, &coded, &codedSize) < 0) + goto fail; + h264->firstLumaFrameDone = TRUE; + memcpy(h264->lumaData, coded, codedSize); + *ppDstData = h264->lumaData; + *pDstSize = codedSize; + } + + if ((*op == 0) || (*op == 2)) + { + const BYTE* pcYUVData[3] = { pYUVData[0], pYUVData[1], pYUVData[2] }; + + if (h264->subsystem->Compress(h264, pcYUVData, h264->iStride, &coded, &codedSize) < 0) + goto fail; + h264->firstChromaFrameDone = TRUE; + *ppAuxDstData = coded; + *pAuxDstSize = codedSize; + } + + rc = 1; +fail: + if (rc < 0) + { + free_h264_metablock(meta); + free_h264_metablock(auxMeta); + } + return rc; +} + +static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight) +{ + WINPR_ASSERT(h264); + + const UINT32* piMainStride = h264->iStride; + UINT32* piDstSize = h264->iYUV444Size; + UINT32* piDstStride = h264->iYUV444Stride; + BYTE** ppYUVDstData = h264->pYUV444Data; + BYTE** ppOldYUVDstData = h264->pOldYUV444Data; + + nDstHeight = MAX(h264->height, nDstHeight); + const UINT32 pad = nDstHeight % 16; + UINT32 padDstHeight = nDstHeight; /* Need alignment to 16x16 blocks */ + + if (pad != 0) + padDstHeight += 16 - pad; + + if ((piMainStride[0] != piDstStride[0]) || + (piDstSize[0] != 1ull * piMainStride[0] * padDstHeight)) + { + for (UINT32 x = 0; x < 3; x++) + { + piDstStride[x] = piMainStride[0]; + piDstSize[x] = piDstStride[x] * padDstHeight; + + if (piDstSize[x] == 0) + return FALSE; + + BYTE* tmp1 = winpr_aligned_recalloc(ppYUVDstData[x], piDstSize[x], 1, 16); + if (!tmp1) + return FALSE; + ppYUVDstData[x] = tmp1; + BYTE* tmp2 = winpr_aligned_recalloc(ppOldYUVDstData[x], piDstSize[x], 1, 16); + if (!tmp2) + return FALSE; + ppOldYUVDstData[x] = tmp2; + } + + { + BYTE* tmp = winpr_aligned_recalloc(h264->lumaData, piDstSize[0], 4, 16); + if (!tmp) + goto fail; + h264->lumaData = tmp; + } + } + + for (UINT32 x = 0; x < 3; x++) + { + if (!ppOldYUVDstData[x] || !ppYUVDstData[x] || (piDstSize[x] == 0) || (piDstStride[x] == 0)) + { + WLog_Print(h264->log, WLOG_ERROR, + "YUV buffer not initialized! check your decoder settings"); + goto fail; + } + } + + if (!h264->lumaData) + goto fail; + + return TRUE; +fail: + return FALSE; +} + +static BOOL avc444_process_rects(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize, + BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, + UINT32 nDstWidth, UINT32 nDstHeight, const RECTANGLE_16* rects, + UINT32 nrRects, avc444_frame_type type) +{ + const BYTE* pYUVData[3]; + BYTE* pYUVDstData[3]; + UINT32* piDstStride = h264->iYUV444Stride; + BYTE** ppYUVDstData = h264->pYUV444Data; + const UINT32* piStride = h264->iStride; + + if (h264->subsystem->Decompress(h264, pSrcData, SrcSize) < 0) + return FALSE; + + pYUVData[0] = h264->pYUVData[0]; + pYUVData[1] = h264->pYUVData[1]; + pYUVData[2] = h264->pYUVData[2]; + if (!avc444_ensure_buffer(h264, nDstHeight)) + return FALSE; + + pYUVDstData[0] = ppYUVDstData[0]; + pYUVDstData[1] = ppYUVDstData[1]; + pYUVDstData[2] = ppYUVDstData[2]; + if (!yuv444_context_decode(h264->yuv, (BYTE)type, pYUVData, piStride, h264->height, pYUVDstData, + piDstStride, DstFormat, pDstData, nDstStep, rects, nrRects)) + return FALSE; + + return TRUE; +} + +#if defined(AVC444_FRAME_STAT) +static UINT64 op1 = 0; +static double op1sum = 0; +static UINT64 op2 = 0; +static double op2sum = 0; +static UINT64 op3 = 0; +static double op3sum = 0; +static double avg(UINT64* count, double old, double size) +{ + double tmp = size + *count * old; + (*count)++; + tmp = tmp / *count; + return tmp; +} +#endif + +INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op, const RECTANGLE_16* regionRects, + UINT32 numRegionRects, const BYTE* pSrcData, UINT32 SrcSize, + const RECTANGLE_16* auxRegionRects, UINT32 numAuxRegionRect, + const BYTE* pAuxSrcData, UINT32 AuxSrcSize, BYTE* pDstData, DWORD DstFormat, + UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, UINT32 codecId) +{ + INT32 status = -1; + avc444_frame_type chroma = + (codecId == RDPGFX_CODECID_AVC444) ? AVC444_CHROMAv1 : AVC444_CHROMAv2; + + if (!h264 || !regionRects || !pSrcData || !pDstData || h264->Compressor) + return -1001; + + switch (op) + { + case 0: /* YUV420 in stream 1 + * Chroma420 in stream 2 */ + if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep, + nDstWidth, nDstHeight, regionRects, numRegionRects, + AVC444_LUMA)) + status = -1; + else if (!avc444_process_rects(h264, pAuxSrcData, AuxSrcSize, pDstData, DstFormat, + nDstStep, nDstWidth, nDstHeight, auxRegionRects, + numAuxRegionRect, chroma)) + status = -1; + else + status = 0; + + break; + + case 2: /* Chroma420 in stream 1 */ + if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep, + nDstWidth, nDstHeight, regionRects, numRegionRects, chroma)) + status = -1; + else + status = 0; + + break; + + case 1: /* YUV420 in stream 1 */ + if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep, + nDstWidth, nDstHeight, regionRects, numRegionRects, + AVC444_LUMA)) + status = -1; + else + status = 0; + + break; + + default: /* WTF? */ + break; + } + +#if defined(AVC444_FRAME_STAT) + + switch (op) + { + case 0: + op1sum = avg(&op1, op1sum, SrcSize + AuxSrcSize); + break; + + case 1: + op2sum = avg(&op2, op2sum, SrcSize); + break; + + case 2: + op3sum = avg(&op3, op3sum, SrcSize); + break; + + default: + break; + } + + WLog_Print(h264->log, WLOG_INFO, + "luma=%" PRIu64 " [avg=%lf] chroma=%" PRIu64 " [avg=%lf] combined=%" PRIu64 + " [avg=%lf]", + op1, op1sum, op2, op2sum, op3, op3sum); +#endif + return status; +} + +#define MAX_SUBSYSTEMS 10 +static INIT_ONCE subsystems_once = INIT_ONCE_STATIC_INIT; +static const H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS] = { 0 }; + +static BOOL CALLBACK h264_register_subsystems(PINIT_ONCE once, PVOID param, PVOID* context) +{ + int i = 0; + +#ifdef WITH_MEDIACODEC + { + subSystems[i] = &g_Subsystem_mediacodec; + i++; + } +#endif +#if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION) + { + subSystems[i] = &g_Subsystem_MF; + i++; + } +#endif +#ifdef WITH_OPENH264 + { + subSystems[i] = &g_Subsystem_OpenH264; + i++; + } +#endif +#ifdef WITH_VIDEO_FFMPEG + { + subSystems[i] = &g_Subsystem_libavcodec; + i++; + } +#endif + return i > 0; +} + +static BOOL h264_context_init(H264_CONTEXT* h264) +{ + if (!h264) + return FALSE; + + h264->log = WLog_Get(TAG); + + if (!h264->log) + return FALSE; + + h264->subsystem = NULL; + InitOnceExecuteOnce(&subsystems_once, h264_register_subsystems, NULL, NULL); + + for (size_t i = 0; i < MAX_SUBSYSTEMS; i++) + { + const H264_CONTEXT_SUBSYSTEM* subsystem = subSystems[i]; + + if (!subsystem || !subsystem->Init) + break; + + if (subsystem->Init(h264)) + { + h264->subsystem = subsystem; + return TRUE; + } + } + + return FALSE; +} + +BOOL h264_context_reset(H264_CONTEXT* h264, UINT32 width, UINT32 height) +{ + if (!h264) + return FALSE; + + h264->width = width; + h264->height = height; + return yuv_context_reset(h264->yuv, width, height); +} + +H264_CONTEXT* h264_context_new(BOOL Compressor) +{ + H264_CONTEXT* h264 = (H264_CONTEXT*)calloc(1, sizeof(H264_CONTEXT)); + if (!h264) + return NULL; + + h264->Compressor = Compressor; + if (Compressor) + + { + /* Default compressor settings, may be changed by caller */ + h264->BitRate = 1000000; + h264->FrameRate = 30; + } + + if (!h264_context_init(h264)) + goto fail; + + h264->yuv = yuv_context_new(Compressor, 0); + if (!h264->yuv) + goto fail; + + return h264; + +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + h264_context_free(h264); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void h264_context_free(H264_CONTEXT* h264) +{ + if (h264) + { + if (h264->subsystem) + h264->subsystem->Uninit(h264); + + for (size_t x = 0; x < 3; x++) + { + if (h264->Compressor) + { + winpr_aligned_free(h264->pYUVData[x]); + winpr_aligned_free(h264->pOldYUVData[x]); + } + winpr_aligned_free(h264->pYUV444Data[x]); + winpr_aligned_free(h264->pOldYUV444Data[x]); + } + winpr_aligned_free(h264->lumaData); + + yuv_context_free(h264->yuv); + free(h264); + } +} + +void free_h264_metablock(RDPGFX_H264_METABLOCK* meta) +{ + RDPGFX_H264_METABLOCK m = { 0 }; + if (!meta) + return; + free(meta->quantQualityVals); + free(meta->regionRects); + *meta = m; +} + +BOOL h264_context_set_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option, UINT32 value) +{ + WINPR_ASSERT(h264); + switch (option) + { + case H264_CONTEXT_OPTION_BITRATE: + h264->BitRate = value; + return TRUE; + case H264_CONTEXT_OPTION_FRAMERATE: + h264->FrameRate = value; + return TRUE; + case H264_CONTEXT_OPTION_RATECONTROL: + h264->RateControlMode = value; + return TRUE; + case H264_CONTEXT_OPTION_QP: + h264->QP = value; + return TRUE; + default: + WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]", + option); + return FALSE; + } +} + +UINT32 h264_context_get_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option) +{ + WINPR_ASSERT(h264); + switch (option) + { + case H264_CONTEXT_OPTION_BITRATE: + return h264->BitRate; + case H264_CONTEXT_OPTION_FRAMERATE: + return h264->FrameRate; + case H264_CONTEXT_OPTION_RATECONTROL: + return h264->RateControlMode; + case H264_CONTEXT_OPTION_QP: + return h264->QP; + default: + WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]", + option); + return 0; + } +} |