Darwin-Streaming-Server/broadcasterctl/BroadcasterAdminController.m

545 lines
16 KiB
Mathematica
Raw Permalink Normal View History

2017-03-07 21:07:56 -08:00
/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
#import "BroadcasterAdminController.h"
#import "BroadcasterRemoteAdmin.h"
#include <signal.h>
#include <unistd.h>
#include "revision.h"
@implementation BroadcasterAdminController
// Class methods
+ (id)broadcasterAdminController
{
return [[[BroadcasterAdminController alloc] init] autorelease];
}
+ (void)printUsage
{
NSString *versionString = [NSString stringWithCString:kVersionString];
NSString *buildString = [NSString stringWithCString:kBuildString];
NSString *usage = [NSString stringWithFormat:@"\nbroadcasterctl %@ (%@)\n\nUsage: broadcasterctl [-b broadcaster-path] [-a audiopreset] [-v videopreset] [-n networkpreset] [-t (audio|video|av)] [-r (record|norecord)] [-f settingsfile] ((config|status|presets|launch|start|stop|restart|quit)\n\n", versionString, buildString];
printf([usage cString]);
}
- (NSArray *)otherBroadcasters
{
NSTask *psTask;
NSString *psResult;
NSPipe *pipe;
NSFileHandle *pipeFileHandle;
NSArray *splitResult;
NSEnumerator *processEnumerator;
id processLine;
NSMutableArray *processesToKill = [NSMutableArray array];
NSString *processID;
NSScanner *processIDScanner;
psTask = [[[NSTask alloc] init] autorelease];
[psTask setLaunchPath:@"/bin/ps"];
[psTask setArguments:[NSArray arrayWithObject:@"awx"]];
pipe = [NSPipe pipe];
[psTask setStandardOutput:pipe];
[psTask launch];
[psTask waitUntilExit];
pipeFileHandle = [pipe fileHandleForReading];
psResult = [[[NSString alloc] initWithData:[pipeFileHandle readDataToEndOfFile] encoding:NSASCIIStringEncoding] autorelease];
splitResult = [psResult componentsSeparatedByString:@"\n"];
processEnumerator = [splitResult objectEnumerator];
while (processLine = [processEnumerator nextObject]) {
if ([processLine rangeOfString:@"/QuickTime Broadcaster.app"].location != NSNotFound) {
processID = [NSString string];
processIDScanner = [NSScanner scannerWithString:processLine];
[processIDScanner scanUpToString:@" " intoString:&processID];
[processesToKill addObject:processID];
}
}
return [NSArray arrayWithArray:processesToKill];
}
- (void)killOtherBroadcastersWithSignal:(int)signal
{
NSArray *otherBroadcasters = [self otherBroadcasters];
NSEnumerator *processEnumerator = [otherBroadcasters objectEnumerator];
id process;
BOOL success = YES;
while (process = [processEnumerator nextObject]) {
if (kill([process intValue], 1) != 0)
success = NO;
}
if (!success)
printf(kCantKillError);
}
- (NSString *)resultForArgument:(NSString *)argName foundError:(BOOL *)err
{
NSArray *args = [[NSProcessInfo processInfo] arguments];
unsigned long argLoc;
NSString *result;
argLoc = [args indexOfObject:argName];
if (argLoc == NSNotFound) {
*err = NO;
return nil;
}
argLoc++;
// if there are no more arguments, something is wrong
if ([args count] <= argLoc) {
*err = YES;
return nil;
}
result = [args objectAtIndex:argLoc];
// if it starts with a dash, something is wrong
if ([result characterAtIndex:0] == '-') {
*err = YES;
return nil;
}
// at this point, all should be fine
*err = NO;
return result;
}
- (NSString *)fixPath:(NSString *)path
{
NSString *pwd = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"];
NSMutableArray *pathComponents = [NSMutableArray arrayWithArray:[[path stringByExpandingTildeInPath] pathComponents]];
if (!pwd)
return path;
if ([path isAbsolutePath])
return path;
while ([[pathComponents objectAtIndex:0] isEqualToString:@".."]) {
[pathComponents removeObjectAtIndex:0];
pwd = [pwd stringByDeletingLastPathComponent];
}
return [pwd stringByAppendingPathComponent:[NSString pathWithComponents:pathComponents]];
}
- (id)init
{
NSArray *args = [[NSProcessInfo processInfo] arguments];
NSString *actionArg = [args lastObject];
BOOL err;
NSString *argValue;
self = [super init];
if ([args count] <= 1) {
return nil; // bail
}
// look for an app path argument
argValue = [self resultForArgument:@"-b" foundError:&err];
if (err)
return nil;
if (argValue)
[self setMyBroadcasterAppPath:[self fixPath:argValue]];
else
[self setMyBroadcasterAppPath:kDefaultBroadcasterLoc];
// if we're not telling it to quit, launch the broadcaster if it's not running
if ((![actionArg isEqualToString:@"quit"]) && (![self myBroadcasterProxy])) {
if ([[self otherBroadcasters] count] > 0) {
printf("\nCannot remotely administer the current Broadcaster.");
printf("\nUse the 'quit' command to quit the currently running broadcaster.\n\n");
return self;
}
if ([actionArg isEqualToString:@"launch"])
[self launchBroadcaster];
else {
printf("\nThe broadcaster isn't running.");
printf("\nLaunch it with the 'launch' command.\n\n");
return self;
}
}
// config file argument
argValue = [self resultForArgument:@"-f" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
[[self myBroadcasterProxy] setCurrentPresetName:nil ofType:kPresetAudio];
[[self myBroadcasterProxy] setCurrentPresetName:nil ofType:kPresetVideo];
[[self myBroadcasterProxy] setCurrentPresetName:nil ofType:kPresetNetwork];
NS_DURING
[[self myBroadcasterProxy] setBroadcastSettingsFile:[self fixPath:argValue]];
NS_HANDLER
printf("ERROR: Invalid settings file.\n");
return self;
NS_ENDHANDLER
}
// audio preset argument
argValue = [self resultForArgument:@"-a" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
[[self myBroadcasterProxy] setCurrentPresetName:argValue ofType:kPresetAudio];
}
// video preset argument
argValue = [self resultForArgument:@"-v" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
[[self myBroadcasterProxy] setCurrentPresetName:argValue ofType:kPresetVideo];
}
// network preset argument
argValue = [self resultForArgument:@"-n" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
[[self myBroadcasterProxy] setCurrentPresetName:argValue ofType:kPresetNetwork];
}
// stream type
argValue = [self resultForArgument:@"-t" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
//audio/video/av
if ([self myBroadcasterProxy]) {
if ([argValue isEqualToString:@"audio"]) {
[[self myBroadcasterProxy] setStreamEnabled:YES ofType:kStreamTypeAudio];
[[self myBroadcasterProxy] setStreamEnabled:NO ofType:kStreamTypeVideo];
}
else if ([argValue isEqualToString:@"video"]) {
[[self myBroadcasterProxy] setStreamEnabled:NO ofType:kStreamTypeAudio];
[[self myBroadcasterProxy] setStreamEnabled:YES ofType:kStreamTypeVideo];
}
else if ([argValue isEqualToString:@"av"]) {
[[self myBroadcasterProxy] setStreamEnabled:YES ofType:kStreamTypeAudio];
[[self myBroadcasterProxy] setStreamEnabled:YES ofType:kStreamTypeVideo];
}
else
return nil;
}
}
/*// recording path argument
argValue = [self resultForArgument:@"-p" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
[[self myBroadcasterProxy] setRecordingPath:argValue];
}*/
// recording argument
argValue = [self resultForArgument:@"-r" foundError:&err];
if (err)
return nil;
if (argValue && [self myBroadcasterProxy]) {
if ([argValue isEqualToString:@"record"])
[[self myBroadcasterProxy] setRecording:YES];
else if ([argValue isEqualToString:@"norecord"])
[[self myBroadcasterProxy] setRecording:NO];
}
if ([actionArg isEqualToString:@"launch"]) {
// no need to do anything... it'll launch on its own
}
else if ([actionArg isEqualToString:@"start"])
[self startBroadcaster];
else if ([actionArg isEqualToString:@"stop"])
[self stopBroadcaster];
else if ([actionArg isEqualToString:@"restart"]) {
[self stopBroadcaster];
[self startBroadcaster];
}
else if ([actionArg isEqualToString:@"quit"])
[self quitBroadcaster];
else if (([actionArg isEqualToString:@"config"]) || ([actionArg isEqualToString:@"status"]))
[self printConfig];
else if ([actionArg isEqualToString:@"xmlstatus"])
[self printXMLConfig];
else if ([actionArg isEqualToString:@"presets"])
[self printPresets];
else
return nil;
return self;
}
// Commands (from broadcasterctl args)
- (void)launchBroadcaster
{
NSString *launchPath;
NSTask *broadcasterTask;
launchPath = [[self myBroadcasterAppPath] stringByAppendingPathComponent:@"Contents"];
launchPath = [launchPath stringByAppendingPathComponent:@"MacOS"];
launchPath = [launchPath stringByAppendingPathComponent:@"QuickTime Broadcaster"];
if (![[NSFileManager defaultManager] fileExistsAtPath:launchPath]) {
printf(kCantFindBroadcasterError);
return;
}
if (!(broadcasterTask = [NSTask launchedTaskWithLaunchPath:launchPath arguments:[NSArray arrayWithObject:@"-noui"]])) {
printf(kCantLaunchBroacasterError);
return;
}
if (![broadcasterTask isRunning]) {
printf(kCantLaunchBroacasterError);
return;
}
while (![self myBroadcasterProxy])
sleep(1);
}
- (void)startBroadcaster
{
int i;
int state;
[myBroadcasterProxy startBroadcast];
for (i = 0; i < 7; i++) {
sleep(1);
state = [[self myBroadcasterProxy] state];
if (state == kBroadcasterStateBroadcasting)
return;
}
printf(kBadConfigFileError);
[self killOtherBroadcastersWithSignal:9];
}
- (void)stopBroadcaster
{
if (![self myBroadcasterProxy]) {
printf(kBroadcasterNotRunningError);
return;
}
[[self myBroadcasterProxy] stopBroadcast];
}
- (void)quitBroadcaster
{
if ([self myBroadcasterProxy]) {
[[self myBroadcasterProxy] quit];
}
[self killOtherBroadcastersWithSignal:1];
}
- (void)printConfig
{
NSString *settingStringValue;
if (![self myBroadcasterProxy]) {
printf(kBroadcasterNotRunningError);
return;
}
printf("\nCurrent State: ");
printf([[[self myBroadcasterProxy] stateString] cString]);
if (settingStringValue = [[self myBroadcasterProxy] broadcastSettingsFile]) {
printf("\nBroadcast Settings File: ");
printf([settingStringValue cString]);
}
printf("\nAudio Stream: ");
if ([[self myBroadcasterProxy] streamEnabled:kStreamTypeAudio])
printf("Enabled");
else
printf("Disabled");
printf("\nVideo Stream: ");
if ([[self myBroadcasterProxy] streamEnabled:kStreamTypeVideo])
printf("Enabled");
else
printf("Disabled");
printf("\n\nCurrently Selected Presets:");
printf("\n Audio: ");
if (settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetAudio])
printf([settingStringValue cString]);
else
printf("(none)");
printf("\n Video: ");
if (settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetVideo])
printf([settingStringValue cString]);
else
printf("(none)");
printf("\n Network: ");
if (settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetNetwork])
printf([settingStringValue cString]);
else
printf("(none)");
if ([[self myBroadcasterProxy] recording]) {
printf("\n\nRecording to ");
settingStringValue = [[self myBroadcasterProxy] recordingPath];
if (settingStringValue) {
printf([settingStringValue cString]);
}
else
printf("(default loc)");
}
else {
printf("\n\nNot Recording");
}
printf("\n\n");
}
- (void)printXMLConfig
{
NSMutableDictionary *printDict = [NSMutableDictionary dictionary];
NSString *settingStringValue;
NSMutableDictionary *currentPresets = [NSMutableDictionary dictionary];
NSMutableDictionary *presetDict = [NSMutableDictionary dictionary];
NSData *plistData;
if (![self myBroadcasterProxy]) {
[printDict setObject:[NSString stringWithCString:kBroadcasterNotRunningError] forKey:@"error"];
printf([[printDict description] cString]);
return;
}
// state and settings file
[printDict setObject:[[self myBroadcasterProxy] stateString] forKey:@"stateString"];
if (!(settingStringValue = [[self myBroadcasterProxy] broadcastSettingsFile]))
settingStringValue = @"";
[printDict setObject:settingStringValue forKey:@"broadcastSettingsFile"];
// currently selected presets
if (!(settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetAudio]))
settingStringValue = @"";
[currentPresets setObject:settingStringValue forKey:@"audio"];
if (!(settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetVideo]))
settingStringValue = @"";
[currentPresets setObject:settingStringValue forKey:@"video"];
if (!(settingStringValue = [[self myBroadcasterProxy] currentPresetName:kPresetNetwork]))
settingStringValue = @"";
[currentPresets setObject:settingStringValue forKey:@"network"];
[printDict setObject:currentPresets forKey:@"selectedPresets"];
// recording options
[printDict setObject:[NSNumber numberWithBool:[[self myBroadcasterProxy] recording]] forKey:@"recording"];
[printDict setObject:[[self myBroadcasterProxy] recordingPath] forKey:@"recordingPath"];
// all presets
[presetDict setObject:[[self myBroadcasterProxy] presetNameList:kPresetAudio] forKey:@"audio"];
[presetDict setObject:[[self myBroadcasterProxy] presetNameList:kPresetVideo] forKey:@"video"];
[presetDict setObject:[[self myBroadcasterProxy] presetNameList:kPresetNetwork] forKey:@"network"];
[printDict setObject:presetDict forKey:@"allPresets"];
// print the dictionary
plistData = (NSData *)CFPropertyListCreateXMLData(NULL, printDict);
printf([[[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding] cString]);
}
- (void)printPresets
{
NSArray *allPresets;
NSEnumerator *presetEnumerator;
id currentPreset;
if (![self myBroadcasterProxy]) {
printf(kBroadcasterNotRunningError);
return;
}
printf("Audio Presets:\n");
allPresets = [[self myBroadcasterProxy] presetNameList:kPresetAudio];
if ([allPresets count] == 0)
printf(" (none)\n");
else {
presetEnumerator = [allPresets objectEnumerator];
while (currentPreset = [presetEnumerator nextObject]) {
printf(" ");
printf([currentPreset cString]);
printf("\n");
}
}
printf("Video Presets:\n");
allPresets = [[self myBroadcasterProxy] presetNameList:kPresetVideo];
if ([allPresets count] == 0)
printf(" (none)\n");
else {
presetEnumerator = [allPresets objectEnumerator];
while (currentPreset = [presetEnumerator nextObject]) {
printf(" ");
printf([currentPreset cString]);
printf("\n");
}
}
printf("Network Presets:\n");
allPresets = [[self myBroadcasterProxy] presetNameList:kPresetNetwork];
if ([allPresets count] == 0)
printf(" (none)\n");
else {
presetEnumerator = [allPresets objectEnumerator];
while (currentPreset = [presetEnumerator nextObject]) {
printf(" ");
printf([currentPreset cString]);
printf("\n");
}
}
}
// Accessor methods
- (NSString *)myBroadcasterAppPath
{
return myBroadcasterAppPath;
}
- (void)setMyBroadcasterAppPath:(NSString *)newPath
{
[newPath retain];
if (myBroadcasterAppPath)
[myBroadcasterAppPath release];
myBroadcasterAppPath = newPath;
}
- (id)myBroadcasterProxy
{
if (!myBroadcasterProxy) {
myBroadcasterProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:kBroadcasterRemoteAdmin host:nil] retain];
}
return myBroadcasterProxy;
}
@end