KeyOff without effect - objective-c

I'm using FMod for first time, and I don't understand why my code doesn't trigger Sound Designer's keyoff.
Working env
iOS
Xcode
Verified
.fev and event's keyoff tested with fmod_eventPlayer
all FOD_RESULT are OK
Here the code processed chronologically
-(void) initFmod
{
...
//init
result = _eventSystem->init(32, FMOD_INIT_NORMAL | FMOD_INIT_ENABLE_PROFILE, NULL, FMOD_EVENT_INIT_NORMAL);
...
//load music bank settings
result = FMOD_OK;
[[NSString stringWithFormat:#"%#/_music.fev", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = _eventSystem->load(buffer, NULL, NULL);
...
}
-(void) onMusicGameStart
{
///////////// LOAD Game Music ////////////
//Build Event name
FMOD_RESULT result = FMOD_OK;
NSString *musicGameEvent = #"music/music/music_sample_with_keyOff";
const char *eventGame = [musicGameEvent UTF8String];
//Get event from Fmod
result = _eventSystem->getEvent(eventGame, FMOD_EVENT_DEFAULT, &_musicGame);
result = _musicGame->start();
...
}
-(void) stopMusic
{
//Stop current Music
[self triggerEventKeyoff:_musicGame];
}
-(void) triggerEventKeyoff:(FMOD::Event*)event
{
if(event)
{
FMOD_RESULT result = FMOD_OK;
//Get Event's Parameter
FMOD::EventParameter *param;
result = event->getParameterByIndex(0, &param);
//Check error message
[self checkResult:result even:nil];
//trigger KeyOff
if(result == FMOD_OK)
{
result = param->keyOff();
//Check error message
[self checkResult:result even:nil];
}
}
}
The music associated to _musicGame doesn't play its KeyOff and just continue playing.
_musicGame is only set in onMusicGameStart().
I don't know what to test from this point.
By the way, I'm not able to launch fmod_profiler (crash at launch).
Thanks for your replies.

There is a bug with the current fmod_profiler, it's simple to fix though:
Open the terminal and navigate to the location of fmod_profiler.app
Navigate into fmod_profiler.app/Contents/MacOS
Type: "chmod u+x fmod_profiler
Now you can run the app properly from the finder.
Regarding keyoff, I would contact FMOD support.

Related

How to properly close a FFmpeg stream and AVFormatContext without leaking memory?

I have built an app that uses FFmpeg to connect to remote IP cameras in order to receive video and audio frames via RTSP 2.0.
The app is built using Xcode 10-11 and Objective-C with a custom FFmpeg build config.
The architecture is the following:
MyApp
Document_0
RTSPContainerObject_0
RTSPObject_0
RTSPContainerObject_1
RTSPObject_1
...
Document_1
...
GOAL:
After closing Document_0 no FFmpeg objects should be leaked.
The closing process should stop-frame reading and destroy all objects which use FFmpeg.
PROBLEM:
Somehow Xcode's memory debugger shows two instances of MyApp.
FACTS:
macOS'es Activity Monitor doesn't show two instances of MyApp.
macOS'es Activity Monitor doesn't any instances of FFmpeg or other child processes.
The issue is not related to some leftover memory due to a late memory snapshot since it can be reproduced easily.
Xcode's memory debugger shows that the second instance only having RTSPObject's AVFormatContext and no other objects.
The second instance has an AVFormatContext and the RTPSObject still has a pointer to the AVFormatContext.
FACTS:
Opening and closing the second document Document_1 leads to the same problem and having two objects leaked. This means that there is a bug that creates scalable problems. More and more memory is used and unavailable.
Here is my termination code:
- (void)terminate
{
// * Video and audio frame provisioning termination *
[self stopVideoStream];
[self stopAudioStream];
// *
// * Video codec termination *
avcodec_free_context(&_videoCodecContext); // NULL pointer safe.
self.videoCodecContext = NULL;
// *
// * Audio codec termination *
avcodec_free_context(&_audioCodecContext); // NULL pointer safe.
self.audioCodecContext = NULL;
// *
if (self.packet)
{
// Free the packet that was allocated by av_read_frame.
av_packet_unref(&packet); // The documentation doesn't mention NULL safety.
self.packet = NULL;
}
if (self.currentAudioPacket)
{
av_packet_unref(_currentAudioPacket);
self.currentAudioPacket = NULL;
}
// Free raw frame data.
av_freep(&_rawFrameData); // NULL pointer safe.
// Free the swscaler context swsContext.
self.isFrameConversionContextAllocated = NO;
sws_freeContext(scallingContext); // NULL pointer safe.
[self.audioPacketQueue removeAllObjects];
self.audioPacketQueue = nil;
self.audioPacketQueueLock = nil;
self.packetQueueLock = nil;
self.audioStream = nil;
BXLogInDomain(kLogDomainSources, kLogLevelVerbose, #"%s:%d: All streams have been terminated!", __FUNCTION__, __LINE__);
// * Session context termination *
AVFormatContext *pFormatCtx = self.sessionContext;
BOOL shouldProceedWithInputSessionTermination = self.isInputStreamOpen && self.shouldTerminateStreams && pFormatCtx;
NSLog(#"\nTerminating session context...");
if (shouldProceedWithInputSessionTermination)
{
NSLog(#"\nTerminating...");
//av_write_trailer(pFormatCtx);
// Discard all internally buffered data.
avformat_flush(pFormatCtx); // The documentation doesn't mention NULL safety.
// Close an opened input AVFormatContext and free it and all its contents.
// WARNING: Closing an non-opened stream will cause avformat_close_input to crash.
avformat_close_input(&pFormatCtx); // The documentation doesn't mention NULL safety.
NSLog(#"Logging leftovers - %p, %p %p", self.sessionContext, _sessionContext, pFormatCtx);
avformat_free_context(pFormatCtx);
NSLog(#"Logging content = %c", *self.sessionContext);
//avformat_free_context(pFormatCtx); - Not needed because avformat_close_input is closing it.
self.sessionContext = NULL;
}
// *
}
IMPORTANT: The termination sequence is:
New frame will be read.
-[(RTSPObject)StreamInput currentVideoFrameDurationSec]
-[(RTSPObject)StreamInput frameDuration:]
-[(RTSPObject)StreamInput currentCGImageRef]
-[(RTSPObject)StreamInput convertRawFrameToRGB]
-[(RTSPObject)StreamInput pixelBufferFromImage:]
-[(RTSPObject)StreamInput cleanup]
-[(RTSPObject)StreamInput dealloc]
-[(RTSPObject)StreamInput stopVideoStream]
-[(RTSPObject)StreamInput stopAudioStream]
Terminating session context...
Terminating...
Logging leftovers - 0x109ec6400, 0x109ec6400 0x109ec6400
Logging content = \330
-[Document dealloc]
NOT WORKING SOLUTIONS:
Changing the order of object releases (The AVFormatContext has been freed first but it didn't lead to any change).
Calling RTSPObject's cleanup method much sooner to give FFmpeg more time to handle object releases.
Reading a lot of SO answers and FFmpeg documentation to find a clean cleanup process or newer code which might highlight why the object release doesn't happen properly.
I am currently reading the documentation on AVFormatContext since I believe that I am forgetting to release something. This believe is based on the memory debuggers output that AVFormatContext is still around.
Here is my creation code:
#pragma mark # Helpers - Start
- (NSError *)openInputStreamWithVideoStreamId:(int)videoStreamId
audioStreamId:(int)audioStreamId
useFirst:(BOOL)useFirstStreamAvailable
inInit:(BOOL)isInitProcess
{
// NSLog(#"%s", __PRETTY_FUNCTION__); // RTSP
self.status = StreamProvisioningStatusStarting;
AVCodec *decoderCodec;
NSString *rtspURL = self.streamURL;
NSString *errorMessage = nil;
NSError *error = nil;
self.sessionContext = NULL;
self.sessionContext = avformat_alloc_context();
AVFormatContext *pFormatCtx = self.sessionContext;
if (!pFormatCtx)
{
// Create approp error.
return error;
}
// MUST be called before avformat_open_input().
av_dict_free(&_sessionOptions);
self.sessionOptions = 0;
if (self.usesTcp)
{
// "rtsp_transport" - Set RTSP transport protocols.
// Allowed are: udp_multicast, tcp, udp, http.
av_dict_set(&_sessionOptions, "rtsp_transport", "tcp", 0);
}
av_dict_set(&_sessionOptions, "rtsp_transport", "tcp", 0);
// Open an input stream and read the header with the demuxer options.
// WARNING: The stream must be closed with avformat_close_input()
if (avformat_open_input(&pFormatCtx, rtspURL.UTF8String, NULL, &_sessionOptions) != 0)
{
// WARNING: Note that a user-supplied AVFormatContext (pFormatCtx) will be freed on failure.
self.isInputStreamOpen = NO;
// Create approp error.
return error;
}
self.isInputStreamOpen = YES;
// user-supplied AVFormatContext pFormatCtx might have been modified.
self.sessionContext = pFormatCtx;
// Retrieve stream information.
if (avformat_find_stream_info(pFormatCtx,NULL) < 0)
{
// Create approp error.
return error;
}
// Find the first video stream
int streamCount = pFormatCtx->nb_streams;
if (streamCount == 0)
{
// Create approp error.
return error;
}
int noStreamsAvailable = pFormatCtx->streams == NULL;
if (noStreamsAvailable)
{
// Create approp error.
return error;
}
// Result. An Index can change, an identifier shouldn't.
self.selectedVideoStreamId = STREAM_NOT_FOUND;
self.selectedAudioStreamId = STREAM_NOT_FOUND;
// Fallback.
int firstVideoStreamIndex = STREAM_NOT_FOUND;
int firstAudioStreamIndex = STREAM_NOT_FOUND;
self.selectedVideoStreamIndex = STREAM_NOT_FOUND;
self.selectedAudioStreamIndex = STREAM_NOT_FOUND;
for (int i = 0; i < streamCount; i++)
{
// Looking for video streams.
AVStream *stream = pFormatCtx->streams[i];
if (!stream) { continue; }
AVCodecParameters *codecPar = stream->codecpar;
if (!codecPar) { continue; }
if (codecPar->codec_type==AVMEDIA_TYPE_VIDEO)
{
if (stream->id == videoStreamId)
{
self.selectedVideoStreamId = videoStreamId;
self.selectedVideoStreamIndex = i;
}
if (firstVideoStreamIndex == STREAM_NOT_FOUND)
{
firstVideoStreamIndex = i;
}
}
// Looking for audio streams.
if (codecPar->codec_type==AVMEDIA_TYPE_AUDIO)
{
if (stream->id == audioStreamId)
{
self.selectedAudioStreamId = audioStreamId;
self.selectedAudioStreamIndex = i;
}
if (firstAudioStreamIndex == STREAM_NOT_FOUND)
{
firstAudioStreamIndex = i;
}
}
}
// Use first video and audio stream available (if possible).
if (self.selectedVideoStreamIndex == STREAM_NOT_FOUND && useFirstStreamAvailable && firstVideoStreamIndex != STREAM_NOT_FOUND)
{
self.selectedVideoStreamIndex = firstVideoStreamIndex;
self.selectedVideoStreamId = pFormatCtx->streams[firstVideoStreamIndex]->id;
}
if (self.selectedAudioStreamIndex == STREAM_NOT_FOUND && useFirstStreamAvailable && firstAudioStreamIndex != STREAM_NOT_FOUND)
{
self.selectedAudioStreamIndex = firstAudioStreamIndex;
self.selectedAudioStreamId = pFormatCtx->streams[firstAudioStreamIndex]->id;
}
if (self.selectedVideoStreamIndex == STREAM_NOT_FOUND)
{
// Create approp error.
return error;
}
// See AVCodecID for codec listing.
// * Video codec setup:
// 1. Find the decoder for the video stream with the gived codec id.
AVStream *stream = pFormatCtx->streams[self.selectedVideoStreamIndex];
if (!stream)
{
// Create approp error.
return error;
}
AVCodecParameters *codecPar = stream->codecpar;
if (!codecPar)
{
// Create approp error.
return error;
}
decoderCodec = avcodec_find_decoder(codecPar->codec_id);
if (decoderCodec == NULL)
{
// Create approp error.
return error;
}
// Get a pointer to the codec context for the video stream.
// WARNING: The resulting AVCodecContext should be freed with avcodec_free_context().
// Replaced:
// self.videoCodecContext = pFormatCtx->streams[self.selectedVideoStreamIndex]->codec;
// With:
self.videoCodecContext = avcodec_alloc_context3(decoderCodec);
avcodec_parameters_to_context(self.videoCodecContext,
codecPar);
self.videoCodecContext->thread_count = 4;
NSString *description = [NSString stringWithUTF8String:decoderCodec->long_name];
// 2. Open codec.
if (avcodec_open2(self.videoCodecContext, decoderCodec, NULL) < 0)
{
// Create approp error.
return error;
}
// * Audio codec setup:
if (self.selectedAudioStreamIndex > -1)
{
[self setupAudioDecoder];
}
// Allocate a raw video frame data structure. Contains audio and video data.
self.rawFrameData = av_frame_alloc();
self.outputWidth = self.videoCodecContext->width;
self.outputHeight = self.videoCodecContext->height;
if (!isInitProcess)
{
// Triggering notifications in init process won't change UI since the object is created locally. All
// objects which need data access to this object will not be able to get it. Thats why we don't notifiy anyone about the changes.
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.rtspVideoStreamSelectionChanged
object:nil userInfo: self.selectedVideoStream];
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.rtspAudioStreamSelectionChanged
object:nil userInfo: self.selectedAudioStream];
}
return nil;
}
UPDATE 1
The initial architecture allowed using any given thread. Most of the below code would mostly run on the main thread. This solution was not appropriate since the opening of the stream input can take several seconds for which the main thread is blocked while waiting for a network response inside FFmpeg. To solve this issue I have implemented the following solution:
Creation and the initial setup are only allowed on the background_thread (see code snippet "1" below).
Changes are allowed on the current_thread(Any).
Termination is allowed on the current_thread(Any).
After removing main thread checks and dispatch_asyncs to background threads, leaking has stopped and I can't reproduce the issue anymore:
// Code that produces the issue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1 - Create and do initial setup.
// This block creates the issue.
[self.rtspObject = [[RTSPObject alloc] initWithURL: ... ];
[self.rtspObject openInputStreamWithVideoStreamId: ...
audioStreamId: ...
useFirst: ...
inInit: ...];
});
I still don't understand why Xcode's memory debugger says that this block is retained?
Any advice or idea is welcome.
If you use av_format_open_input to open a file, you must use avformat_close_input to free it. Using free_context will leak all io related allocations.

SFAuthorizationPluginView example NameAndPassword hangs after button is pressed

I'm using the patched version of NameAndPassword as is here: https://github.com/skycocker/NameAndPassword
When using this outside of the login window (e.g. system.login.screensaver or my own test right), with only the NameAndPassword plug-in, the window hangs for about 10 seconds after pressing Ok or cancel.
Adding some logs, I see that MechanismDestroy is getting called on the plugin and NameAndPassword is released but it never gets to dealloc in NameAndPassword. I also don't see PluginDestroy getting called. The SecurityAgent hangs around for an additional 10 seconds after MechanismDestroy is called.
I saw this related post: SecurityAgentPlugin not working anymore on Yosemite (SFAuthorizationPluginView)
But following the accepted answer doesn't work and according to the Authorization Plug-in Reference didDeactivate shouldn't need to be called (there are no calls to RequestInterrupt and MechanismDeactivate is never called). Calling SetResult should be sufficient.
I can hack it by getting the window from the view and closing it forcibly but there must be a way to get it to work as intended.
I found different hack - I got the reference count of the auth-mechanism instance and release all but the last one. Do this after the setResult call.
This solves the issue. It shouldn't be like that - but Apple doesn't give us much of a choice.
If my reply cant help someone, i have had the same problem and find a different way (not clean, it's even rather dirty) because i could not make the proposed solutions work.
I destroy the window if the identifiers are valid (I test them). Like that if the identifiers are not valid the window is still there.
- (void)buttonPressed:(SFButtonType)inButtonType
{
NSString *userNameString;
NSString *passwordString;
userNameString = mUserName;
passwordString = [mPPasswordSecureTextField stringValue];
// if the OK button was pressed, write the identity and credentials and allow authorization,
// otherwise, if the cancel button was pressed, cancel the authorization
if (inButtonType == SFButtonTypeOK)
{
const char *puserName = [userNameString UTF8String];
const char *ppassword = [passwordString UTF8String];
AuthorizationValue userNameValue = { strlen(puserName) + 1, (char*)puserName };
AuthorizationValue userPasswordValue = { strlen(ppassword) + 1, (char*)ppassword };
// add the username and password to the context values
[self callbacks]->SetContextValue([self engineRef], kAuthorizationEnvironmentUsername, 1, &userNameValue);
[self callbacks]->SetContextValue([self engineRef], kAuthorizationEnvironmentPassword, 1, &userPasswordValue);
// allow authorization
[self callbacks]->SetResult([self engineRef], kAuthorizationResultAllow);
// to know if we must close the window
// try to auth with the provided user and pswd
BOOL status = [self macosTestLogin: puserName with: ppassword];
if(status == YES)
{
// the user and pwd are good, we can close the window
NSView* v;
// if we are in sleep, screensaver and lock mode (don't work in loggin mode,
// but don't be sad loggin mode have a workaround in config authdb,
// the setting is shared true)
if (mUseIPView) {
v = mPasswordView;
NSWindow* w = [v window];
[w close];
}
}
// suggested workaround (don't work)
[self didDeactivate];
}
else if (inButtonType == SFButtonTypeCancel)
{
// cancel authorization
[self callbacks]->SetResult([self engineRef], kAuthorizationResultUserCanceled);
}
}
And the test function :
- (BOOL) macosTestLogin: (const char *)userName with: (const char *)password
{
// hack to know if we must close the window
// try to auth with the provided user and pswd
AuthorizationRef authorization = NULL;
AuthorizationItem items[2];
items[0].name = kAuthorizationEnvironmentPassword;
items[0].value = (char*) password;
items[0].valueLength = strlen(password);
items[0].flags = 0;
items[1].name = kAuthorizationEnvironmentUsername;
items[1].value = (char*) userName;
items[1].valueLength = strlen(userName);
items[1].flags = 0;
AuthorizationRights rights = {2, items};
AuthorizationEnvironment enviroment = {2, items};
// creates a new authorization reference and provides an option to authorize or preauthorize rights.
AuthorizationCreate(NULL, &enviroment, kAuthorizationFlagDefaults, &authorization);
AuthorizationFlags flag = kAuthorizationFlagDefaults| kAuthorizationFlagExtendRights;
OSStatus status = AuthorizationCopyRights(authorization, &rights, &enviroment, flag, NULL);
if(status == errAuthorizationSuccess)
{
return YES;
}
return NO;
}
This work for me, i do this only in 'screensaver' mode, on login the option shared = true is sufficient.

Change system time, time zone and auto checkbox in time and date settings programmatically

I'm trying to figure out how to edit the time and the 'Set Date and Time Automatically' check box programmatically. I've spent a while and cant find the solution.
I've tried looking at the NSUserDefault keys but don't see them.
NSLog(#"%#", [[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys]);
help appreciated. This is OSX (not iphone).
This is not a complete answer. I just code below will just change the system time. Note: changing the system time requires root permission. Running the code via Xcode IDE as is will fail. Running from Terminal using sudo command works.
//
// main.m
// TimeChange
//
// Created by ... on 4/13/15.
// Copyright (c) 2015 .... All rights reserved.
//
#import <Foundation/Foundation.h>
#import <sys/time.h>
#include <errno.h>
extern int errno;
void NSLogTime(const struct tm *restrict temp, suseconds_t microseconds)
{
char tmbuf[64], buf[64];
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", temp);
snprintf(buf, sizeof buf, "%s.%06d\n", tmbuf, microseconds);
NSLog(#" %#", [[NSString alloc] initWithUTF8String:buf]);
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
// Built from samples based on the URL listed below
// http://stackoverflow.com/questions/2408976/struct-timeval-to-printable-format
// http://www.linuxquestions.org/questions/programming-9/c-code-to-change-date-time-on-linux-707384/
// Do whatever you need to set the following variable
// In this example I am hard-coding it
int month = 2;
int day = 27;
int year = 2002;
NSLog(#"Getting current date/time...");
struct timeval currentTime;
int success = gettimeofday(&currentTime, 0); // should check for success
struct tm *localTime = localtime(&currentTime.tv_sec);
NSLogTime(localTime, currentTime.tv_usec);
if (localTime)
{
NSLog(#"...create new date/time structure...");
localTime->tm_mon = month - 1;
localTime->tm_mday = day;
localTime->tm_year = year - 1900;
const struct timeval tv = {mktime(localTime), 0};
success = settimeofday(&tv, 0);
// check if we are success
if (success == 0)
{
NSLog(#"...time was changed!");
// get the new time from the system and display it
struct timeval updatedTime;
gettimeofday(&updatedTime, 0); // should check for success
NSLogTime(localtime(&updatedTime.tv_sec), updatedTime.tv_usec);
}
else
{
// display the error message
NSLog(#"Error Setting Date: %s", strerror(errno));
}
}
}
return 0;
}
Below is screen shot of the coding running in the terminal.
Note: the one hour time difference in the output is because Daylight Saving Time (DST) was not in-effect back in Feb 22, 2002.
So I discovered that I could write an applescript that would execute bash script commands. Then I called the script with NSApplescript.
The cool thing is apple script has an elegant password dialog and it only needs to be handled once for everything. This is far nicer than making the terminal appear.
The downside was the process for calling the applescript with NSApplescript.
What should have been a simple process of passing 3 args to the script needed to be handled by about 50 lines of outdated NSAppleEvent code that didn't even work in Apples docs. Luckily, I found a post where someone knew the constants missing from the absent Carbon framework.
The Code:
// Caller responsible for well formed ip address.
+(BOOL)setDateAndTimePreferences:(NSString*)ipAddress setAutoNetworkTime:(BOOL)yNo withTimezone:(NSString*)timezone{
// Load the script from a resource by fetching its URL from within our bundle
// Note: if the script if stored in a nother file location, NSBundle may not be
// necessary. Make sure the path to the script is correct.
NSString* path = [[NSBundle mainBundle] pathForResource:#"date_time_pref" ofType:#"scpt"];
if (path != nil){
NSURL* url = [NSURL fileURLWithPath:path];
if (url != nil)
{
NSDictionary* errors = [NSDictionary dictionary];
NSAppleScript* appleScript =
[[NSAppleScript alloc] initWithContentsOfURL:url error:&errors];
if (appleScript != nil)
{
// Get the value of the setAutoNetwork checkbox.
NSString *chkBox = (yNo == YES)? #"on": #"off";
// Create the arg parameters
NSAppleEventDescriptor* firstParameter =
[NSAppleEventDescriptor descriptorWithString:ipAddress];
NSAppleEventDescriptor* secondParameter =
[NSAppleEventDescriptor descriptorWithString:chkBox];
NSAppleEventDescriptor* thirdParameter =
[NSAppleEventDescriptor descriptorWithString:timezone];
// Create and populate the list of parameters.
NSAppleEventDescriptor* parameters = [NSAppleEventDescriptor listDescriptor];
[parameters insertDescriptor:firstParameter atIndex:1];
[parameters insertDescriptor:secondParameter atIndex:2];
[parameters insertDescriptor:thirdParameter atIndex:3];
// Create the AppleEvent target
ProcessSerialNumber psn = {0, kCurrentProcess};
NSAppleEventDescriptor* target = [NSAppleEventDescriptor
descriptorWithDescriptorType:typeProcessSerialNumber
bytes:&psn length:sizeof(ProcessSerialNumber)];
// We need these constants from the Carbon OpenScripting
// framework, but we don't actually need Carbon.framework.
#define kASAppleScriptSuite 'ascr'
#define kASSubroutineEvent 'psbr'
#define keyASSubroutineName 'snam'
// Create an NSAppleEventDescriptor with the script's method name to call,
// this is used for the script statement: "on set_preferences(arg1,arg2arg3)"
// Note that the routine name must be in lower case.
NSAppleEventDescriptor* handler =
[NSAppleEventDescriptor descriptorWithString:
[#"set_preferences" lowercaseString]];
// Create the event for an AppleScript subroutine,
// set the method name and the list of parameters
NSAppleEventDescriptor* event =
[NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite
eventID:kASSubroutineEvent
targetDescriptor:target
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
[event setParamDescriptor:handler forKeyword:keyASSubroutineName];
[event setParamDescriptor:parameters forKeyword:keyDirectObject];
// call the event in AppleScript
if (![appleScript executeAppleEvent:event error:&errors])
{
// report any errors from 'errors'
NSLog(#"Errors %#",[errors description]);
}
[appleScript release];
}
else{
// report any errors from 'errors'
NSLog(#"Error: applescript is nil");
}
}else{
NSLog(#"Could not locate the time_date_preferences script");
return NO;
}
}else{
NSLog(#"Could not locate the time_date_preferences script");
return NO;
}
return YES;
}
The Script:
on set_preferences(ipaddress, chkbox, timezone)
global timezonelist
do shell script "/usr/sbin/systemsetup -setusingnetworktime " & chkbox password "passwordhere" with administrator privileges
do shell script "/usr/sbin/systemsetup -setnetworktimeserver " & ipaddress with administrator privileges
set timezonelist to (do shell script "/usr/sbin/systemsetup -listtimezones" with administrator privileges)
if timezonelist contains timezone then
do shell script "/usr/sbin/systemsetup -settimezone " & timezone with administrator privileges
else
display notification "Please open Date and Time Preferences and set your time zone manually." with title ("Invalid Time Zone")
delay 1
end if
end set_preferences

Adding audio to a video using Obj-C plugin and AVAssetWriterInput

I'm trying to take a video created using the iVidCap plugin and add audio to it. Basically the exact same thing as in this question: Writing video + generated audio to AVAssetWriterInput, audio stuttering. I've used the code from this post as a basis to try and modify the iVidCap.mm file myself, but the app always crashes in endRecordingSession.
I'm not sure how I need to modify endRecordingSession to accomodate for the audio (the original plugin just creates a video file). Here is the function:
- (int) endRecordingSession: (VideoDisposition) action {
NSLog(#"Start endRecordingSession");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Auto released pool");
NSString *filePath;
BOOL success = false;
[videoWriterInput markAsFinished];
NSLog(#"Mark video writer input as finished");
//[audioWriterInput markAsFinished];
// Wait for the video status to become known.
// Is this really doing anything?
int status = videoWriter.status;
while (status == AVAssetWriterStatusUnknown) {
NSLog(#"Waiting for video to complete...");
[NSThread sleepForTimeInterval:0.5f];
status = videoWriter.status;
}
NSLog(#"Video completed");
#synchronized(self) {
success = [videoWriter finishWriting];
NSLog(#"Success: %#", success);
if (!success) {
// We failed to successfully finalize the video file.
NSLog(#"finishWriting returned NO");
} else {
// The video file was successfully written to the Documents folder.
filePath = [[self getDocumentsFileURL:videoFileName] path];
if (action == Save_Video_To_Album) {
// Move the video to an accessible location on the device.
NSLog(#"Temporary video filePath=%#", filePath);
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(filePath)) {
NSLog(#"Video IS compatible. Adding it to photo album.");
UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, #selector(copyToPhotoAlbumCompleteFromVideo: didFinishSavingWithError: contextInfo:), nil);
} else {
NSLog(#"Video IS NOT compatible. Could not be added to the photo album.");
success = NO;
}
} else if (action == Discard_Video) {
NSLog(#"Video cancelled. Removing temporary video file: %#", filePath);
[self removeFile:filePath];
}
}
[self cleanupWriter];
}
isRecording = false;
[pool drain];
return success; }
Right now it crashes on [videoWriter finishWriting]. I tried adding [audioWriterInput markAsFinished], but then it crashes on that. I would contact the original poster since it seems like they got it working, but there doesn't seem to be a way to send private messages.
Does anyone have any suggestions on how I can get this to work or why it's crashing? I've tried my best to figure this out but I'm pretty new to Obj-C. I can post the rest of the code if needed (a lot of it is in the original post referenced earlier).
The issue might actually be in the writeAudioBuffer function.
If you copied the code from that post but didnlt change it then you will certainly have some problems.
You need to do something like this:
if ( ![self waitForAudioWriterReadiness]) {
NSLog(#"WARNING: writeAudioBuffer dropped frame after wait limit reached.");
return 0;
}
OSStatus status;
CMBlockBufferRef bbuf = NULL;
CMSampleBufferRef sbuf = NULL;
size_t buflen = n * nchans * sizeof(float);
CMBlockBufferRef tmp_bbuf = NULL;
status = CMBlockBufferCreateWithMemoryBlock(
kCFAllocatorDefault,
samples,
buflen,
kCFAllocatorDefault,
NULL,
0,
buflen,
0,
&tmp_bbuf);
if (status != noErr || !tmp_bbuf) {
NSLog(#"CMBlockBufferCreateWithMemoryBlock error");
return -1;
}
// Copy the buffer so that we get a copy of the samples in memory.
// CMBlockBufferCreateWithMemoryBlock does not actually copy the data!
//
status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, tmp_bbuf, kCFAllocatorDefault, NULL, 0, buflen, kCMBlockBufferAlwaysCopyDataFlag, &bbuf);
//CFRelease(tmp_bbuf); // causes abort?!
if (status != noErr) {
NSLog(#"CMBlockBufferCreateContiguous error");
//CFRelease(bbuf);
return -1;
}
CMTime timestamp = CMTimeMake(sample_position_, 44100);
status = CMAudioSampleBufferCreateWithPacketDescriptions(
kCFAllocatorDefault, bbuf, TRUE, 0, NULL, audio_fmt_desc_, 1, timestamp, NULL, &sbuf);
sample_position_ += n;
if (status != noErr) {
NSLog(#"CMSampleBufferCreate error");
return -1;
}
BOOL r = [audioWriterInput appendSampleBuffer:sbuf];
if (!r) {
NSLog(#"appendSampleBuffer error");
}
//CFRelease(bbuf); // crashes, don't know why.. Is there a leak here?
//CFRelease(sbuf);
return 0;
There are a few things to do with memory management that I am unsure on here.
Additionally be sure to use:
audioWriterInput.expectsMediaDataInRealTime = YES;

Recommended way to copy arbitrary files using Cocoa

I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.
Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.
Optional: Which command line tool provides help and could be utilized by a Cocoa application.
NSFileManager is your friend:
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:#"path/to/source" toPath:#"path/to/destination" error:&error])
{
// copy succeeded
}
else
{
// copy failed, print error
}
You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.
This sample shows how to copy and move both files and folders. It
shows both the synchronous and asynchronous (using CFRunLoop) use of
the FSFileOperation APIs. In addition, it shows path and FSRef
variants of the API and how to get status out of the callbacks. The
API is conceptually similar to the FSVolumeOperation APIs introduced
in Mac OS X 10.2.
Example of FSCopyObjectAsync:
#import <Cocoa/Cocoa.h>
#interface AsyncCopyController : NSObject {
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
//delegate method
-(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
-(void)didCopyOperationComplete : (BOOL)boolean;
-(void)didReceiveCopyError : (NSString *)Error;
-(void)cancelAllAsyncCopyOperation;
#end
#import "AsyncCopyController.h"
static Boolean copying= YES;
#implementation AsyncCopyController
static void statusCallback (FSFileOperationRef fileOp,
const FSRef *currentItem,
FSFileOperationStage stage,
OSStatus error,
CFDictionaryRef statusDictionary,
void *info )
{
NSLog(#"Callback got called. %ld", error);
id delegate;
if (info)
delegate = (id)info;
if (error!=0) {
if (error==-48) {
[delegate didReceiveCopyError:#"Duplicate filename and version or Destination file already exists or File found instead of folder"];
}
}
CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
NSString* currentPath = [(NSURL *)theURL path];
// NSLog(#"currentPath %#", currentPath);
// If the status dictionary is valid, we can grab the current values to
// display status changes, or in our case to update the progress indicator.
if (statusDictionary)
{
CFNumberRef bytesCompleted;
bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
kFSOperationBytesCompleteKey);
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberMaxType,
&floatBytesCompleted);
// NSLog(#"Copied %d bytes so far.",
// (unsigned long long)floatBytesCompleted);
if (info)
[delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
}
NSLog(#"stage %d", stage);
if (stage == kFSOperationStageComplete) {
NSLog(#"Finished copying the file");
if (info)
[delegate didCopyOperationComplete:YES];
// Would like to call a Cocoa Method here...
}
if (!copying) {
FSFileOperationCancel(fileOp);
}
}
-(void)cancelAllAsyncCopyOperation
{
copying = NO;
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
{
NSLog(#"copySource");
copying = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(#"%#", runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp, FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop, kCFRunLoopDefaultMode);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
return status;
}
require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
//return NO;
}
// Create a filesystem ref structure for the source and destination and
// populate them with their respective paths from our NSTextFields.
FSRef source;
FSRef destination;
// Used FSPathMakeRefWithOptions instead of FSPathMakeRef
// because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference
status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&source,
NULL);
require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
Boolean isDir = true;
status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&destination,
&isDir);
require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)
FSFileOperationClientContext clientContext;
// The FSFileOperation will copy the data from the passed in clientContext so using
// a stack based record that goes out of scope during the operation is fine.
if (object)
{
clientContext.version = 0;
clientContext.info = (void *) object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}
// Start the async copy.
status = FSCopyObjectAsync (fileOp,
&source,
&destination, // Full path to destination dir
NULL,// Use the same filename as source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object != NULL ? &clientContext : NULL);
//CFRelease(fileOp);
NSLog(#"Failed to begin asynchronous object copy: %d", status);
if (status) {
NSString * errMsg = [NSString stringWithFormat:#" - %#", status];
NSLog(#"Failed to begin asynchronous object copy: %d", status);
}
if (object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopFailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;
}
#end
FSCopyObjectAsync is Deprecated in OS X v10.8
copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.