New upstream version 26.1.0+dfsg1
This commit is contained in:
parent
040dcc3fc2
commit
013818c4af
594 changed files with 19576 additions and 4478 deletions
570
plugins/mac-virtualcam/src/dal-plugin/OBSDALStream.mm
Normal file
570
plugins/mac-virtualcam/src/dal-plugin/OBSDALStream.mm
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
//
|
||||
// Stream.mm
|
||||
// obs-mac-virtualcam
|
||||
//
|
||||
// Created by John Boiles on 4/10/20.
|
||||
//
|
||||
// obs-mac-virtualcam is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// obs-mac-virtualcam is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with obs-mac-virtualcam. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#import "OBSDALStream.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <mach/mach_time.h>
|
||||
#include <CoreMediaIO/CMIOSampleBuffer.h>
|
||||
|
||||
#import "Logging.h"
|
||||
#import "CMSampleBufferUtils.h"
|
||||
#import "OBSDALPlugin.h"
|
||||
|
||||
@interface OBSDALStream () {
|
||||
CMSimpleQueueRef _queue;
|
||||
CFTypeRef _clock;
|
||||
NSImage *_testCardImage;
|
||||
dispatch_source_t _frameDispatchSource;
|
||||
NSSize _testCardSize;
|
||||
Float64 _fps;
|
||||
}
|
||||
|
||||
@property CMIODeviceStreamQueueAlteredProc alteredProc;
|
||||
@property void *alteredRefCon;
|
||||
@property (readonly) CMSimpleQueueRef queue;
|
||||
@property (readonly) CFTypeRef clock;
|
||||
@property UInt64 sequenceNumber;
|
||||
@property (readonly) NSImage *testCardImage;
|
||||
@property (readonly) NSSize testCardSize;
|
||||
@property (readonly) Float64 fps;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OBSDALStream
|
||||
|
||||
#define DEFAULT_FPS 30.0
|
||||
#define DEFAULT_WIDTH 1280
|
||||
#define DEFAULT_HEIGHT 720
|
||||
|
||||
- (instancetype _Nonnull)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_frameDispatchSource = dispatch_source_create(
|
||||
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
|
||||
dispatch_get_global_queue(
|
||||
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||||
__weak __typeof(self) wself = self;
|
||||
dispatch_source_set_event_handler(_frameDispatchSource, ^{
|
||||
[wself fillFrame];
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
DLog(@"Stream Dealloc");
|
||||
CMIOStreamClockInvalidate(_clock);
|
||||
CFRelease(_clock);
|
||||
_clock = NULL;
|
||||
CFRelease(_queue);
|
||||
_queue = NULL;
|
||||
dispatch_suspend(_frameDispatchSource);
|
||||
}
|
||||
|
||||
- (void)startServingDefaultFrames
|
||||
{
|
||||
DLogFunc(@"");
|
||||
_testCardImage = nil;
|
||||
_testCardSize = NSZeroSize;
|
||||
_fps = 0;
|
||||
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
||||
uint64_t intervalTime = (int64_t)(NSEC_PER_SEC / self.fps);
|
||||
dispatch_source_set_timer(_frameDispatchSource, startTime, intervalTime,
|
||||
0);
|
||||
dispatch_resume(_frameDispatchSource);
|
||||
}
|
||||
|
||||
- (void)stopServingDefaultFrames
|
||||
{
|
||||
DLogFunc(@"");
|
||||
dispatch_suspend(_frameDispatchSource);
|
||||
}
|
||||
|
||||
- (CMSimpleQueueRef)queue
|
||||
{
|
||||
if (_queue == NULL) {
|
||||
// Allocate a one-second long queue, which we can use our FPS constant for.
|
||||
OSStatus err = CMSimpleQueueCreate(kCFAllocatorDefault,
|
||||
self.fps, &_queue);
|
||||
if (err != noErr) {
|
||||
DLog(@"Err %d in CMSimpleQueueCreate", err);
|
||||
}
|
||||
}
|
||||
return _queue;
|
||||
}
|
||||
|
||||
- (CFTypeRef)clock
|
||||
{
|
||||
if (_clock == NULL) {
|
||||
OSStatus err = CMIOStreamClockCreate(
|
||||
kCFAllocatorDefault,
|
||||
CFSTR("obs-mac-virtualcam::Stream::clock"),
|
||||
(__bridge void *)self, CMTimeMake(1, 10), 100, 10,
|
||||
&_clock);
|
||||
if (err != noErr) {
|
||||
DLog(@"Error %d from CMIOStreamClockCreate", err);
|
||||
}
|
||||
}
|
||||
return _clock;
|
||||
}
|
||||
|
||||
- (NSSize)testCardSize
|
||||
{
|
||||
if (NSEqualSizes(_testCardSize, NSZeroSize)) {
|
||||
NSUserDefaults *defaults =
|
||||
[NSUserDefaults standardUserDefaults];
|
||||
int width = [[defaults objectForKey:kTestCardWidthKey]
|
||||
integerValue];
|
||||
int height = [[defaults objectForKey:kTestCardHeightKey]
|
||||
integerValue];
|
||||
if (width == 0 || height == 0) {
|
||||
_testCardSize =
|
||||
NSMakeSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
} else {
|
||||
_testCardSize = NSMakeSize(width, height);
|
||||
}
|
||||
}
|
||||
return _testCardSize;
|
||||
}
|
||||
|
||||
- (Float64)fps
|
||||
{
|
||||
if (_fps == 0) {
|
||||
NSUserDefaults *defaults =
|
||||
[NSUserDefaults standardUserDefaults];
|
||||
double fps =
|
||||
[[defaults objectForKey:kTestCardFPSKey] doubleValue];
|
||||
if (fps == 0) {
|
||||
_fps = DEFAULT_FPS;
|
||||
} else {
|
||||
_fps = fps;
|
||||
}
|
||||
}
|
||||
return _fps;
|
||||
}
|
||||
|
||||
- (NSImage *)testCardImage
|
||||
{
|
||||
if (_testCardImage == nil) {
|
||||
NSString *bundlePath = [[NSBundle
|
||||
bundleForClass:[OBSDALStream class]] bundlePath];
|
||||
NSString *placeHolderPath = [bundlePath
|
||||
stringByAppendingString:
|
||||
@"/Contents/Resources/placeholder.png"];
|
||||
NSImage *placeholderImage = [[NSImage alloc]
|
||||
initWithContentsOfFile:placeHolderPath];
|
||||
|
||||
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:self.testCardSize.width
|
||||
pixelsHigh:self.testCardSize.height
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSCalibratedRGBColorSpace
|
||||
bytesPerRow:0
|
||||
bitsPerPixel:0];
|
||||
rep.size = self.testCardSize;
|
||||
|
||||
float hScale =
|
||||
placeholderImage.size.width / self.testCardSize.width;
|
||||
float vScale =
|
||||
placeholderImage.size.height / self.testCardSize.height;
|
||||
|
||||
float scaling = fmax(hScale, vScale);
|
||||
|
||||
float newWidth = placeholderImage.size.width / scaling;
|
||||
float newHeight = placeholderImage.size.height / scaling;
|
||||
|
||||
float leftOffset = (self.testCardSize.width - newWidth) / 2;
|
||||
float topOffset = (self.testCardSize.height - newHeight) / 2;
|
||||
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
[NSGraphicsContext
|
||||
setCurrentContext:
|
||||
[NSGraphicsContext
|
||||
graphicsContextWithBitmapImageRep:rep]];
|
||||
|
||||
NSColor *backgroundColor = [NSColor blackColor];
|
||||
[backgroundColor set];
|
||||
NSRectFill(NSMakeRect(0, 0, self.testCardSize.width,
|
||||
self.testCardSize.height));
|
||||
|
||||
[placeholderImage drawInRect:NSMakeRect(leftOffset, topOffset,
|
||||
newWidth, newHeight)
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationCopy
|
||||
fraction:1.0];
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
|
||||
NSImage *testCardImage =
|
||||
[[NSImage alloc] initWithSize:self.testCardSize];
|
||||
[testCardImage addRepresentation:rep];
|
||||
|
||||
_testCardImage = testCardImage;
|
||||
}
|
||||
return _testCardImage;
|
||||
}
|
||||
|
||||
- (CMSimpleQueueRef)copyBufferQueueWithAlteredProc:
|
||||
(CMIODeviceStreamQueueAlteredProc)alteredProc
|
||||
alteredRefCon:(void *)alteredRefCon
|
||||
{
|
||||
self.alteredProc = alteredProc;
|
||||
self.alteredRefCon = alteredRefCon;
|
||||
|
||||
// Retain this since it's a copy operation
|
||||
CFRetain(self.queue);
|
||||
|
||||
return self.queue;
|
||||
}
|
||||
|
||||
- (CVPixelBufferRef)createPixelBufferWithTestAnimation
|
||||
{
|
||||
int width = self.testCardSize.width;
|
||||
int height = self.testCardSize.height;
|
||||
|
||||
NSDictionary *options = [NSDictionary
|
||||
dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithBool:YES],
|
||||
kCVPixelBufferCGImageCompatibilityKey,
|
||||
[NSNumber numberWithBool:YES],
|
||||
kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
|
||||
CVPixelBufferRef pxbuffer = NULL;
|
||||
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,
|
||||
height, kCVPixelFormatType_32ARGB,
|
||||
(__bridge CFDictionaryRef)options,
|
||||
&pxbuffer);
|
||||
|
||||
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
|
||||
|
||||
CVPixelBufferLockBaseAddress(pxbuffer, 0);
|
||||
void *pxdata = CVPixelBufferGetBaseAddressOfPlane(pxbuffer, 0);
|
||||
NSParameterAssert(pxdata != NULL);
|
||||
|
||||
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
pxdata, width, height, 8,
|
||||
CVPixelBufferGetBytesPerRowOfPlane(pxbuffer, 0), rgbColorSpace,
|
||||
kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Big);
|
||||
NSParameterAssert(context);
|
||||
|
||||
NSGraphicsContext *nsContext = [NSGraphicsContext
|
||||
graphicsContextWithCGContext:context
|
||||
flipped:NO];
|
||||
[NSGraphicsContext setCurrentContext:nsContext];
|
||||
|
||||
NSRect rect = NSMakeRect(0, 0, self.testCardImage.size.width,
|
||||
self.testCardImage.size.height);
|
||||
CGImageRef image = [self.testCardImage CGImageForProposedRect:&rect
|
||||
context:nsContext
|
||||
hints:nil];
|
||||
CGContextDrawImage(context,
|
||||
CGRectMake(0, 0, CGImageGetWidth(image),
|
||||
CGImageGetHeight(image)),
|
||||
image);
|
||||
|
||||
// DrawDialWithFrame(
|
||||
// NSMakeRect(0, 0, width, height),
|
||||
// (int(self.fps) - self.sequenceNumber % int(self.fps)) * 360 /
|
||||
// int(self.fps));
|
||||
|
||||
CGContextRelease(context);
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
|
||||
|
||||
return pxbuffer;
|
||||
}
|
||||
|
||||
- (void)fillFrame
|
||||
{
|
||||
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
|
||||
DLog(@"Queue is full, bailing out");
|
||||
return;
|
||||
}
|
||||
|
||||
CVPixelBufferRef pixelBuffer =
|
||||
[self createPixelBufferWithTestAnimation];
|
||||
|
||||
uint64_t hostTime = mach_absolute_time();
|
||||
CMSampleTimingInfo timingInfo =
|
||||
CMSampleTimingInfoForTimestamp(hostTime, self.fps, 1);
|
||||
|
||||
OSStatus err = CMIOStreamClockPostTimingEvent(
|
||||
timingInfo.presentationTimeStamp, hostTime, true, self.clock);
|
||||
if (err != noErr) {
|
||||
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
|
||||
}
|
||||
|
||||
CMFormatDescriptionRef format;
|
||||
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault,
|
||||
pixelBuffer, &format);
|
||||
|
||||
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
|
||||
|
||||
CMSampleBufferRef buffer;
|
||||
err = CMIOSampleBufferCreateForImageBuffer(
|
||||
kCFAllocatorDefault, pixelBuffer, format, &timingInfo,
|
||||
self.sequenceNumber, kCMIOSampleBufferNoDiscontinuities,
|
||||
&buffer);
|
||||
CFRelease(pixelBuffer);
|
||||
CFRelease(format);
|
||||
if (err != noErr) {
|
||||
DLog(@"CMIOSampleBufferCreateForImageBuffer err %d", err);
|
||||
}
|
||||
|
||||
CMSimpleQueueEnqueue(self.queue, buffer);
|
||||
|
||||
// Inform the clients that the queue has been altered
|
||||
if (self.alteredProc != NULL) {
|
||||
(self.alteredProc)(self.objectId, buffer, self.alteredRefCon);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)queueFrameWithSize:(NSSize)size
|
||||
timestamp:(uint64_t)timestamp
|
||||
fpsNumerator:(uint32_t)fpsNumerator
|
||||
fpsDenominator:(uint32_t)fpsDenominator
|
||||
frameData:(NSData *)frameData
|
||||
{
|
||||
if (CMSimpleQueueGetFullness(self.queue) >= 1.0) {
|
||||
DLog(@"Queue is full, bailing out");
|
||||
return;
|
||||
}
|
||||
OSStatus err = noErr;
|
||||
|
||||
CMSampleTimingInfo timingInfo = CMSampleTimingInfoForTimestamp(
|
||||
timestamp, fpsNumerator, fpsDenominator);
|
||||
|
||||
err = CMIOStreamClockPostTimingEvent(timingInfo.presentationTimeStamp,
|
||||
mach_absolute_time(), true,
|
||||
self.clock);
|
||||
if (err != noErr) {
|
||||
DLog(@"CMIOStreamClockPostTimingEvent err %d", err);
|
||||
}
|
||||
|
||||
self.sequenceNumber = CMIOGetNextSequenceNumber(self.sequenceNumber);
|
||||
|
||||
CMSampleBufferRef sampleBuffer;
|
||||
CMSampleBufferCreateFromData(size, timingInfo, self.sequenceNumber,
|
||||
frameData, &sampleBuffer);
|
||||
CMSimpleQueueEnqueue(self.queue, sampleBuffer);
|
||||
|
||||
// Inform the clients that the queue has been altered
|
||||
if (self.alteredProc != NULL) {
|
||||
(self.alteredProc)(self.objectId, sampleBuffer,
|
||||
self.alteredRefCon);
|
||||
}
|
||||
}
|
||||
|
||||
- (CMVideoFormatDescriptionRef)getFormatDescription
|
||||
{
|
||||
CMVideoFormatDescriptionRef formatDescription;
|
||||
OSStatus err = CMVideoFormatDescriptionCreate(
|
||||
kCFAllocatorDefault, kCMVideoCodecType_422YpCbCr8,
|
||||
self.testCardSize.width, self.testCardSize.height, NULL,
|
||||
&formatDescription);
|
||||
if (err != noErr) {
|
||||
DLog(@"Error %d from CMVideoFormatDescriptionCreate", err);
|
||||
}
|
||||
return formatDescription;
|
||||
}
|
||||
|
||||
#pragma mark - CMIOObject
|
||||
|
||||
- (UInt32)getPropertyDataSizeWithAddress:(CMIOObjectPropertyAddress)address
|
||||
qualifierDataSize:(UInt32)qualifierDataSize
|
||||
qualifierData:(nonnull const void *)qualifierData
|
||||
{
|
||||
switch (address.mSelector) {
|
||||
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
|
||||
return sizeof(CMTime);
|
||||
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
|
||||
return sizeof(UInt32);
|
||||
case kCMIOObjectPropertyName:
|
||||
return sizeof(CFStringRef);
|
||||
case kCMIOObjectPropertyManufacturer:
|
||||
return sizeof(CFStringRef);
|
||||
case kCMIOObjectPropertyElementName:
|
||||
return sizeof(CFStringRef);
|
||||
case kCMIOObjectPropertyElementCategoryName:
|
||||
return sizeof(CFStringRef);
|
||||
case kCMIOObjectPropertyElementNumberName:
|
||||
return sizeof(CFStringRef);
|
||||
case kCMIOStreamPropertyDirection:
|
||||
return sizeof(UInt32);
|
||||
case kCMIOStreamPropertyTerminalType:
|
||||
return sizeof(UInt32);
|
||||
case kCMIOStreamPropertyStartingChannel:
|
||||
return sizeof(UInt32);
|
||||
case kCMIOStreamPropertyLatency:
|
||||
return sizeof(UInt32);
|
||||
case kCMIOStreamPropertyFormatDescriptions:
|
||||
return sizeof(CFArrayRef);
|
||||
case kCMIOStreamPropertyFormatDescription:
|
||||
return sizeof(CMFormatDescriptionRef);
|
||||
case kCMIOStreamPropertyFrameRateRanges:
|
||||
return sizeof(AudioValueRange);
|
||||
case kCMIOStreamPropertyFrameRate:
|
||||
case kCMIOStreamPropertyFrameRates:
|
||||
return sizeof(Float64);
|
||||
case kCMIOStreamPropertyMinimumFrameRate:
|
||||
return sizeof(Float64);
|
||||
case kCMIOStreamPropertyClock:
|
||||
return sizeof(CFTypeRef);
|
||||
default:
|
||||
DLog(@"Stream unhandled getPropertyDataSizeWithAddress for %@",
|
||||
[OBSDALObjectStore
|
||||
StringFromPropertySelector:address.mSelector]);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
- (void)getPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
|
||||
qualifierDataSize:(UInt32)qualifierDataSize
|
||||
qualifierData:(nonnull const void *)qualifierData
|
||||
dataSize:(UInt32)dataSize
|
||||
dataUsed:(nonnull UInt32 *)dataUsed
|
||||
data:(nonnull void *)data
|
||||
{
|
||||
switch (address.mSelector) {
|
||||
case kCMIOObjectPropertyName:
|
||||
*static_cast<CFStringRef *>(data) = CFSTR("OBS Virtual Camera");
|
||||
*dataUsed = sizeof(CFStringRef);
|
||||
break;
|
||||
case kCMIOObjectPropertyElementName:
|
||||
*static_cast<CFStringRef *>(data) =
|
||||
CFSTR("OBS Virtual Camera Stream Element");
|
||||
*dataUsed = sizeof(CFStringRef);
|
||||
break;
|
||||
case kCMIOObjectPropertyManufacturer:
|
||||
case kCMIOObjectPropertyElementCategoryName:
|
||||
case kCMIOObjectPropertyElementNumberName:
|
||||
case kCMIOStreamPropertyTerminalType:
|
||||
case kCMIOStreamPropertyStartingChannel:
|
||||
case kCMIOStreamPropertyLatency:
|
||||
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
|
||||
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
|
||||
break;
|
||||
case kCMIOStreamPropertyDirection:
|
||||
*static_cast<UInt32 *>(data) = 1;
|
||||
*dataUsed = sizeof(UInt32);
|
||||
break;
|
||||
case kCMIOStreamPropertyFormatDescriptions:
|
||||
*static_cast<CFArrayRef *>(
|
||||
data) = (__bridge_retained CFArrayRef)[NSArray
|
||||
arrayWithObject:(__bridge_transfer NSObject *)
|
||||
[self getFormatDescription]];
|
||||
*dataUsed = sizeof(CFArrayRef);
|
||||
break;
|
||||
case kCMIOStreamPropertyFormatDescription:
|
||||
*static_cast<CMVideoFormatDescriptionRef *>(data) =
|
||||
[self getFormatDescription];
|
||||
*dataUsed = sizeof(CMVideoFormatDescriptionRef);
|
||||
break;
|
||||
case kCMIOStreamPropertyFrameRateRanges:
|
||||
AudioValueRange range;
|
||||
range.mMinimum = self.fps;
|
||||
range.mMaximum = self.fps;
|
||||
*static_cast<AudioValueRange *>(data) = range;
|
||||
*dataUsed = sizeof(AudioValueRange);
|
||||
break;
|
||||
case kCMIOStreamPropertyFrameRate:
|
||||
case kCMIOStreamPropertyFrameRates:
|
||||
*static_cast<Float64 *>(data) = self.fps;
|
||||
*dataUsed = sizeof(Float64);
|
||||
break;
|
||||
case kCMIOStreamPropertyMinimumFrameRate:
|
||||
*static_cast<Float64 *>(data) = self.fps;
|
||||
*dataUsed = sizeof(Float64);
|
||||
break;
|
||||
case kCMIOStreamPropertyClock:
|
||||
*static_cast<CFTypeRef *>(data) = self.clock;
|
||||
// This one was incredibly tricky and cost me many hours to find. It seems that DAL expects
|
||||
// the clock to be retained when returned. It's unclear why, and that seems inconsistent
|
||||
// with other properties that don't have the same behavior. But this is what Apple's sample
|
||||
// code does.
|
||||
// https://github.com/lvsti/CoreMediaIO-DAL-Example/blob/0392cb/Sources/Extras/CoreMediaIO/DeviceAbstractionLayer/Devices/DP/Properties/CMIO_DP_Property_Clock.cpp#L75
|
||||
CFRetain(*static_cast<CFTypeRef *>(data));
|
||||
*dataUsed = sizeof(CFTypeRef);
|
||||
break;
|
||||
default:
|
||||
DLog(@"Stream unhandled getPropertyDataWithAddress for %@",
|
||||
[OBSDALObjectStore
|
||||
StringFromPropertySelector:address.mSelector]);
|
||||
*dataUsed = 0;
|
||||
};
|
||||
}
|
||||
|
||||
- (BOOL)hasPropertyWithAddress:(CMIOObjectPropertyAddress)address
|
||||
{
|
||||
switch (address.mSelector) {
|
||||
case kCMIOObjectPropertyName:
|
||||
case kCMIOObjectPropertyElementName:
|
||||
case kCMIOStreamPropertyFormatDescriptions:
|
||||
case kCMIOStreamPropertyFormatDescription:
|
||||
case kCMIOStreamPropertyFrameRateRanges:
|
||||
case kCMIOStreamPropertyFrameRate:
|
||||
case kCMIOStreamPropertyFrameRates:
|
||||
case kCMIOStreamPropertyMinimumFrameRate:
|
||||
case kCMIOStreamPropertyClock:
|
||||
return true;
|
||||
case kCMIOObjectPropertyManufacturer:
|
||||
case kCMIOObjectPropertyElementCategoryName:
|
||||
case kCMIOObjectPropertyElementNumberName:
|
||||
case kCMIOStreamPropertyDirection:
|
||||
case kCMIOStreamPropertyTerminalType:
|
||||
case kCMIOStreamPropertyStartingChannel:
|
||||
case kCMIOStreamPropertyLatency:
|
||||
case kCMIOStreamPropertyInitialPresentationTimeStampForLinkedAndSyncedAudio:
|
||||
case kCMIOStreamPropertyOutputBuffersNeededForThrottledPlayback:
|
||||
DLog(@"TODO: %@",
|
||||
[OBSDALObjectStore
|
||||
StringFromPropertySelector:address.mSelector]);
|
||||
return false;
|
||||
default:
|
||||
DLog(@"Stream unhandled hasPropertyWithAddress for %@",
|
||||
[OBSDALObjectStore
|
||||
StringFromPropertySelector:address.mSelector]);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
- (BOOL)isPropertySettableWithAddress:(CMIOObjectPropertyAddress)address
|
||||
{
|
||||
DLog(@"Stream unhandled isPropertySettableWithAddress for %@",
|
||||
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)setPropertyDataWithAddress:(CMIOObjectPropertyAddress)address
|
||||
qualifierDataSize:(UInt32)qualifierDataSize
|
||||
qualifierData:(nonnull const void *)qualifierData
|
||||
dataSize:(UInt32)dataSize
|
||||
data:(nonnull const void *)data
|
||||
{
|
||||
DLog(@"Stream unhandled setPropertyDataWithAddress for %@",
|
||||
[OBSDALObjectStore StringFromPropertySelector:address.mSelector]);
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Add table
Add a link
Reference in a new issue