From 82cc32308b6e505884c0bfdc5fe99d8a3e3fc2c2 Mon Sep 17 00:00:00 2001 From: adamroach Date: Wed, 6 Jul 2022 23:58:56 -0500 Subject: [PATCH] Enable configuration of OpenH264 Encoder (#409) There's been a long-standing TODO comment in the bridge for OpenH264 to remove hardcoding for the set of configurable parameters exposed by that library. That patch does exactly that. --- pkg/codec/openh264/bridge.cpp | 21 ++++++-------- pkg/codec/openh264/bridge.hpp | 9 ++++++ pkg/codec/openh264/openh264.go | 17 +++++++++--- pkg/codec/openh264/params.go | 51 ++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/pkg/codec/openh264/bridge.cpp b/pkg/codec/openh264/bridge.cpp index f25b047..fa05ad6 100644 --- a/pkg/codec/openh264/bridge.cpp +++ b/pkg/codec/openh264/bridge.cpp @@ -21,29 +21,26 @@ Encoder *enc_new(const EncoderOptions opts, int *eresult) { return NULL; } - // TODO: Remove hardcoded values - params.iUsageType = CAMERA_VIDEO_REAL_TIME; + params.iUsageType = opts.usage_type; params.iPicWidth = opts.width; params.iPicHeight = opts.height; params.iTargetBitrate = opts.target_bitrate; params.iMaxBitrate = opts.target_bitrate; - params.iRCMode = RC_BITRATE_MODE; + params.iRCMode = opts.rc_mode; params.fMaxFrameRate = opts.max_fps; - params.bEnableFrameSkip = true; - params.uiMaxNalSize = 0; - params.uiIntraPeriod = 30; - // set to 0, so that it'll automatically use multi threads when needed - params.iMultipleThreadIdc = 0; + params.bEnableFrameSkip = opts.enable_frame_skip; + params.uiMaxNalSize = opts.max_nal_size; + params.uiIntraPeriod = opts.intra_period; + params.iMultipleThreadIdc = opts.multiple_thread_idc; // The base spatial layer 0 is the only one we use. params.sSpatialLayers[0].iVideoWidth = params.iPicWidth; params.sSpatialLayers[0].iVideoHeight = params.iPicHeight; params.sSpatialLayers[0].fFrameRate = params.fMaxFrameRate; params.sSpatialLayers[0].iSpatialBitrate = params.iTargetBitrate; params.sSpatialLayers[0].iMaxSpatialBitrate = params.iTargetBitrate; - // Single NAL unit mode - params.sSpatialLayers[0].sSliceArgument.uiSliceNum = 1; - params.sSpatialLayers[0].sSliceArgument.uiSliceMode = SM_SIZELIMITED_SLICE; - params.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = 12800; + params.sSpatialLayers[0].sSliceArgument.uiSliceNum = opts.slice_num; + params.sSpatialLayers[0].sSliceArgument.uiSliceMode = opts.slice_mode; + params.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = opts.slice_size_constraint; rv = engine->InitializeExt(¶ms); if (rv != 0) { diff --git a/pkg/codec/openh264/bridge.hpp b/pkg/codec/openh264/bridge.hpp index 982ca92..b326e11 100644 --- a/pkg/codec/openh264/bridge.hpp +++ b/pkg/codec/openh264/bridge.hpp @@ -22,6 +22,15 @@ typedef struct EncoderOptions { int width, height; int target_bitrate; float max_fps; + EUsageType usage_type; + RC_MODES rc_mode; + bool enable_frame_skip; + unsigned int max_nal_size; + unsigned int intra_period; + int multiple_thread_idc; + unsigned int slice_num; + SliceModeEnum slice_mode; + unsigned int slice_size_constraint; } EncoderOptions; typedef struct Encoder { diff --git a/pkg/codec/openh264/openh264.go b/pkg/codec/openh264/openh264.go index f778cd0..c83e6f2 100644 --- a/pkg/codec/openh264/openh264.go +++ b/pkg/codec/openh264/openh264.go @@ -33,10 +33,19 @@ func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser, var rv C.int cEncoder := C.enc_new(C.EncoderOptions{ - width: C.int(p.Width), - height: C.int(p.Height), - target_bitrate: C.int(params.BitRate), - max_fps: C.float(p.FrameRate), + width: C.int(p.Width), + height: C.int(p.Height), + target_bitrate: C.int(params.BitRate), + max_fps: C.float(p.FrameRate), + usage_type: C.EUsageType(params.UsageType), + rc_mode: C.RC_MODES(params.RCMode), + enable_frame_skip: C.bool(params.EnableFrameSkip), + max_nal_size: C.uint(params.MaxNalSize), + intra_period: C.uint(params.IntraPeriod), + multiple_thread_idc: C.int(params.MultipleThreadIdc), + slice_num: C.uint(params.SliceNum), + slice_mode: C.SliceModeEnum(params.SliceMode), + slice_size_constraint: C.uint(params.SliceSizeConstraint), }, &rv) if err := errResult(rv); err != nil { return nil, fmt.Errorf("failed in creating encoder: %v", err) diff --git a/pkg/codec/openh264/params.go b/pkg/codec/openh264/params.go index 86bfeee..48ec827 100644 --- a/pkg/codec/openh264/params.go +++ b/pkg/codec/openh264/params.go @@ -1,5 +1,8 @@ package openh264 +// #include +import "C" + import ( "github.com/pion/mediadevices/pkg/codec" "github.com/pion/mediadevices/pkg/io/video" @@ -9,14 +12,62 @@ import ( // Params stores libopenh264 specific encoding parameters. type Params struct { codec.BaseParams + UsageType UsageTypeEnum + RCMode RCModeEnum + EnableFrameSkip bool + MaxNalSize uint + IntraPeriod uint + MultipleThreadIdc int + SliceNum uint + SliceMode SliceModeEnum + SliceSizeConstraint uint } +type UsageTypeEnum int + +const ( + CameraVideoRealTime UsageTypeEnum = C.CAMERA_VIDEO_REAL_TIME ///< camera video for real-time communication + ScreenContentRealTime UsageTypeEnum = C.SCREEN_CONTENT_REAL_TIME ///< screen content signal + CameraVideoNonRealTime UsageTypeEnum = C.CAMERA_VIDEO_NON_REAL_TIME + ScreenContentNonRealTime UsageTypeEnum = C.SCREEN_CONTENT_NON_REAL_TIME + InputContentTypeAll UsageTypeEnum = C.INPUT_CONTENT_TYPE_ALL +) + +type RCModeEnum int + +const ( + RCQualityMode RCModeEnum = C.RC_QUALITY_MODE ///< quality mode + RCBitrateMode RCModeEnum = C.RC_BITRATE_MODE ///< bitrate mode + RCBufferbaseedMode RCModeEnum = C.RC_BUFFERBASED_MODE ///< no bitrate control,only using buffer status,adjust the video quality + RCTimestampMode RCModeEnum = C.RC_TIMESTAMP_MODE //rate control based timestamp + RCBitrateModePostSkip RCModeEnum = C.RC_BITRATE_MODE_POST_SKIP ///< this is in-building RC MODE, WILL BE DELETED after algorithm tuning! + RCOffMode RCModeEnum = C.RC_OFF_MODE ///< rate control off mode +) + +type SliceModeEnum uint + +const ( + SMSingleSlice SliceModeEnum = C.SM_SINGLE_SLICE ///< | SliceNum==1 + SMFixedslcnumSlice SliceModeEnum = C.SM_FIXEDSLCNUM_SLICE ///< | according to SliceNum | enabled dynamic slicing for multi-thread + SMRasterSlice SliceModeEnum = C.SM_RASTER_SLICE ///< | according to SlicesAssign | need input of MB numbers each slice. In addition, if other constraint in SSliceArgument is presented, need to follow the constraints. Typically if MB num and slice size are both constrained, re-encoding may be involved. + SMSizelimitedSlice SliceModeEnum = C.SM_SIZELIMITED_SLICE ///< | according to SliceSize | slicing according to size, the slicing will be dynamic(have no idea about slice_nums until encoding current frame) +) + // NewParams returns default openh264 codec specific parameters. func NewParams() (Params, error) { return Params{ BaseParams: codec.BaseParams{ BitRate: 100000, }, + UsageType: CameraVideoRealTime, + RCMode: RCBitrateMode, + EnableFrameSkip: true, + MaxNalSize: 0, + IntraPeriod: 30, + MultipleThreadIdc: 0, // Defaults to 0, so that it'll automatically use multi threads when needed + SliceNum: 1, // Defaults to single NAL unit mode + SliceMode: SMSizelimitedSlice, + SliceSizeConstraint: 12800, }, nil }