gstreamer/subprojects/gst-examples/playback/player/ios/GstPlay/VideoViewController.m

207 lines
6.4 KiB
Objective-C

#import "VideoViewController.h"
#import <gst/player/player.h>
#import <UIKit/UIKit.h>
@interface VideoViewController () {
GstPlayer *player;
int media_width; /* Width of the clip */
int media_height; /* height ofthe clip */
Boolean dragging_slider; /* Whether the time slider is being dragged or not */
Boolean is_local_media; /* Whether this clip is stored locally or is being streamed */
Boolean is_playing_desired; /* Whether the user asked to go to PLAYING */
}
@end
@implementation VideoViewController
@synthesize uri;
/*
* Private methods
*/
/* The text widget acts as an slave for the seek bar, so it reflects what the seek bar shows, whether
* it is an actual pipeline position or the position the user is currently dragging to. */
- (void) updateTimeWidget
{
NSInteger position = time_slider.value / 1000;
NSInteger duration = time_slider.maximumValue / 1000;
NSString *position_txt = @" -- ";
NSString *duration_txt = @" -- ";
if (duration > 0) {
NSUInteger hours = duration / (60 * 60);
NSUInteger minutes = (duration / 60) % 60;
NSUInteger seconds = duration % 60;
duration_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
}
if (position > 0) {
NSUInteger hours = position / (60 * 60);
NSUInteger minutes = (position / 60) % 60;
NSUInteger seconds = position % 60;
position_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
}
NSString *text = [NSString stringWithFormat:@"%@ / %@",
position_txt, duration_txt];
time_label.text = text;
}
/*
* Methods from UIViewController
*/
- (void)viewDidLoad
{
[super viewDidLoad];
/* As soon as the GStreamer backend knows the real values, these ones will be replaced */
media_width = 320;
media_height = 240;
player = gst_player_new (gst_player_video_overlay_video_renderer_new ((__bridge gpointer)(video_view)), NULL);
g_object_set (player, "uri", [uri UTF8String], NULL);
gst_debug_set_threshold_for_name("gst-player", GST_LEVEL_TRACE);
g_signal_connect (player, "position-updated", G_CALLBACK (position_updated), (__bridge gpointer) self);
g_signal_connect (player, "duration-changed", G_CALLBACK (duration_changed), (__bridge gpointer) self);
g_signal_connect (player, "video-dimensions-changed", G_CALLBACK (video_dimensions_changed), (__bridge gpointer) self);
is_local_media = [uri hasPrefix:@"file://"];
is_playing_desired = NO;
time_slider.value = 0;
time_slider.minimumValue = 0;
time_slider.maximumValue = 0;
}
- (void)viewDidDisappear:(BOOL)animated
{
if (player)
{
gst_object_unref (player);
}
[UIApplication sharedApplication].idleTimerDisabled = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/* Called when the Play button is pressed */
-(IBAction) play:(id)sender
{
gst_player_play (player);
is_playing_desired = YES;
[UIApplication sharedApplication].idleTimerDisabled = YES;
}
/* Called when the Pause button is pressed */
-(IBAction) pause:(id)sender
{
gst_player_pause(player);
is_playing_desired = NO;
[UIApplication sharedApplication].idleTimerDisabled = NO;
}
/* Called when the time slider position has changed, either because the user dragged it or
* we programmatically changed its position. dragging_slider tells us which one happened */
- (IBAction)sliderValueChanged:(id)sender {
if (!dragging_slider) return;
// If this is a local file, allow scrub seeking, this is, seek as soon as the slider is moved.
if (is_local_media)
gst_player_seek (player, time_slider.value * 1000000);
[self updateTimeWidget];
}
/* Called when the user starts to drag the time slider */
- (IBAction)sliderTouchDown:(id)sender {
gst_player_pause (player);
dragging_slider = YES;
}
/* Called when the user stops dragging the time slider */
- (IBAction)sliderTouchUp:(id)sender {
dragging_slider = NO;
// If this is a remote file, scrub seeking is probably not going to work smoothly enough.
// Therefore, perform only the seek when the slider is released.
if (!is_local_media)
gst_player_seek (player, ((long)time_slider.value) * 1000000);
if (is_playing_desired)
gst_player_play (player);
}
/* Called when the size of the main view has changed, so we can
* resize the sub-views in ways not allowed by storyboarding. */
- (void)viewDidLayoutSubviews
{
CGFloat view_width = video_container_view.bounds.size.width;
CGFloat view_height = video_container_view.bounds.size.height;
CGFloat correct_height = view_width * media_height / media_width;
CGFloat correct_width = view_height * media_width / media_height;
if (correct_height < view_height) {
video_height_constraint.constant = correct_height;
video_width_constraint.constant = view_width;
} else {
video_width_constraint.constant = correct_width;
video_height_constraint.constant = view_height;
}
}
static void video_dimensions_changed (GstPlayer * unused, gint width, gint height, VideoViewController * self)
{
dispatch_async(dispatch_get_main_queue(), ^{
if (width > 0 && height > 0) {
[self videoDimensionsChanged:width height:height];
}
});
}
-(void) videoDimensionsChanged:(NSInteger)width height:(NSInteger)height
{
media_width = width;
media_height = height;
[self viewDidLayoutSubviews];
[video_view setNeedsLayout];
[video_view layoutIfNeeded];
}
static void position_updated (GstPlayer * unused, GstClockTime position, VideoViewController *self)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self positionUpdated:(int) (position / 1000000)];
});
}
-(void) positionUpdated:(NSInteger)position
{
/* Ignore messages from the pipeline if the time sliders is being dragged */
if (dragging_slider) return;
time_slider.value = position;
[self updateTimeWidget];
}
static void duration_changed (GstPlayer * unused, GstClockTime duration, VideoViewController *self)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self durationChanged:(int) (duration / 1000000)];
});
}
-(void) durationChanged:(NSInteger)duration
{
time_slider.maximumValue = duration;
[self updateTimeWidget];
}
@end