New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom video encoder settings and dynamic screen sizes #406
Changes from 7 commits
aac227a
abfb28d
801f54f
f3eb424
4694398
74ae86f
2e929ed
1c82de4
6bb971b
1ac0a6b
5f7b6c3
0f7226c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,10 @@ | |
@import UIKit; | ||
|
||
#import "SDLAbstractProtocol.h" | ||
#import "SDLDisplayCapabilities.h" | ||
#import "SDLGlobals.h" | ||
|
||
#import "SDLImageResolution.h" | ||
#import "SDLScreenParams.h" | ||
|
||
NSString *const SDLErrorDomainStreamingMediaVideo = @"com.sdl.streamingmediamanager.video"; | ||
NSString *const SDLErrorDomainStreamingMediaAudio = @"com.sdl.streamingmediamanager.audio"; | ||
|
@@ -39,6 +41,8 @@ @interface SDLStreamingMediaManager () | |
|
||
@implementation SDLStreamingMediaManager | ||
|
||
@synthesize videoEncoderSettings = _videoEncoderSettings; | ||
|
||
#pragma mark - Class Lifecycle | ||
|
||
- (instancetype)initWithProtocol:(SDLAbstractProtocol *)protocol { | ||
|
@@ -56,6 +60,8 @@ - (instancetype)initWithProtocol:(SDLAbstractProtocol *)protocol { | |
|
||
_videoStartBlock = nil; | ||
_audioStartBlock = nil; | ||
|
||
_screenSize = CGSizeMake(640, 480); | ||
|
||
return self; | ||
} | ||
|
@@ -127,6 +133,52 @@ - (BOOL)sendAudioData:(NSData *)pcmAudioData { | |
return YES; | ||
} | ||
|
||
#pragma mark - Update video encoder | ||
|
||
- (void)setVideoEncoderSettings:(NSDictionary * _Nullable)videoEncoderSettings { | ||
if (self.videoSessionConnected) { | ||
@throw [NSException exceptionWithName:SDLErrorDomainStreamingMediaVideo reason:@"Cannot update video encoder settings while video session is connected." userInfo:nil]; | ||
return; | ||
} | ||
|
||
if (videoEncoderSettings) { | ||
_videoEncoderSettings = videoEncoderSettings; | ||
} else { | ||
_videoEncoderSettings = self.defaultVideoEncoderSettings; | ||
} | ||
} | ||
|
||
- (NSDictionary*)videoEncoderSettings { | ||
if (!_videoEncoderSettings) { | ||
_videoEncoderSettings = self.defaultVideoEncoderSettings; | ||
} | ||
return _videoEncoderSettings; | ||
} | ||
|
||
- (NSDictionary*)defaultVideoEncoderSettings { | ||
static NSDictionary* defaultVideoEncoderSettings = nil; | ||
if (defaultVideoEncoderSettings == nil) { | ||
defaultVideoEncoderSettings = @{ | ||
(__bridge NSString*)kVTCompressionPropertyKey_ProfileLevel : (__bridge NSString*)kVTProfileLevel_H264_Baseline_AutoLevel, | ||
(__bridge NSString*)kVTCompressionPropertyKey_RealTime : @YES | ||
}; | ||
} | ||
return defaultVideoEncoderSettings; | ||
} | ||
|
||
- (void)setDisplayCapabilities:(SDLDisplayCapabilities *)displayCapabilities { | ||
_displayCapabilities = displayCapabilities; | ||
|
||
SDLImageResolution* resolution = displayCapabilities.screenParams.resolution; | ||
if (resolution != nil) { | ||
_screenSize = CGSizeMake(resolution.resolutionWidth.floatValue, | ||
resolution.resolutionHeight.floatValue); | ||
} else { | ||
NSLog(@"Could not retrieve screen size. Defaulting to 640 x 480."); | ||
_screenSize = CGSizeMake(640, | ||
480); | ||
} | ||
} | ||
|
||
#pragma mark - SDLProtocolListener Methods | ||
|
||
|
@@ -224,36 +276,52 @@ - (BOOL)sdl_configureVideoEncoderWithError:(NSError *__autoreleasing *)error { | |
OSStatus status; | ||
|
||
// Create a compression session | ||
// TODO (Joel F.)[2015-08-18]: Dimensions should be from the Head Unit | ||
status = VTCompressionSessionCreate(NULL, 640, 480, kCMVideoCodecType_H264, NULL, NULL, NULL, &sdl_videoEncoderOutputCallback, (__bridge void *)self, &_compressionSession); | ||
status = VTCompressionSessionCreate(NULL, self.screenSize.width, self.screenSize.height, kCMVideoCodecType_H264, NULL, NULL, NULL, &sdl_videoEncoderOutputCallback, (__bridge void *)self, &_compressionSession); | ||
|
||
if (status != noErr) { | ||
// TODO: Log the error | ||
if (*error != nil) { | ||
if (*error == nil) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These were correct originally. They're checking that the passed NSError address can be dereferenced, and later retrieved by the developer. See "Generating your own errors" at https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html |
||
*error = [NSError errorWithDomain:SDLErrorDomainStreamingMediaVideo code:SDLStreamingVideoErrorConfigurationCompressionSessionCreationFailure userInfo:@{ @"OSStatus" : @(status) }]; | ||
} | ||
|
||
return NO; | ||
} | ||
|
||
// Set the profile level of the video stream | ||
status = VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel); | ||
// Validate that the video encoder properties are valid. | ||
CFDictionaryRef supportedProperties; | ||
status = VTSessionCopySupportedPropertyDictionary(self.compressionSession, &supportedProperties); | ||
if (status != noErr) { | ||
if (*error != nil) { | ||
if (*error == nil) { | ||
*error = [NSError errorWithDomain:SDLErrorDomainStreamingMediaVideo code:SDLStreamingVideoErrorConfigurationCompressionSessionSetPropertyFailure userInfo:@{ @"OSStatus" : @(status) }]; | ||
} | ||
|
||
return NO; | ||
} | ||
|
||
// Set the session to compress in real time | ||
status = VTSessionSetProperty(self.compressionSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue); | ||
if (status != noErr) { | ||
if (*error != nil) { | ||
*error = [NSError errorWithDomain:SDLErrorDomainStreamingMediaVideo code:SDLStreamingVideoErrorConfigurationCompressionSessionSetPropertyFailure userInfo:@{ @"OSStatus" : @(status) }]; | ||
|
||
for (NSString* key in self.videoEncoderSettings.allKeys) { | ||
if (CFDictionaryContainsKey(supportedProperties, (__bridge CFStringRef)key) == false) { | ||
if (*error == nil) { | ||
NSString* description = [NSString stringWithFormat:@"\"%@\" is not a supported key.", key]; | ||
*error = [NSError errorWithDomain:SDLErrorDomainStreamingMediaVideo code:SDLStreamingVideoErrorConfigurationCompressionSessionSetPropertyFailure userInfo:@{NSLocalizedDescriptionKey : description}]; | ||
} | ||
CFRelease(supportedProperties); | ||
return NO; | ||
} | ||
} | ||
CFRelease(supportedProperties); | ||
|
||
// Populate the video encoder settings from provided dictionary. | ||
for (NSString* key in self.videoEncoderSettings.allKeys) { | ||
id value = self.videoEncoderSettings[key]; | ||
|
||
status = VTSessionSetProperty(self.compressionSession, (__bridge CFStringRef)key, (__bridge CFTypeRef)value); | ||
if (status != noErr) { | ||
if (*error == nil) { | ||
*error = [NSError errorWithDomain:SDLErrorDomainStreamingMediaVideo code:SDLStreamingVideoErrorConfigurationCompressionSessionSetPropertyFailure userInfo:@{ @"OSStatus" : @(status) }]; | ||
} | ||
|
||
return NO; | ||
} | ||
|
||
return NO; | ||
} | ||
|
||
return YES; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we deprecate
initWithProtocol:
and addinitWithProtocol:displayCapabilities:
? The displayCapabilities property on SMM will have to stay nullable for now, but that would clarify the actual dependencies.