Move files from gst-examples into the "subprojects/gst-examples/" subdir
[platform/upstream/gstreamer.git] / subprojects / gst-examples / playback / player / ios / GstPlay / VideoViewController.m
1 #import "VideoViewController.h"
2 #import <gst/player/player.h>
3 #import <UIKit/UIKit.h>
4
5 @interface VideoViewController () {
6     GstPlayer *player;
7     int media_width;                /* Width of the clip */
8     int media_height;               /* height ofthe clip */
9     Boolean dragging_slider;        /* Whether the time slider is being dragged or not */
10     Boolean is_local_media;         /* Whether this clip is stored locally or is being streamed */
11     Boolean is_playing_desired;     /* Whether the user asked to go to PLAYING */
12 }
13
14 @end
15
16 @implementation VideoViewController
17
18 @synthesize uri;
19
20 /*
21  * Private methods
22  */
23
24 /* The text widget acts as an slave for the seek bar, so it reflects what the seek bar shows, whether
25  * it is an actual pipeline position or the position the user is currently dragging to. */
26 - (void) updateTimeWidget
27 {
28     NSInteger position = time_slider.value / 1000;
29     NSInteger duration = time_slider.maximumValue / 1000;
30     NSString *position_txt = @" -- ";
31     NSString *duration_txt = @" -- ";
32
33     if (duration > 0) {
34         NSUInteger hours = duration / (60 * 60);
35         NSUInteger minutes = (duration / 60) % 60;
36         NSUInteger seconds = duration % 60;
37
38         duration_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
39     }
40     if (position > 0) {
41         NSUInteger hours = position / (60 * 60);
42         NSUInteger minutes = (position / 60) % 60;
43         NSUInteger seconds = position % 60;
44
45         position_txt = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, minutes, seconds];
46     }
47
48     NSString *text = [NSString stringWithFormat:@"%@ / %@",
49                       position_txt, duration_txt];
50
51     time_label.text = text;
52 }
53
54 /*
55  * Methods from UIViewController
56  */
57
58 - (void)viewDidLoad
59 {
60     [super viewDidLoad];
61     
62     /* As soon as the GStreamer backend knows the real values, these ones will be replaced */
63     media_width = 320;
64     media_height = 240;
65
66     player = gst_player_new (gst_player_video_overlay_video_renderer_new ((__bridge gpointer)(video_view)), NULL);
67     g_object_set (player, "uri", [uri UTF8String], NULL);
68     
69     gst_debug_set_threshold_for_name("gst-player", GST_LEVEL_TRACE);
70     
71     g_signal_connect (player, "position-updated", G_CALLBACK (position_updated), (__bridge gpointer) self);
72     g_signal_connect (player, "duration-changed", G_CALLBACK (duration_changed), (__bridge gpointer) self);
73     g_signal_connect (player, "video-dimensions-changed", G_CALLBACK (video_dimensions_changed), (__bridge gpointer) self);
74     
75     is_local_media = [uri hasPrefix:@"file://"];
76     is_playing_desired = NO;
77
78     time_slider.value = 0;
79     time_slider.minimumValue = 0;
80     time_slider.maximumValue = 0;
81 }
82
83 - (void)viewDidDisappear:(BOOL)animated
84 {
85     if (player)
86     {
87         gst_object_unref (player);
88     }
89     [UIApplication sharedApplication].idleTimerDisabled = NO;
90 }
91
92 - (void)didReceiveMemoryWarning
93 {
94     [super didReceiveMemoryWarning];
95     // Dispose of any resources that can be recreated.
96 }
97
98 /* Called when the Play button is pressed */
99 -(IBAction) play:(id)sender
100 {
101     gst_player_play (player);
102     is_playing_desired = YES;
103     [UIApplication sharedApplication].idleTimerDisabled = YES;
104 }
105
106 /* Called when the Pause button is pressed */
107 -(IBAction) pause:(id)sender
108 {
109     gst_player_pause(player);
110     is_playing_desired = NO;
111     [UIApplication sharedApplication].idleTimerDisabled = NO;
112 }
113
114 /* Called when the time slider position has changed, either because the user dragged it or
115  * we programmatically changed its position. dragging_slider tells us which one happened */
116 - (IBAction)sliderValueChanged:(id)sender {
117     if (!dragging_slider) return;
118     // If this is a local file, allow scrub seeking, this is, seek as soon as the slider is moved.
119     if (is_local_media)
120         gst_player_seek (player, time_slider.value * 1000000);
121     [self updateTimeWidget];
122 }
123
124 /* Called when the user starts to drag the time slider */
125 - (IBAction)sliderTouchDown:(id)sender {
126     gst_player_pause (player);
127     dragging_slider = YES;
128 }
129
130 /* Called when the user stops dragging the time slider */
131 - (IBAction)sliderTouchUp:(id)sender {
132     dragging_slider = NO;
133     // If this is a remote file, scrub seeking is probably not going to work smoothly enough.
134     // Therefore, perform only the seek when the slider is released.
135     if (!is_local_media)
136         gst_player_seek (player, ((long)time_slider.value) * 1000000);
137     if (is_playing_desired)
138         gst_player_play (player);
139 }
140
141 /* Called when the size of the main view has changed, so we can
142  * resize the sub-views in ways not allowed by storyboarding. */
143 - (void)viewDidLayoutSubviews
144 {
145     CGFloat view_width = video_container_view.bounds.size.width;
146     CGFloat view_height = video_container_view.bounds.size.height;
147
148     CGFloat correct_height = view_width * media_height / media_width;
149     CGFloat correct_width = view_height * media_width / media_height;
150
151     if (correct_height < view_height) {
152         video_height_constraint.constant = correct_height;
153         video_width_constraint.constant = view_width;
154     } else {
155         video_width_constraint.constant = correct_width;
156         video_height_constraint.constant = view_height;
157     }
158 }
159
160 static void video_dimensions_changed (GstPlayer * unused, gint width, gint height, VideoViewController * self)
161 {
162     dispatch_async(dispatch_get_main_queue(), ^{
163         if (width > 0 && height > 0) {
164             [self videoDimensionsChanged:width height:height];
165         }
166     });
167 }
168
169 -(void) videoDimensionsChanged:(NSInteger)width height:(NSInteger)height
170 {
171     media_width = width;
172     media_height = height;
173     [self viewDidLayoutSubviews];
174     [video_view setNeedsLayout];
175     [video_view layoutIfNeeded];
176 }
177
178 static void position_updated (GstPlayer * unused, GstClockTime position, VideoViewController *self)
179 {
180     dispatch_async(dispatch_get_main_queue(), ^{
181         [self positionUpdated:(int) (position / 1000000)];
182     });
183 }
184
185 -(void) positionUpdated:(NSInteger)position
186 {
187     /* Ignore messages from the pipeline if the time sliders is being dragged */
188     if (dragging_slider) return;
189     
190     time_slider.value = position;
191     [self updateTimeWidget];
192 }
193
194 static void duration_changed (GstPlayer * unused, GstClockTime duration, VideoViewController *self)
195 {
196     dispatch_async(dispatch_get_main_queue(), ^{
197         [self durationChanged:(int) (duration / 1000000)];
198     });
199 }
200
201 -(void) durationChanged:(NSInteger)duration
202 {
203     time_slider.maximumValue = duration;
204     [self updateTimeWidget];
205 }
206
207 @end