Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/@webex/plugin-meetings/src/multistream/codec/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {H264EncodingParams, SupportedResolution} from '@webex/internal-media-core';
import {RemoteVideoResolution} from '../types';

export const H264_CODEC_PARAMETERS = {
'90p': {
Expand Down Expand Up @@ -28,3 +29,12 @@ export const CODEC_DEFAULTS = {
maxMbps: 245760,
},
};

export const PANE_SIZE_TO_RESOLUTION = {
thumbnail: '90p',
'very small': '180p',
small: '360p',
medium: '720p',
large: '1080p',
best: '1080p',
} satisfies Record<RemoteVideoResolution, SupportedResolution>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/* eslint-disable class-methods-use-this */
import {
getRecommendedMaxBitrateForFrameSize,
H264Codec,
SupportedResolution,
CodecInfo as WcmeCodecInfo,
} from '@webex/internal-media-core';
import {CODEC_DEFAULTS, H264_CODEC_PARAMETERS, PANE_SIZE_TO_RESOLUTION} from './constants';
import {MediaCodecHelper, H264CodecInfo} from './types';
import {MediaRequest, RemoteVideoResolution} from '../types';
import LoggerProxy from '../../common/logs/logger-proxy';

type H264CodecOptions = {
getMaxFs?: () => number;
};

/**
* Class for H264 media codec info
*/
export default class MediaCodecHelperH264
implements MediaCodecHelper<H264CodecOptions, H264CodecInfo>
{
/**
* Gets the H264 codec info
*
* @param {Object} options - The options for the H264 codec info
* @param {number} options.maxFs - The maximum frame size
* @returns {H264CodecInfo} The H264 codec info
*/
getCodecInfo(options: H264CodecOptions): H264CodecInfo | undefined {
if (!options.getMaxFs) {
return undefined;
}

return {
codec: 'h264',
maxFs: options.getMaxFs(),
};
}

/**
* Degrades the media request
*
* @param {MediaRequest} mr - The media request to degrade
* @param {Resolution} resolution - The resolution to degrade to
* @returns {number} The total macroblocks requested
*/
degradeMediaRequest(mr: MediaRequest, resolution: SupportedResolution): number {
if (mr.codecInfo?.codec !== 'h264') {
return 0;
}

mr.codecInfo.maxFs = Math.min(
mr.preferredMaxFs || CODEC_DEFAULTS.h264.maxFs,
mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
H264_CODEC_PARAMETERS[resolution].maxFs
);

// we only consider sources with "live" state
const slotsWithLiveSource = mr.receiveSlots.filter((rs) => rs.sourceState === 'live');

return mr.codecInfo.maxFs * slotsWithLiveSource.length;
}

/**
* Gets the max payload bits per second
*
* @param {MediaRequest} mediaRequest - The media request to get the max payload bits per second from
* @returns {number} The max payload bits per second
*/
getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number {
if (mediaRequest.codecInfo?.codec !== 'h264') {
return 0;
}

return getRecommendedMaxBitrateForFrameSize(mediaRequest.codecInfo.maxFs);
}

/**
* Gets the WCME codec infos
*
* @param {MediaRequest} mr - The media request to get the WCME codec infos from
* @returns {WcmeCodecInfo[]} The WCME codec infos
*/
getWCMECodecInfos(mr: MediaRequest): WcmeCodecInfo[] {
if (mr.codecInfo?.codec !== 'h264') {
return [];
}

return [
WcmeCodecInfo.fromH264(
0x80,
new H264Codec(
mr.codecInfo.maxFs,
mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,
mr.codecInfo.maxWidth,
mr.codecInfo.maxHeight
)
),
];
}

/**
* Converts pane size into h264 maxFs
*
* @param {RemoteVideoResolution} paneSize - The pane size to get the max fs for
* @returns {number} The max fs
*/
getMaxFs(paneSize: RemoteVideoResolution): number {
let resolution: SupportedResolution;

if (paneSize in PANE_SIZE_TO_RESOLUTION) {
resolution = PANE_SIZE_TO_RESOLUTION[paneSize];
} else {
LoggerProxy.logger.warn(
`MediaCodecHelperH264#getMaxFs --> unsupported paneSize: ${paneSize}, using "medium" instead`
);
resolution = PANE_SIZE_TO_RESOLUTION.medium;
}

return H264_CODEC_PARAMETERS[resolution].maxFs;
}

/**
* Gets the max fs for the given width and height
*
* @param {number} width - The width of the video element
* @param {number} height - The height of the video element
* @returns {number | undefined} The max fs for the given width and height, or undefined if the width or height is 0
*/
getSizeHintMaxFs(width: number, height: number): number | undefined {
if (width === 0 || height === 0) {
return undefined;
}

// we switch to the next resolution level when the height is 10% more than the current resolution height
// except for 1080p - we switch to it immediately when the height is more than 720p
const threshold = 1.1;
const getThresholdHeight = (h: number) => Math.round(h * threshold);

if (height < getThresholdHeight(90)) {
return H264_CODEC_PARAMETERS['90p'].maxFs;
}
if (height < getThresholdHeight(180)) {
return H264_CODEC_PARAMETERS['180p'].maxFs;
}
if (height < getThresholdHeight(360)) {
return H264_CODEC_PARAMETERS['360p'].maxFs;
}
if (height < getThresholdHeight(540)) {
return H264_CODEC_PARAMETERS['540p'].maxFs;
}
if (height <= 720) {
return H264_CODEC_PARAMETERS['720p'].maxFs;
}

return H264_CODEC_PARAMETERS['1080p'].maxFs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import MediaCodecHelperH264 from './mediaCodecHelper.h264';

const MediaCodecHelper = {
H264: new MediaCodecHelperH264(),
get: (codec?: 'h264'): MediaCodecHelperH264 => {
switch (codec) {
case 'h264':
default:
return MediaCodecHelper.H264;
}
},
};

export default MediaCodecHelper;
14 changes: 13 additions & 1 deletion packages/@webex/plugin-meetings/src/multistream/codec/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import {H264EncodingParams} from '@webex/internal-media-core';
import {
H264EncodingParams,
SupportedResolution,
CodecInfo as WcmeCodecInfo,
} from '@webex/internal-media-core';
import {MediaRequest} from '../types';

export type H264CodecInfo = H264EncodingParams & {
codec: 'h264';
};

export type CodecInfo = H264CodecInfo;

export interface MediaCodecHelper<TCodecOptions, TCodecInfo extends CodecInfo> {
getCodecInfo(options: TCodecOptions): TCodecInfo | undefined;
getWCMECodecInfos(mediaRequest: MediaRequest): WcmeCodecInfo[];
degradeMediaRequest(mediaRequest: MediaRequest, resolution: SupportedResolution): number;
getMaxPayloadBitsPerSecond(mediaRequest: MediaRequest): number;
}