Reputation: 125
This one has been doing my head in for months - So time to swallow my pride and reach out for a little help. At the moment this is being done in UIWebView as HTML5/JS controlled system. But UIWebview frankly sux and looking to make this last component native too.
I have a collection of videos and at specific timed points during the video, I am calling a page of instructions that relate to the timed period in the video. The video controls also act as a controller for the instructions pages. So whatever timed point is reached, the corresponding page is animated into place.
I've looked in many, many options, with the closest coming in with http video streaming and using timed metadata to initiate a view, but I am containing the videos locally on the device. And, as yet cannot find anything that looks like it will work. Seems simple enough in principle, but I'll be damned if I can find a decent solution...
Any ideas / pointers?
Here's the last attempt at going native with this before the remainder of my hair fell out - I think I may be seeing where I was heading in the wrong direction, but if you can spare a few moments, I'd really appreciate it!
OBJECTIVE is to have a shoutOut that lives below the video that contains a page of instructions. At x seconds, the content will be refreshed to correspond to that portion of the video and persist until the next shoutOut for fresh content. This I have managed to achieve successfully. Where I have been falling down (a lot) is when I scrub the video back to a previous section, the shoutOut content remains at the position from which I scrubbed and remains there permanently. Or as the code is below, simply doesn't re-apear as it is set to a timed visible duration.
Anyway, here's the code...
Header:
// START:import
#import <UIKit/UIKit.h>
// START_HIGHLIGHT
#import <MediaPlayer/MPMoviePlayerController.h>
#import "CommentView.h"
// END_HIGHLIGHT
// START:def
// START:wiring
@interface MoviePlayerViewController : UIViewController {
UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
MPMoviePlayerController *player;
// END_HIGHLIGHT
// START:wiring
UILabel *onScreenDisplayLabel;
UIScrollView *myScrollView;
NSMutableArray *keyframeTimes;
NSArray *shoutOutTexts;
NSArray *shoutOutTimes;
}
@property (nonatomic, retain) IBOutlet UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
@property (nonatomic, retain) MPMoviePlayerController *player;
// END_HIGHLIGHT
@property (nonatomic, retain) IBOutlet UILabel *onScreenDisplayLabel;
@property (nonatomic, retain) IBOutlet UIScrollView *myScrollView;
@property (nonatomic, retain) NSMutableArray *keyframeTimes;
// START_HIGHLIGHT
-(NSURL *)movieURL;
- (void)timerAction:(NSTimer*)theTimer;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification;
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer;
- (IBAction) getInfo:(id)sender;
- (void)removeView:(NSTimer*)theTimer;
// END_HIGHLIGHT
// START:wiring
@end
// END:def
// END:wiring
// END:import
Main:
@implementation MoviePlayerViewController
// START:synth
@synthesize player;
@synthesize viewForMovie;
@synthesize onScreenDisplayLabel;
@synthesize myScrollView;
@synthesize keyframeTimes;
// END:synth
// Implement loadView to create a view hierarchy programmatically, without using a nib.
// START:viewDidLoad
// START:viewDidLoad1
- (void)viewDidLoad {
[super viewDidLoad];
keyframeTimes = [[NSMutableArray alloc] init];
shoutOutTexts = [[NSArray
arrayWithObjects:@"This is a test\nLabel at 2 secs ",
@"This is a test\nLabel at 325 secs",
nil] retain];
shoutOutTimes = [[NSArray
arrayWithObjects:[[NSNumber alloc] initWithInt: 2],
[[NSNumber alloc] initWithInt: 325],
nil] retain];
self.player = [[MPMoviePlayerController alloc] init];
self.player.contentURL = [self movieURL];
// END:viewDidLoad1
self.player.view.frame = self.viewForMovie.bounds;
self.player.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.viewForMovie addSubview:player.view];
[self.player play];
// START_HIGHLIGHT
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
// END_HIGHLIGHT
// START:viewDidLoad1
[self.view addSubview:self.myScrollView];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(movieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:nil];
}
// END:viewDidLoad
// END:viewDidLoad1
// START:movieURL
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath =
[bundle
pathForResource:@"BigBuckBunny_640x360"
ofType:@"m4v"];
if (moviePath) {
return [NSURL fileURLWithPath:moviePath];
} else {
return nil;
}
}
// END:movieURL
int position = 0;
- (void)timerAction:(NSTimer*)theTimer {
NSLog(@"hi");
int count = [shoutOutTimes count];
NSLog(@"count is at %d", count);
if (position < count) {
NSNumber *timeObj = [shoutOutTimes objectAtIndex:position];
int time = [timeObj intValue];
NSLog(@"time is at %d", time);
if (self.player.currentPlaybackTime >= time) {
CommentView *cview = [[CommentView alloc]
initWithText:[shoutOutTexts objectAtIndex:position]];
[self.player.view addSubview:cview];
position++;
[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:@selector(removeView:) userInfo:cview repeats:NO];
}
}
}
- (void)removeView:(NSTimer*)theTimer {
UIView *view = [theTimer userInfo];
[view removeFromSuperview];
}
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
- (void) movieDurationAvailable:(NSNotification*)notification {
MPMoviePlayerController *moviePlayer = [notification object];
int duration = [moviePlayer duration];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(playerThumbnailImageRequestDidFinish:)
name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
object:nil];
NSMutableArray *times = [[NSMutableArray alloc] init];
for(int i = 0; i < 20; i++) {
[times addObject:[NSNumber numberWithInt:5+i*((duration)/20)]];
}
[self.player requestThumbnailImagesAtTimes:times timeOption: MPMovieTimeOptionNearestKeyFrame];
}
int p = 0;
int ll=0;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification {
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *timecode;
timecode = [userInfo objectForKey: @"MPMoviePlayerThumbnailTimeKey"];
[keyframeTimes addObject: timecode];
UIImage *image;
image = [userInfo objectForKey: @"MPMoviePlayerThumbnailImageKey"];
int width = image.size.width;
int height = image.size.height;
float newwidth = 75 * ((float)width / (float)height);
self.myScrollView.contentSize = CGSizeMake((newwidth + 2) * 20, 75);
UIImageView *imgv = [[UIImageView alloc] initWithImage:image];
[imgv setUserInteractionEnabled:YES];
[imgv setFrame:CGRectMake(ll, 0, newwidth, 75.0f)];
ll+=newwidth + 2;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(handleTapFrom:)];
[tapRecognizer setNumberOfTapsRequired:1];
[imgv addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
[myScrollView addSubview:imgv];
}
- (void) getInfo:(id)sender
{
MPMovieMediaTypeMask mask = self.player.movieMediaTypes;
NSMutableString *mediaTypes = [[NSMutableString alloc] init];
if (mask == MPMovieMediaTypeMaskNone) {
[mediaTypes appendString:@"Unknown Media Type"];
} else {
if (mask & MPMovieMediaTypeMaskAudio) {
[mediaTypes appendString:@"Audio"];
}
if (mask & MPMovieMediaTypeMaskVideo) {
[mediaTypes appendString:@"Video"];
}
}
MPMovieSourceType type = self.player.movieSourceType;
NSMutableString *sourceType = [[NSMutableString alloc] initWithString:@""];
if (type == MPMovieSourceTypeUnknown) {
[sourceType appendString:@"Source Unknown"];
} else if (type == MPMovieSourceTypeFile) {
[sourceType appendString:@"File"];
} else if (type == MPMovieSourceTypeStreaming) {
[sourceType appendString:@"Streaming"];
}
CGSize size = self.player.naturalSize;
onScreenDisplayLabel.text = [NSString stringWithFormat:@"[Type: %@] [Source: %@] [Time: %.1f of %.f secs] [Playback: %.0fx] [Size: %.0fx%.0f]",
mediaTypes,
sourceType,
self.player.currentPlaybackTime,
self.player.duration,
self.player.currentPlaybackRate,
size.width,
size.height];
}
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSArray *subviews = [myScrollView subviews];
for (int i = 0; i < 20; i++) {
if (recognizer.view == [subviews objectAtIndex:i]) {
NSNumber *num = [keyframeTimes objectAtIndex:i];
self.player.currentPlaybackTime = [num intValue];
return;
}
}
}
@end
The Comment View Header:
#import <UIKit/UIKit.h>
@interface CommentView : UIView {
}
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text;
- (id)initWithText:(NSString *) text;
@end
The Comment View Main:
#import "CommentView.h"
@implementation CommentView
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text {
if ((self = [super initWithFrame:frame])) {
UIImage *image = [UIImage imageNamed:@"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (id)initWithText:(NSString *) text {
if ((self = [super init])) {
UIImage *image = [UIImage imageNamed:@"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
@end
Thoughts anyone?
Cheers!
Upvotes: 0
Views: 1415
Reputation: 135578
What's wrong with monitoring currentPlaybackTime
at regular intervals (assuming you are using an instance that implements MPMediaPlayback
for playback).
Upvotes: 0