Game Development Community

Video playing in iOS 4.x

by Vyacheslav Klymenko · in Technical Issues · 09/11/2010 (10:42 am) · 1 replies

Hi there, he are some modifications I made in itgb 1.4 to make happens video-playing in iPhone iOS 4.x

Issues:
- I haven't tested on iOS 3.x, but should work (let me know if someone will do that ; )
- I've hardcoded landscape orientation
- video-controls turned off
- I've commented checks for "is media player allowed", so it is always allowed in games : )

hope it will be useful ; )


(Thanks to: Oleg ; )


iPhoneMoviePlayback.h:

//
//  iPhoneVideoPlayback.h
//  Torque2D for iPhone
//
//  Created by Sven Bergstrom on 2009/10/23.
//  Copyright 2009 Luma Arcade. All rights reserved.
//



//Some notes on playing the video : 
//	The filetypes are monitored internally, so that bad files are not attempted to be handed to the OS
//  The parameters : 
//		scalingMode { "none" , "aspectFit" , "aspectFill" , "fill" }; - Note : these are constants script side! 0, 1, 2, 3 respectively.
//		controlMode { "default" , "volumeOnly" , "hidden" }; - 0, 1, 2 respectively. Declare console globals as needed


//These correspond to the docs over on Apples site : 
//#ifdef TORQUE_ALLOW_MOVIEPLAYER

#include "platformiPhone/platformiPhone.h"
#import <UIKit/UIKit.h>


enum iPhoneMovieScalingMode
{
	iMSM_NONE,
	iMSM_ASPECTFIT,
	iMSM_ASPECTFILL,
	iMSM_FILL
};

enum iPhoneMovieControlMode
{
	iMCM_DEFAULT,
	iMCM_VOLUMEONLY,
	iMCM_HIDDEN
};


bool playiPhoneMovie(const char* fileName, iPhoneMovieScalingMode scalingMode, iPhoneMovieControlMode controlMode);
//#endif//TORQUE_ALLOW_MOVIEPLAYER

iPhoneMoviePlayback.mm:


//
//  iPhoneVideoPlayback.mm
//  Torque2D for iPhone
//
//  Created by Sven Bergstrom on 2009/10/23.
//  Copyright 2009 Luma Arcade. All rights reserved.
//
//#ifdef TORQUE_ALLOW_MOVIEPLAYER
//iPhone media stuff
#include "iPhoneMoviePlayback.h"
#import "MediaPlayer/MPMoviePlayerController.h"
#import "AudioToolbox/AudioServices.h"
#include "platformiPhone/iPhoneWindow.h"
#include "platformiPhone/iPhoneOGLVideo.h"

#include "platformiPhone/platformiPhone.h"

#include "audio/audio.h"

//Torque Stuff
#include "console/console.h"


///ObjC section below
//Declaring the interface here, so that i can use the header for inclusion in C++ code
@interface iPhoneMoviePlaybackDelegate : NSObject {
	
	MPMoviePlayerController*  movieController;
	
}



@property (nonatomic, retain) MPMoviePlayerController*  movieController;

-(NSURL *) urlForLocalFile:(NSString*)fileLocation withFileType:(NSString*)fileType;
-(BOOL) playAVideo:(NSString*)fileName withScalingMode:(MPMovieScalingMode)scalingMode withControlMode:(MPMovieControlMode)controlMode;

@end


@implementation iPhoneMoviePlaybackDelegate 

@synthesize movieController;

//Wanted to keep the ObjC stuff neat and not littered with C++, implemented the C++ inside the playVideo function instead (the one exposed to the engine).
-(NSURL *)urlForLocalFile:(NSString*)fileLocation withFileType:(NSString*)typeName
{
	NSURL* movieURL = nil;
	//Find the file location within our application bundle
	NSBundle *bundle = [NSBundle mainBundle];
	if (bundle) 
	{
		//Actually, we need to use inDirectory or else it only looks in the toplevel directories.
		//Cut the file name from the end of the string
		NSString* fnameOnly = [fileLocation lastPathComponent];
		NSString* pathOnly = [fileLocation stringByDeletingLastPathComponent];
		//Ask the bundle for the path to the file
		NSString *moviePath = [bundle pathForResource:fnameOnly ofType:typeName inDirectory:pathOnly];
		if (moviePath)
		{
			movieURL = [NSURL fileURLWithPath:moviePath];
		}
	}
    
	//Check if its nil when you use it, if there was some kind of error
    return movieURL;
}

//This is the last step of the video call, the C++ hands in the values we want here (scaling etc). NO EXTENSION on the filename.
-(BOOL) playAVideo:(NSString*)fileName withScalingMode:(MPMovieScalingMode)scalingMode withControlMode:(MPMovieControlMode)controlMode
{

	UIWindow* window = [UIApplication sharedApplication].keyWindow;
	
	NSLog(@"play movie");
	NSString* defaultFileType = @"m4v";
	NSURL* filePath = [self urlForLocalFile:fileName withFileType:defaultFileType];
	if(filePath == nil)
	{
		return NO;
	}
	
	movieController = [[MPMoviePlayerController alloc] initWithContentURL: filePath];
	[[movieController view] setFrame: [window bounds]];  // frame must match parent view
	[window addSubview:[movieController view]];

	movieController.view.center = CGPointMake(480.0 / 2.0, 320.0 / 2.0);
	movieController.view.transform = CGAffineTransformMakeRotation(mDegToRad(90.0f));
	movieController.view.frame = CGRectMake(0.0f, 0.0f, 320.0, 480.0);
	
	
	
//	CGAffineTransform transform = movieController.view.transform;
	
	// Rotate the view 90 degrees. 
//	transform = CGAffineTransformRotate(transform, (M_PI / 2.0));
//    transform = CGAffineTransformTranslate(transform,0,0);
//	movieController.view.transform = transform;
	
//	movieController.view.center = CGPointMake(480.0 / 2, 320.0 / 2);
	
//	movieController.view.frame = CGRectMake(160.0f, -240.0f, 480.0f, 320.0f);
	
//	[movieController setScalingMode:MPMovieScalingModeAspectFit];
	
	
	
	//for 3.2 devices and above
	if([movieController respondsToSelector:@selector(loadState)])
	{
		
		[movieController setControlStyle:MPMovieControlStyleNone];
		[movieController prepareToPlay];
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(moviePlayerLoadStateChanged:)
													 name:@"MPMoviePlayerLoadStateDidChangeNotification"
												   object:movieController];
	}
	else {
		[movieController setMovieControlMode:MPMovieControlModeHidden];
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(moviePreloadDidFinish:)
													 name:@"MPMoviePlayerContentPreloadDidFinishNotification"
												   object:movieController];
	}
	
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(moviePlaybackDidFinish:)
												 name:@"MPMoviePlayerPlaybackDidFinishNotification"
											   object:movieController];
	
	[movieController play]; 
	
	return true;
	
}


// For 3.1.x deices

- (void) moviePreloadDidFinish:(NSNotification*)notification
{
//	[[NSNotificationCenter defaultCenter] removeObserver:self name:@"MPMoviePlayerContentPreloadDidFinishNotification" object:movieController];
//	[movieController play];
}

//for 3.2 and 4.x devies
-(void) moviePlayerLoadStateChanged:(NSNotification*)notification
{
//	UIWindow* window = [UIApplication sharedApplication].keyWindow;

//	if([movieController loadState] != MPMovieLoadStateUnknown)
//	{
//		[[NSNotificationCenter defaultCenter] removeObserver:self name:@"MPMoviePlayerLoadStateDidChangeNotification" object:movieController];
//		[window addSubview:[movieController view]];

//	}
}

//Notification of when the video has done playing now, and we can move on.
- (void) moviePlaybackDidFinish:(NSNotification*)notification
{
	//Remove our observer
    [[NSNotificationCenter defaultCenter] 
	 removeObserver:self 
	 name:@"MPMoviePlayerPlaybackDidFinishNotification" 
	 object:movieController];
	
	if([movieController respondsToSelector:@selector(loadState)])
	{
		[[movieController view] removeFromSuperview];
	}
	
	[movieController stop];
	[movieController release];
	
	// Reactivate the current audio session
//	AudioSessionSetActive(YES);
//	Audio::OpenALInit();
	
	//We can dealloc this whole delegate once we are done as the playback was asynchronous, which releases the movieController
	[super dealloc];
}

- (void)dealloc
{
	//clean up
	[movieController release];
	[super dealloc];
}

@end



//C++ Section below
bool playMovie(const char* fileName, iPhoneMovieScalingMode scalingMode, iPhoneMovieControlMode controlMode)
{
	MPMovieScalingMode scale = MPMovieScalingModeFill;
	MPMovieControlMode controls = MPMovieControlModeHidden;
	
	if((fileName != NULL) && (strlen(fileName) > 0))
	{
		//For now we assume the m4v format, as that is how iTunes exports it.
		// todo : make this try the other supported extensions, and or load without one. - Sven
		//NSString* filePath = [[NSString alloc] initWithUTF8String:fileName];
		//		NSString* filePath = [[NSString alloc] initWithUTF8String:fileName];
		
		NSString* filePath = [[NSString alloc] initWithUTF8String:fileName];
	
		iPhoneMoviePlaybackDelegate* playMovieDelegate = [iPhoneMoviePlaybackDelegate alloc];
		
		BOOL success = [playMovieDelegate playAVideo:filePath withScalingMode:scale withControlMode:controls];
		if(success == NO)
		{
			Con::errorf("playiPhoneMovie : cannot find the file %s in the bundle", fileName);
		}
		
		[filePath release];
		
		//Continue with success
		return true;
	}
	
	return false;
}


//Make it accessible to scripts!
ConsoleFunction(playiPhoneMovie, bool, 4, 4, "playiPhoneMovie(%filename, %scaleMode, %controlMode) Returns True if the file was found, false otherwise")
{
	iPhoneMovieScalingMode scale = (iPhoneMovieScalingMode)dAtoi(argv[2]);
	iPhoneMovieControlMode controls = (iPhoneMovieControlMode)dAtoi(argv[3]);
	bool worked = playMovie(argv[1], scale, controls);
	return worked;
}

//#endif //TORQUE_ALLOW_MOVIEPLAYER

#1
02/14/2011 (1:14 am)
It appears that in iOS4, if you run a video, you then can no longer run startiPhoneAudioStream without causing a crash. Specifically, a divide by 0 error ends up occurring. It's getting pretty late and I gotta get some sleep, but so far I've tracked it down inside of SoundEngine.mm, to this line:

OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);

For those following along at home, that's line 749. What happens is that function will always return a '0' for maxPacketSize if you call startiPhoneAudioStream anytime after you have previously watched a video. If you step through the debugger a bit further, you'll eventually get to a line where it divides something against maxPacketSize, which is 0, causes a rift in space time, hence crashing the game. For those who have not looked at this code specifically before, if you run startiPhoneAudioStream without first running a video, then maxPacketSize is always returned with an appropriate, non-zero value.

I'm guessing that there is some kind of an important buffer that the Torque movie player code is not clearing out. I'll get back to this one in the morning, but figured I'd post here before I crash in hopes maybe someone else knows the answer to save me some extra debugging time.