Skip to content

Commit c6ee02d

Browse files
committed
Scale H264 software input before encoding
1 parent ad4681c commit c6ee02d

1 file changed

Lines changed: 71 additions & 4 deletions

File tree

cli/XCWH264Encoder.m

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#import "XCWH264Encoder.h"
22

3+
#import <CoreImage/CoreImage.h>
34
#import <CoreMedia/CoreMedia.h>
45
#import <os/lock.h>
56
#import <QuartzCore/QuartzCore.h>
@@ -232,14 +233,11 @@ static int32_t XCWRoundToEvenDimension(double value) {
232233
}
233234

234235
static CGSize XCWScaledDimensionsForSourceSize(int32_t width, int32_t height, XCWVideoEncoderMode mode) {
236+
(void)mode;
235237
if (width <= 0 || height <= 0) {
236238
return CGSizeZero;
237239
}
238240

239-
if (mode == XCWVideoEncoderModeH264Software) {
240-
return CGSizeMake(width, height);
241-
}
242-
243241
int32_t longestEdge = MAX(width, height);
244242
if (longestEdge <= XCWMaximumEncodedDimension) {
245243
return CGSizeMake(width, height);
@@ -345,6 +343,7 @@ @implementation XCWH264Encoder {
345343
int32_t _height;
346344
uint64_t _timestampOriginUs;
347345
VTPixelTransferSessionRef _pixelTransferSession;
346+
CIContext *_scalingContext;
348347
CVPixelBufferRef _scaledPixelBuffer;
349348
OSType _scaledPixelFormat;
350349
XCWVideoEncoderMode _encoderMode;
@@ -681,6 +680,12 @@ - (nullable CVPixelBufferRef)copyScaledPixelBufferIfNeeded:(CVPixelBufferRef)pix
681680
return pixelBuffer;
682681
}
683682

683+
if (_encoderMode == XCWVideoEncoderModeH264Software) {
684+
return [self copyCoreImageScaledPixelBuffer:pixelBuffer
685+
targetWidth:targetWidth
686+
targetHeight:targetHeight];
687+
}
688+
684689
if (_pixelTransferSession == NULL) {
685690
OSStatus sessionStatus = VTPixelTransferSessionCreate(kCFAllocatorDefault, &_pixelTransferSession);
686691
if (sessionStatus != noErr || _pixelTransferSession == NULL) {
@@ -738,6 +743,67 @@ - (nullable CVPixelBufferRef)copyScaledPixelBufferIfNeeded:(CVPixelBufferRef)pix
738743
return _scaledPixelBuffer;
739744
}
740745

746+
- (nullable CVPixelBufferRef)copyCoreImageScaledPixelBuffer:(CVPixelBufferRef)pixelBuffer
747+
targetWidth:(int32_t)targetWidth
748+
targetHeight:(int32_t)targetHeight {
749+
OSType sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
750+
BOOL needsNewBuffer = (_scaledPixelBuffer == NULL)
751+
|| ((int32_t)CVPixelBufferGetWidth(_scaledPixelBuffer) != targetWidth)
752+
|| ((int32_t)CVPixelBufferGetHeight(_scaledPixelBuffer) != targetHeight)
753+
|| (_scaledPixelFormat != sourcePixelFormat);
754+
if (needsNewBuffer) {
755+
if (_scaledPixelBuffer != NULL) {
756+
CVPixelBufferRelease(_scaledPixelBuffer);
757+
_scaledPixelBuffer = NULL;
758+
}
759+
760+
NSDictionary *attributes = @{
761+
(__bridge NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{},
762+
};
763+
CVPixelBufferRef scaledPixelBuffer = NULL;
764+
OSStatus bufferStatus = CVPixelBufferCreate(kCFAllocatorDefault,
765+
targetWidth,
766+
targetHeight,
767+
sourcePixelFormat,
768+
(__bridge CFDictionaryRef)attributes,
769+
&scaledPixelBuffer);
770+
if (bufferStatus != noErr || scaledPixelBuffer == NULL) {
771+
_lastScaleStatus = bufferStatus;
772+
return NULL;
773+
}
774+
_scaledPixelBuffer = scaledPixelBuffer;
775+
_scaledPixelFormat = sourcePixelFormat;
776+
}
777+
778+
if (_scalingContext == nil) {
779+
_scalingContext = [CIContext contextWithOptions:@{
780+
kCIContextUseSoftwareRenderer: @YES,
781+
}];
782+
}
783+
784+
CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];
785+
if (image == nil) {
786+
_lastScaleStatus = -1;
787+
return NULL;
788+
}
789+
790+
CGFloat scaleX = (CGFloat)targetWidth / MAX((CGFloat)CVPixelBufferGetWidth(pixelBuffer), 1.0);
791+
CGFloat scaleY = (CGFloat)targetHeight / MAX((CGFloat)CVPixelBufferGetHeight(pixelBuffer), 1.0);
792+
CIImage *scaledImage = [image imageByApplyingTransform:CGAffineTransformMakeScale(scaleX, scaleY)];
793+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
794+
[_scalingContext render:scaledImage
795+
toCVPixelBuffer:_scaledPixelBuffer
796+
bounds:CGRectMake(0, 0, targetWidth, targetHeight)
797+
colorSpace:colorSpace];
798+
if (colorSpace != NULL) {
799+
CGColorSpaceRelease(colorSpace);
800+
}
801+
802+
_lastScaleStatus = noErr;
803+
CVPixelBufferRetain(_scaledPixelBuffer);
804+
return _scaledPixelBuffer;
805+
}
806+
741807
- (void)invalidateScalingResourcesLocked {
742808
if (_scaledPixelBuffer != NULL) {
743809
CVPixelBufferRelease(_scaledPixelBuffer);
@@ -749,6 +815,7 @@ - (void)invalidateScalingResourcesLocked {
749815
CFRelease(_pixelTransferSession);
750816
_pixelTransferSession = NULL;
751817
}
818+
_scalingContext = nil;
752819
}
753820

754821
- (void)handleEncodedSampleBuffer:(CMSampleBufferRef)sampleBuffer

0 commit comments

Comments
 (0)