Update
@@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application
|
||||
{
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application
|
||||
{
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application
|
||||
{
|
||||
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application
|
||||
{
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application
|
||||
{
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#include <OpenGLES/ES2/gl.h>
|
||||
#include <OpenGLES/ES2/glext.h>
|
||||
|
||||
@interface EaglUIView : UIView
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
#import "EaglUIVIew.h"
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@implementation EaglUIView
|
||||
|
||||
|
||||
+ (Class) layerClass
|
||||
{
|
||||
return [CAEAGLLayer class];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GStreamerBackendDelegate.h"
|
||||
|
||||
@interface GStreamerBackend : NSObject
|
||||
|
||||
/* Initialization method. Pass the delegate that will take care of the UI.
|
||||
* This delegate must implement the GStreamerBackendDelegate protocol.
|
||||
* Pass also the UIView object that will hold the video window. */
|
||||
-(id) init:(id) uiDelegate videoView:(UIView*) video_view;
|
||||
|
||||
/* Quit the main loop and free all resources, including the pipeline and
|
||||
* the references to the ui delegate and the UIView used for rendering, so
|
||||
* these objects can be deallocated. */
|
||||
-(void) deinit;
|
||||
|
||||
/* Set the pipeline to PLAYING */
|
||||
-(void) play;
|
||||
|
||||
/* Set the pipeline to PAUSED */
|
||||
-(void) pause;
|
||||
|
||||
/* Set the URI to be played */
|
||||
-(void) setUri:(NSString*)uri;
|
||||
|
||||
/* Set the position to seek to, in milliseconds */
|
||||
-(void) setPosition:(NSInteger)milliseconds;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,386 @@
|
||||
#import "GStreamerBackend.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (debug_category);
|
||||
#define GST_CAT_DEFAULT debug_category
|
||||
|
||||
/* Do not allow seeks to be performed closer than this distance. It is visually useless, and will probably
|
||||
* confuse some demuxers. */
|
||||
#define SEEK_MIN_DELAY (500 * GST_MSECOND)
|
||||
|
||||
@interface GStreamerBackend()
|
||||
-(void)setUIMessage:(gchar*) message;
|
||||
-(void)app_function;
|
||||
-(void)check_initialization_complete;
|
||||
@end
|
||||
|
||||
@implementation GStreamerBackend {
|
||||
id ui_delegate; /* Class that we use to interact with the user interface */
|
||||
GstElement *pipeline; /* The running pipeline */
|
||||
GstElement *video_sink; /* The video sink element which receives XOverlay commands */
|
||||
GMainContext *context; /* GLib context used to run the main loop */
|
||||
GMainLoop *main_loop; /* GLib main loop */
|
||||
gboolean initialized; /* To avoid informing the UI multiple times about the initialization */
|
||||
UIView *ui_video_view; /* UIView that holds the video */
|
||||
GstState state; /* Current pipeline state */
|
||||
GstState target_state; /* Desired pipeline state, to be set once buffering is complete */
|
||||
gint64 duration; /* Cached clip duration */
|
||||
gint64 desired_position; /* Position to seek to, once the pipeline is running */
|
||||
GstClockTime last_seek_time; /* For seeking overflow prevention (throttling) */
|
||||
gboolean is_live; /* Live streams do not use buffering */
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface methods
|
||||
*/
|
||||
|
||||
-(id) init:(id) uiDelegate videoView:(UIView *)video_view
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self->ui_delegate = uiDelegate;
|
||||
self->ui_video_view = video_view;
|
||||
self->duration = GST_CLOCK_TIME_NONE;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (debug_category, "tutorial-4", 0, "iOS tutorial 4");
|
||||
gst_debug_set_threshold_for_name("tutorial-4", GST_LEVEL_DEBUG);
|
||||
|
||||
/* Start the bus monitoring task */
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self app_function];
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) deinit
|
||||
{
|
||||
if (main_loop) {
|
||||
g_main_loop_quit(main_loop);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) play
|
||||
{
|
||||
target_state = GST_STATE_PLAYING;
|
||||
is_live = (gst_element_set_state (pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_NO_PREROLL);
|
||||
}
|
||||
|
||||
-(void) pause
|
||||
{
|
||||
target_state = GST_STATE_PAUSED;
|
||||
is_live = (gst_element_set_state (pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_NO_PREROLL);
|
||||
}
|
||||
|
||||
-(void) setUri:(NSString*)uri
|
||||
{
|
||||
const char *char_uri = [uri UTF8String];
|
||||
g_object_set(pipeline, "uri", char_uri, NULL);
|
||||
GST_DEBUG ("URI set to %s", char_uri);
|
||||
}
|
||||
|
||||
-(void) setPosition:(NSInteger)milliseconds
|
||||
{
|
||||
gint64 position = (gint64)(milliseconds * GST_MSECOND);
|
||||
if (state >= GST_STATE_PAUSED) {
|
||||
execute_seek(position, self);
|
||||
} else {
|
||||
GST_DEBUG ("Scheduling seek to %" GST_TIME_FORMAT " for later", GST_TIME_ARGS (position));
|
||||
self->desired_position = position;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private methods
|
||||
*/
|
||||
|
||||
/* Change the message on the UI through the UI delegate */
|
||||
-(void)setUIMessage:(gchar*) message
|
||||
{
|
||||
NSString *string = [NSString stringWithUTF8String:message];
|
||||
if(ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerSetUIMessage:)])
|
||||
{
|
||||
[ui_delegate gstreamerSetUIMessage:string];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the application what is the current position and clip duration */
|
||||
-(void) setCurrentUIPosition:(gint)pos duration:(gint)dur
|
||||
{
|
||||
if(ui_delegate && [ui_delegate respondsToSelector:@selector(setCurrentPosition:duration:)])
|
||||
{
|
||||
[ui_delegate setCurrentPosition:pos duration:dur];
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have pipeline and it is running, query the current position and clip duration and inform
|
||||
* the application */
|
||||
static gboolean refresh_ui (GStreamerBackend *self) {
|
||||
gint64 position;
|
||||
|
||||
/* We do not want to update anything unless we have a working pipeline in the PAUSED or PLAYING state */
|
||||
if (!self || !self->pipeline || self->state < GST_STATE_PAUSED)
|
||||
return TRUE;
|
||||
|
||||
/* If we didn't know it yet, query the stream duration */
|
||||
if (!GST_CLOCK_TIME_IS_VALID (self->duration)) {
|
||||
gst_element_query_duration (self->pipeline, GST_FORMAT_TIME, &self->duration);
|
||||
}
|
||||
|
||||
if (gst_element_query_position (self->pipeline, GST_FORMAT_TIME, &position)) {
|
||||
/* The UI expects these values in milliseconds, and GStreamer provides nanoseconds */
|
||||
[self setCurrentUIPosition:position / GST_MSECOND duration:self->duration / GST_MSECOND];
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Forward declaration for the delayed seek callback */
|
||||
static gboolean delayed_seek_cb (GStreamerBackend *self);
|
||||
|
||||
/* Perform seek, if we are not too close to the previous seek. Otherwise, schedule the seek for
|
||||
* some time in the future. */
|
||||
static void execute_seek (gint64 position, GStreamerBackend *self) {
|
||||
gint64 diff;
|
||||
|
||||
if (position == GST_CLOCK_TIME_NONE)
|
||||
return;
|
||||
|
||||
diff = gst_util_get_timestamp () - self->last_seek_time;
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (self->last_seek_time) && diff < SEEK_MIN_DELAY) {
|
||||
/* The previous seek was too close, delay this one */
|
||||
GSource *timeout_source;
|
||||
|
||||
if (self->desired_position == GST_CLOCK_TIME_NONE) {
|
||||
/* There was no previous seek scheduled. Setup a timer for some time in the future */
|
||||
timeout_source = g_timeout_source_new ((SEEK_MIN_DELAY - diff) / GST_MSECOND);
|
||||
g_source_set_callback (timeout_source, (GSourceFunc)delayed_seek_cb, (__bridge void *)self, NULL);
|
||||
g_source_attach (timeout_source, self->context);
|
||||
g_source_unref (timeout_source);
|
||||
}
|
||||
/* Update the desired seek position. If multiple requests are received before it is time
|
||||
* to perform a seek, only the last one is remembered. */
|
||||
self->desired_position = position;
|
||||
GST_DEBUG ("Throttling seek to %" GST_TIME_FORMAT ", will be in %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (position), GST_TIME_ARGS (SEEK_MIN_DELAY - diff));
|
||||
} else {
|
||||
/* Perform the seek now */
|
||||
GST_DEBUG ("Seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (position));
|
||||
self->last_seek_time = gst_util_get_timestamp ();
|
||||
gst_element_seek_simple (self->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, position);
|
||||
self->desired_position = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Delayed seek callback. This gets called by the timer setup in the above function. */
|
||||
static gboolean delayed_seek_cb (GStreamerBackend *self) {
|
||||
GST_DEBUG ("Doing delayed seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (self->desired_position));
|
||||
execute_seek (self->desired_position, self);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Retrieve errors from the bus and show them on the UI */
|
||||
static void error_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self)
|
||||
{
|
||||
GError *err;
|
||||
gchar *debug_info;
|
||||
gchar *message_string;
|
||||
|
||||
gst_message_parse_error (msg, &err, &debug_info);
|
||||
message_string = g_strdup_printf ("Error received from element %s: %s", GST_OBJECT_NAME (msg->src), err->message);
|
||||
g_clear_error (&err);
|
||||
g_free (debug_info);
|
||||
[self setUIMessage:message_string];
|
||||
g_free (message_string);
|
||||
gst_element_set_state (self->pipeline, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
/* Called when the End Of the Stream is reached. Just move to the beginning of the media and pause. */
|
||||
static void eos_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) {
|
||||
self->target_state = GST_STATE_PAUSED;
|
||||
self->is_live = (gst_element_set_state (self->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_NO_PREROLL);
|
||||
execute_seek (0, self);
|
||||
}
|
||||
|
||||
/* Called when the duration of the media changes. Just mark it as unknown, so we re-query it in the next UI refresh. */
|
||||
static void duration_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) {
|
||||
self->duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
/* Called when buffering messages are received. We inform the UI about the current buffering level and
|
||||
* keep the pipeline paused until 100% buffering is reached. At that point, set the desired state. */
|
||||
static void buffering_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) {
|
||||
gint percent;
|
||||
|
||||
if (self->is_live)
|
||||
return;
|
||||
|
||||
gst_message_parse_buffering (msg, &percent);
|
||||
if (percent < 100 && self->target_state >= GST_STATE_PAUSED) {
|
||||
gchar * message_string = g_strdup_printf ("Buffering %d%%", percent);
|
||||
gst_element_set_state (self->pipeline, GST_STATE_PAUSED);
|
||||
[self setUIMessage:message_string];
|
||||
g_free (message_string);
|
||||
} else if (self->target_state >= GST_STATE_PLAYING) {
|
||||
gst_element_set_state (self->pipeline, GST_STATE_PLAYING);
|
||||
} else if (self->target_state >= GST_STATE_PAUSED) {
|
||||
[self setUIMessage:"Buffering complete"];
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when the clock is lost */
|
||||
static void clock_lost_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self) {
|
||||
if (self->target_state >= GST_STATE_PLAYING) {
|
||||
gst_element_set_state (self->pipeline, GST_STATE_PAUSED);
|
||||
gst_element_set_state (self->pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
/* Retrieve the video sink's Caps and tell the application about the media size */
|
||||
static void check_media_size (GStreamerBackend *self) {
|
||||
GstElement *video_sink;
|
||||
GstPad *video_sink_pad;
|
||||
GstCaps *caps;
|
||||
GstVideoInfo info;
|
||||
|
||||
/* Retrieve the Caps at the entrance of the video sink */
|
||||
g_object_get (self->pipeline, "video-sink", &video_sink, NULL);
|
||||
|
||||
/* Do nothing if there is no video sink (this might be an audio-only clip */
|
||||
if (!video_sink) return;
|
||||
|
||||
video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
|
||||
caps = gst_pad_get_current_caps (video_sink_pad);
|
||||
|
||||
if (gst_video_info_from_caps (&info, caps)) {
|
||||
info.width = info.width * info.par_n / info.par_d;
|
||||
GST_DEBUG ("Media size is %dx%d, notifying application", info.width, info.height);
|
||||
|
||||
if (self->ui_delegate && [self->ui_delegate respondsToSelector:@selector(mediaSizeChanged:height:)])
|
||||
{
|
||||
[self->ui_delegate mediaSizeChanged:info.width height:info.height];
|
||||
}
|
||||
}
|
||||
|
||||
gst_caps_unref(caps);
|
||||
gst_object_unref (video_sink_pad);
|
||||
gst_object_unref(video_sink);
|
||||
}
|
||||
|
||||
/* Notify UI about pipeline state changes */
|
||||
static void state_changed_cb (GstBus *bus, GstMessage *msg, GStreamerBackend *self)
|
||||
{
|
||||
GstState old_state, new_state, pending_state;
|
||||
gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
|
||||
/* Only pay attention to messages coming from the pipeline, not its children */
|
||||
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->pipeline)) {
|
||||
self->state = new_state;
|
||||
gchar *message = g_strdup_printf("State changed to %s", gst_element_state_get_name(new_state));
|
||||
[self setUIMessage:message];
|
||||
g_free (message);
|
||||
|
||||
if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED)
|
||||
{
|
||||
check_media_size(self);
|
||||
|
||||
/* If there was a scheduled seek, perform it now that we have moved to the Paused state */
|
||||
if (GST_CLOCK_TIME_IS_VALID (self->desired_position))
|
||||
execute_seek (self->desired_position, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if all conditions are met to report GStreamer as initialized.
|
||||
* These conditions will change depending on the application */
|
||||
-(void) check_initialization_complete
|
||||
{
|
||||
if (!initialized && main_loop) {
|
||||
GST_DEBUG ("Initialization complete, notifying application.");
|
||||
if (ui_delegate && [ui_delegate respondsToSelector:@selector(gstreamerInitialized)])
|
||||
{
|
||||
[ui_delegate gstreamerInitialized];
|
||||
}
|
||||
initialized = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main method for the bus monitoring code */
|
||||
-(void) app_function
|
||||
{
|
||||
GstBus *bus;
|
||||
GSource *timeout_source;
|
||||
GSource *bus_source;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_DEBUG ("Creating pipeline");
|
||||
|
||||
/* Create our own GLib Main Context and make it the default one */
|
||||
context = g_main_context_new ();
|
||||
g_main_context_push_thread_default(context);
|
||||
|
||||
/* Build pipeline */
|
||||
pipeline = gst_parse_launch("playbin", &error);
|
||||
if (error) {
|
||||
gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
[self setUIMessage:message];
|
||||
g_free (message);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the pipeline to READY, so it can already accept a window handle */
|
||||
gst_element_set_state(pipeline, GST_STATE_READY);
|
||||
|
||||
video_sink = gst_bin_get_by_interface(GST_BIN(pipeline), GST_TYPE_VIDEO_OVERLAY);
|
||||
if (!video_sink) {
|
||||
GST_ERROR ("Could not retrieve video sink");
|
||||
return;
|
||||
}
|
||||
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(video_sink), (guintptr) (id) ui_video_view);
|
||||
|
||||
/* Instruct the bus to emit signals for each received message, and connect to the interesting signals */
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
bus_source = gst_bus_create_watch (bus);
|
||||
g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func, NULL, NULL);
|
||||
g_source_attach (bus_source, context);
|
||||
g_source_unref (bus_source);
|
||||
g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, (__bridge void *)self);
|
||||
g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, (__bridge void *)self);
|
||||
g_signal_connect (G_OBJECT (bus), "message::state-changed", (GCallback)state_changed_cb, (__bridge void *)self);
|
||||
g_signal_connect (G_OBJECT (bus), "message::duration", (GCallback)duration_cb, (__bridge void *)self);
|
||||
g_signal_connect (G_OBJECT (bus), "message::buffering", (GCallback)buffering_cb, (__bridge void *)self);
|
||||
g_signal_connect (G_OBJECT (bus), "message::clock-lost", (GCallback)clock_lost_cb, (__bridge void *)self);
|
||||
gst_object_unref (bus);
|
||||
|
||||
/* Register a function that GLib will call 4 times per second */
|
||||
timeout_source = g_timeout_source_new (250);
|
||||
g_source_set_callback (timeout_source, (GSourceFunc)refresh_ui, (__bridge void *)self, NULL);
|
||||
g_source_attach (timeout_source, context);
|
||||
g_source_unref (timeout_source);
|
||||
|
||||
/* Create a GLib Main Loop and set it to run */
|
||||
GST_DEBUG ("Entering main loop...");
|
||||
main_loop = g_main_loop_new (context, FALSE);
|
||||
[self check_initialization_complete];
|
||||
g_main_loop_run (main_loop);
|
||||
GST_DEBUG ("Exited main loop");
|
||||
g_main_loop_unref (main_loop);
|
||||
main_loop = NULL;
|
||||
|
||||
/* Free resources */
|
||||
g_main_context_pop_thread_default(context);
|
||||
g_main_context_unref (context);
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (pipeline);
|
||||
pipeline = NULL;
|
||||
|
||||
ui_delegate = NULL;
|
||||
ui_video_view = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol GStreamerBackendDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/* Called when the GStreamer backend has finished initializing
|
||||
* and is ready to accept orders. */
|
||||
-(void) gstreamerInitialized;
|
||||
|
||||
/* Called when the GStreamer backend wants to output some message
|
||||
* to the screen. */
|
||||
-(void) gstreamerSetUIMessage:(NSString *)message;
|
||||
|
||||
/* Called when the media size is first discovered or it changes */
|
||||
-(void) mediaSizeChanged:(NSInteger)width height:(NSInteger)height;
|
||||
|
||||
/* Called when the media position changes. Times in milliseconds */
|
||||
-(void) setCurrentPosition:(NSInteger)position duration:(NSInteger)duration;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "57x57",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "50x50",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "50x50",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-72.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "72x72",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-72@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-76.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Default-568h@2x.png",
|
||||
"extent" : "full-screen",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "to-status-bar",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "8.0",
|
||||
"subtype" : "736h",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "8.0",
|
||||
"subtype" : "736h",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "8.0",
|
||||
"subtype" : "667h",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "8.0",
|
||||
"subtype" : "1366h",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "retina4",
|
||||
"filename" : "Default-568h@2x-1.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict/>
|
||||
<key>CFBundleIcons~ipad</key>
|
||||
<dict/>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>MainStoryboard_iPhone</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>MainStoryboard_iPhone</string>
|
||||
<key>UIMainStoryboardFile~ipad</key>
|
||||
<string>MainStoryboard_iPad</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'Tutorial 4' target in the 'Tutorial 4' project
|
||||
//
|
||||
|
||||
#import <Availability.h>
|
||||
|
||||
#ifndef __IPHONE_5_0
|
||||
#warning "This project uses features only available in iOS SDK 5.0 and later."
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "GStreamerBackendDelegate.h"
|
||||
|
||||
@interface VideoViewController : UIViewController <GStreamerBackendDelegate> {
|
||||
IBOutlet UILabel *message_label;
|
||||
IBOutlet UIBarButtonItem *play_button;
|
||||
IBOutlet UIBarButtonItem *pause_button;
|
||||
IBOutlet UIView *video_view;
|
||||
IBOutlet UIView *video_container_view;
|
||||
IBOutlet NSLayoutConstraint *video_width_constraint;
|
||||
IBOutlet NSLayoutConstraint *video_height_constraint;
|
||||
IBOutlet UIToolbar *toolbar;
|
||||
IBOutlet UITextField *time_label;
|
||||
IBOutlet UISlider *time_slider;
|
||||
}
|
||||
|
||||
@property (retain,nonatomic) NSString *uri;
|
||||
|
||||
-(IBAction) play:(id)sender;
|
||||
-(IBAction) pause:(id)sender;
|
||||
-(IBAction) sliderValueChanged:(id)sender;
|
||||
-(IBAction) sliderTouchDown:(id)sender;
|
||||
-(IBAction) sliderTouchUp:(id)sender;
|
||||
|
||||
/* From GStreamerBackendDelegate */
|
||||
-(void) gstreamerInitialized;
|
||||
-(void) gstreamerSetUIMessage:(NSString *)message;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,198 @@
|
||||
#import "VideoViewController.h"
|
||||
#import "GStreamerBackend.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface VideoViewController () {
|
||||
GStreamerBackend *gst_backend;
|
||||
NSInteger media_width; /* Width of the clip */
|
||||
NSInteger media_height; /* height of the 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:@"%02lu:%02lu:%02lu", (unsigned long)hours, (unsigned long)minutes, (unsigned long)seconds];
|
||||
}
|
||||
if (position > 0) {
|
||||
NSUInteger hours = position / (60 * 60);
|
||||
NSUInteger minutes = (position / 60) % 60;
|
||||
NSUInteger seconds = position % 60;
|
||||
|
||||
position_txt = [NSString stringWithFormat:@"%02lu:%02lu:%02lu", (unsigned long)hours, (unsigned long)minutes, (unsigned long)seconds];
|
||||
}
|
||||
|
||||
NSString *text = [NSString stringWithFormat:@"%@ / %@",
|
||||
position_txt, duration_txt];
|
||||
|
||||
time_label.text = text;
|
||||
}
|
||||
|
||||
/*
|
||||
* Methods from UIViewController
|
||||
*/
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
play_button.enabled = FALSE;
|
||||
pause_button.enabled = FALSE;
|
||||
|
||||
/* As soon as the GStreamer backend knows the real values, these ones will be replaced */
|
||||
media_width = 320;
|
||||
media_height = 240;
|
||||
|
||||
uri = @"https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv";
|
||||
|
||||
gst_backend = [[GStreamerBackend alloc] init:self videoView:video_view];
|
||||
|
||||
time_slider.value = 0;
|
||||
time_slider.minimumValue = 0;
|
||||
time_slider.maximumValue = 0;
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
{
|
||||
if (gst_backend)
|
||||
{
|
||||
[gst_backend deinit];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
/* Called when the Play button is pressed */
|
||||
-(IBAction) play:(id)sender
|
||||
{
|
||||
[gst_backend play];
|
||||
is_playing_desired = YES;
|
||||
}
|
||||
|
||||
/* Called when the Pause button is pressed */
|
||||
-(IBAction) pause:(id)sender
|
||||
{
|
||||
[gst_backend pause];
|
||||
is_playing_desired = 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_backend setPosition:time_slider.value];
|
||||
[self updateTimeWidget];
|
||||
}
|
||||
|
||||
/* Called when the user starts to drag the time slider */
|
||||
- (IBAction)sliderTouchDown:(id)sender {
|
||||
[gst_backend pause];
|
||||
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_backend setPosition:time_slider.value];
|
||||
if (is_playing_desired)
|
||||
[gst_backend play];
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Methods from GstreamerBackendDelegate
|
||||
*/
|
||||
|
||||
-(void) gstreamerInitialized
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
play_button.enabled = TRUE;
|
||||
pause_button.enabled = TRUE;
|
||||
message_label.text = @"Ready";
|
||||
[gst_backend setUri:uri];
|
||||
is_local_media = [uri hasPrefix:@"file://"];
|
||||
is_playing_desired = NO;
|
||||
});
|
||||
}
|
||||
|
||||
-(void) gstreamerSetUIMessage:(NSString *)message
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
message_label.text = message;
|
||||
});
|
||||
}
|
||||
|
||||
-(void) mediaSizeChanged:(NSInteger)width height:(NSInteger)height
|
||||
{
|
||||
media_width = width;
|
||||
media_height = height;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self viewDidLayoutSubviews];
|
||||
[video_view setNeedsLayout];
|
||||
[video_view layoutIfNeeded];
|
||||
});
|
||||
}
|
||||
|
||||
-(void) setCurrentPosition:(NSInteger)position duration:(NSInteger)duration
|
||||
{
|
||||
/* Ignore messages from the pipeline if the time sliders is being dragged */
|
||||
if (dragging_slider) return;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
time_slider.maximumValue = duration;
|
||||
time_slider.value = position;
|
||||
[self updateTimeWidget];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="3084" systemVersion="11G63" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="z7O-8l-Zeo">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="2083"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--VideoViewController-->
|
||||
<scene sceneID="P93-nn-HBJ">
|
||||
<objects>
|
||||
<viewController title="Playback" id="z7O-8l-Zeo" userLabel="VideoViewController" customClass="VideoViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="CqS-Gu-I1O">
|
||||
<rect key="frame" x="0.0" y="20" width="768" height="1004"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xWd-bg-0b6" userLabel="VideoContainer">
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6tN-97-YoQ" userLabel="Video" customClass="EaglUIView">
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="240" type="user" id="A9A-eK-7QX"/>
|
||||
<constraint firstAttribute="width" constant="320" type="user" id="xcj-6M-2KJ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="6tN-97-YoQ" secondAttribute="centerY" type="user" id="UYa-oM-cdf"/>
|
||||
<constraint firstAttribute="centerX" secondItem="6tN-97-YoQ" secondAttribute="centerX" type="user" id="w9H-W0-MfF"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Initializing..." textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="728" translatesAutoresizingMaskIntoConstraints="NO" id="iLX-h1-Ko5" userLabel="Message">
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="50" type="user" id="7Z8-1b-Y41"/>
|
||||
<constraint firstAttribute="height" constant="21" type="user" id="jAS-RD-k2d"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MUi-CE-Ydy">
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" type="user" id="EwL-Ma-A4v"/>
|
||||
</constraints>
|
||||
<items>
|
||||
<barButtonItem systemItem="play" id="UlF-Ga-2VX">
|
||||
<connections>
|
||||
<action selector="play:" destination="z7O-8l-Zeo" id="5xC-uv-9lM"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem systemItem="pause" id="J3O-8j-Tkt">
|
||||
<connections>
|
||||
<action selector="pause:" destination="z7O-8l-Zeo" id="BYM-2X-Tel"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="s39-nx-e0L">
|
||||
<textField key="customView" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="00:00:00 / 00:00:00" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" id="G8q-Tu-Qx0" userLabel="Time">
|
||||
<rect key="frame" x="90" y="7" width="140" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="2n0-TO-8Ss">
|
||||
<slider key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="1" id="ufs-E5-87w" userLabel="Slider">
|
||||
<rect key="frame" x="240" y="11" width="118" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="sliderTouchDown:" destination="z7O-8l-Zeo" eventType="touchDown" id="yV6-eN-VUb"/>
|
||||
<action selector="sliderTouchUp:" destination="z7O-8l-Zeo" eventType="touchCancel" id="OyS-WZ-sEk"/>
|
||||
<action selector="sliderTouchUp:" destination="z7O-8l-Zeo" eventType="touchUpOutside" id="Vfz-se-pJg"/>
|
||||
<action selector="sliderTouchUp:" destination="z7O-8l-Zeo" eventType="touchUpInside" id="kZF-uW-GRo"/>
|
||||
<action selector="sliderValueChanged:" destination="z7O-8l-Zeo" eventType="valueChanged" id="cwm-pm-BfT"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</barButtonItem>
|
||||
</items>
|
||||
</toolbar>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="MUi-CE-Ydy" firstAttribute="trailing" secondItem="CqS-Gu-I1O" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="4vm-hF-9E8"/>
|
||||
<constraint firstItem="iLX-h1-Ko5" firstAttribute="bottom" secondItem="MUi-CE-Ydy" secondAttribute="top" constant="8" symbolic="YES" type="user" id="FXP-eH-VSK"/>
|
||||
<constraint firstItem="xWd-bg-0b6" firstAttribute="top" secondItem="CqS-Gu-I1O" secondAttribute="top" type="user" id="MXr-HK-07f"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xWd-bg-0b6" secondAttribute="trailing" type="user" id="NGT-7D-rHD"/>
|
||||
<constraint firstItem="MUi-CE-Ydy" firstAttribute="leading" secondItem="CqS-Gu-I1O" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="OM9-gE-OVP"/>
|
||||
<constraint firstItem="xWd-bg-0b6" firstAttribute="leading" secondItem="CqS-Gu-I1O" secondAttribute="leading" type="user" id="XO5-WJ-Y3R"/>
|
||||
<constraint firstItem="iLX-h1-Ko5" firstAttribute="top" secondItem="xWd-bg-0b6" secondAttribute="bottom" type="user" id="aP8-7M-Fq3"/>
|
||||
<constraint firstItem="iLX-h1-Ko5" firstAttribute="leading" secondItem="CqS-Gu-I1O" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="bT2-TE-X7Q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="iLX-h1-Ko5" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="ruI-5P-l7I"/>
|
||||
<constraint firstItem="MUi-CE-Ydy" firstAttribute="bottom" secondItem="CqS-Gu-I1O" secondAttribute="bottom" constant="20" symbolic="YES" type="user" id="w6K-xy-EJe"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="message_label" destination="iLX-h1-Ko5" id="Q0Y-3J-zis"/>
|
||||
<outlet property="pause_button" destination="J3O-8j-Tkt" id="Dls-sg-FPm"/>
|
||||
<outlet property="play_button" destination="UlF-Ga-2VX" id="243-yq-GEe"/>
|
||||
<outlet property="time_label" destination="G8q-Tu-Qx0" id="jpR-6u-zp0"/>
|
||||
<outlet property="time_slider" destination="ufs-E5-87w" id="ZUd-jO-4qN"/>
|
||||
<outlet property="toolbar" destination="MUi-CE-Ydy" id="Lme-0P-4Xq"/>
|
||||
<outlet property="video_container_view" destination="xWd-bg-0b6" id="7dL-Mp-QGc"/>
|
||||
<outlet property="video_height_constraint" destination="A9A-eK-7QX" id="rMe-ze-8l5"/>
|
||||
<outlet property="video_view" destination="6tN-97-YoQ" id="Q0n-dR-hqv"/>
|
||||
<outlet property="video_width_constraint" destination="xcj-6M-2KJ" id="oTt-9z-KCW"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iRS-GG-bR6" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="439" y="-199"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<classes>
|
||||
<class className="EaglUIView" superclassName="UIView">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/EaglUIView.h"/>
|
||||
</class>
|
||||
<class className="VideoViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/VideoViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="pause:"/>
|
||||
<relationship kind="action" name="play:"/>
|
||||
<relationship kind="action" name="sliderTouchDown:"/>
|
||||
<relationship kind="action" name="sliderTouchUp:"/>
|
||||
<relationship kind="action" name="sliderValueChanged:"/>
|
||||
<relationship kind="outlet" name="message_label" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="pause_button" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="outlet" name="play_button" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="outlet" name="time_label" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="time_slider" candidateClass="UISlider"/>
|
||||
<relationship kind="outlet" name="toolbar" candidateClass="UIToolbar"/>
|
||||
<relationship kind="outlet" name="video_container_view" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="video_height_constraint" candidateClass="NSLayoutConstraint"/>
|
||||
<relationship kind="outlet" name="video_view" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="video_width_constraint" candidateClass="NSLayoutConstraint"/>
|
||||
</relationships>
|
||||
</class>
|
||||
</classes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="blackTranslucent"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="2.0" toolsVersion="3084" systemVersion="11G63" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="iMo-Z9-PrL">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="2083"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--VideoViewController-->
|
||||
<scene sceneID="cnz-j4-g9W">
|
||||
<objects>
|
||||
<viewController title="Playback" id="iMo-Z9-PrL" userLabel="VideoViewController" customClass="VideoViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="zgN-eK-M4Q">
|
||||
<rect key="frame" x="0.0" y="20" width="320" height="548"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nA3-W2-kn7" userLabel="VideoContainer">
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WHW-sv-bWc" userLabel="Video" customClass="EaglUIView">
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="240" type="user" id="5z2-ux-czd"/>
|
||||
<constraint firstAttribute="width" constant="320" type="user" id="76i-Rz-yha"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="WHW-sv-bWc" secondAttribute="centerY" type="user" id="3wK-Zr-bQY"/>
|
||||
<constraint firstAttribute="centerX" secondItem="WHW-sv-bWc" secondAttribute="centerX" type="user" id="X5U-dh-PaZ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="Initializing..." textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="280" translatesAutoresizingMaskIntoConstraints="NO" id="LmB-wv-Ztp" userLabel="Message">
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="50" type="user" id="8aS-9o-dME"/>
|
||||
<constraint firstAttribute="height" constant="21" type="user" id="hUE-Fx-tsG"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A2w-65-QSF">
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" type="user" id="apr-w6-VYs"/>
|
||||
</constraints>
|
||||
<items>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Ixt-So-LpU"/>
|
||||
<barButtonItem systemItem="play" id="8Yb-MS-rAF">
|
||||
<connections>
|
||||
<action selector="play:" destination="iMo-Z9-PrL" id="9nM-or-ZY7"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem systemItem="pause" id="nH5-s3-C0i">
|
||||
<connections>
|
||||
<action selector="pause:" destination="iMo-Z9-PrL" id="mef-Ij-Agl"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" id="VUJ-y8-aWS">
|
||||
<textField key="customView" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="00:00:00 / 00:00:00" borderStyle="roundedRect" textAlignment="center" minimumFontSize="17" id="R6T-PH-VPd" userLabel="Time">
|
||||
<rect key="frame" x="132" y="7" width="139" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</barButtonItem>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="LSl-TA-0qV"/>
|
||||
</items>
|
||||
</toolbar>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="4Ns-t9-gs7" userLabel="Slider">
|
||||
<connections>
|
||||
<action selector="sliderTouchDown:" destination="iMo-Z9-PrL" eventType="touchDown" id="jGO-Pw-b5d"/>
|
||||
<action selector="sliderTouchUp:" destination="iMo-Z9-PrL" eventType="touchCancel" id="L1U-aK-FFn"/>
|
||||
<action selector="sliderTouchUp:" destination="iMo-Z9-PrL" eventType="touchUpInside" id="jRZ-vw-FQR"/>
|
||||
<action selector="sliderTouchUp:" destination="iMo-Z9-PrL" eventType="touchUpOutside" id="u1u-UD-WJw"/>
|
||||
<action selector="sliderValueChanged:" destination="iMo-Z9-PrL" eventType="valueChanged" id="ZOq-XS-0M1"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="LmB-wv-Ztp" secondAttribute="bottom" constant="66" id="9mR-hc-MeV"/>
|
||||
<constraint firstItem="A2w-65-QSF" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="Aki-Hx-2C9"/>
|
||||
<constraint firstItem="4Ns-t9-gs7" firstAttribute="trailing" secondItem="zgN-eK-M4Q" secondAttribute="trailing" type="default" id="HFi-uV-wdo"/>
|
||||
<constraint firstAttribute="bottom" secondItem="nA3-W2-kn7" secondAttribute="bottom" constant="87" id="Jdn-RD-NPI"/>
|
||||
<constraint firstItem="A2w-65-QSF" firstAttribute="trailing" secondItem="zgN-eK-M4Q" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="WWj-l6-D2k"/>
|
||||
<constraint firstAttribute="trailing" secondItem="LmB-wv-Ztp" secondAttribute="trailing" constant="20" symbolic="YES" type="user" id="ddw-6a-Ccz"/>
|
||||
<constraint firstItem="A2w-65-QSF" firstAttribute="bottom" secondItem="zgN-eK-M4Q" secondAttribute="bottom" type="default" id="e3s-lP-iPh"/>
|
||||
<constraint firstAttribute="trailing" secondItem="nA3-W2-kn7" secondAttribute="trailing" type="user" id="lOJ-ew-ZyI"/>
|
||||
<constraint firstItem="nA3-W2-kn7" firstAttribute="top" secondItem="zgN-eK-M4Q" secondAttribute="top" type="user" id="lUb-ik-h6u"/>
|
||||
<constraint firstItem="4Ns-t9-gs7" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" type="default" id="ma4-AV-cQs"/>
|
||||
<constraint firstItem="LmB-wv-Ztp" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" constant="20" symbolic="YES" type="user" id="nfT-8Y-Tvw"/>
|
||||
<constraint firstItem="nA3-W2-kn7" firstAttribute="leading" secondItem="zgN-eK-M4Q" secondAttribute="leading" type="user" id="p8G-QE-uZ8"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4Ns-t9-gs7" secondAttribute="bottom" constant="45" id="qfD-gf-vLB"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="message_label" destination="LmB-wv-Ztp" id="YqJ-GW-DBG"/>
|
||||
<outlet property="pause_button" destination="nH5-s3-C0i" id="VWV-EW-jB6"/>
|
||||
<outlet property="play_button" destination="8Yb-MS-rAF" id="5SI-l2-mAJ"/>
|
||||
<outlet property="time_label" destination="R6T-PH-VPd" id="bGs-Zr-rv3"/>
|
||||
<outlet property="time_slider" destination="4Ns-t9-gs7" id="9Ne-1N-clc"/>
|
||||
<outlet property="video_container_view" destination="nA3-W2-kn7" id="lEY-hP-YHD"/>
|
||||
<outlet property="video_height_constraint" destination="5z2-ux-czd" id="9R7-fg-G0e"/>
|
||||
<outlet property="video_view" destination="WHW-sv-bWc" id="c6W-td-a74"/>
|
||||
<outlet property="video_width_constraint" destination="76i-Rz-yha" id="vSP-Kk-v8g"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="YOs-uD-FMw" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-609" y="-199"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<classes>
|
||||
<class className="EaglUIView" superclassName="UIView">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/EaglUIView.h"/>
|
||||
</class>
|
||||
<class className="NSLayoutConstraint" superclassName="NSObject">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/NSLayoutConstraint.h"/>
|
||||
</class>
|
||||
<class className="VideoViewController" superclassName="UIViewController">
|
||||
<source key="sourceIdentifier" type="project" relativePath="./Classes/VideoViewController.h"/>
|
||||
<relationships>
|
||||
<relationship kind="action" name="pause:"/>
|
||||
<relationship kind="action" name="play:"/>
|
||||
<relationship kind="action" name="sliderTouchDown:"/>
|
||||
<relationship kind="action" name="sliderTouchUp:"/>
|
||||
<relationship kind="action" name="sliderValueChanged:"/>
|
||||
<relationship kind="outlet" name="message_label" candidateClass="UILabel"/>
|
||||
<relationship kind="outlet" name="pause_button" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="outlet" name="play_button" candidateClass="UIBarButtonItem"/>
|
||||
<relationship kind="outlet" name="time_label" candidateClass="UITextField"/>
|
||||
<relationship kind="outlet" name="time_slider" candidateClass="UISlider"/>
|
||||
<relationship kind="outlet" name="toolbar" candidateClass="UIToolbar"/>
|
||||
<relationship kind="outlet" name="video_container_view" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="video_height_constraint" candidateClass="NSLayoutConstraint"/>
|
||||
<relationship kind="outlet" name="video_view" candidateClass="UIView"/>
|
||||
<relationship kind="outlet" name="video_width_constraint" candidateClass="NSLayoutConstraint"/>
|
||||
</relationships>
|
||||
</class>
|
||||
</classes>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<!-- /etc/fonts/fonts.conf file to configure system font access -->
|
||||
<fontconfig>
|
||||
|
||||
<!-- Font directory list -->
|
||||
|
||||
<dir prefix="xdg"></dir>
|
||||
|
||||
<!-- Font cache directory list -->
|
||||
|
||||
<cachedir prefix="xdg"></cachedir>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'mono' alias, replacing it with 'monospace'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>mono</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>monospace</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!--
|
||||
Accept deprecated 'sans' alias, replacing it with 'sans-serif'
|
||||
-->
|
||||
<match target="pattern">
|
||||
<test qual="any" name="family">
|
||||
<string>sans</string>
|
||||
</test>
|
||||
<edit name="family" mode="assign" binding="same">
|
||||
<string>sans-serif</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<config>
|
||||
<!--
|
||||
These are the default Unicode chars that are expected to be blank
|
||||
in fonts. All other blank chars are assumed to be broken and
|
||||
won't appear in the resulting charsets
|
||||
-->
|
||||
<blank>
|
||||
<int>0x0020</int> <!-- SPACE -->
|
||||
<int>0x00A0</int> <!-- NO-BREAK SPACE -->
|
||||
<int>0x00AD</int> <!-- SOFT HYPHEN -->
|
||||
<int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
|
||||
<int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
|
||||
<int>0x0601</int> <!-- ARABIC SIGN SANAH -->
|
||||
<int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
|
||||
<int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
|
||||
<int>0x06DD</int> <!-- ARABIC END OF AYAH -->
|
||||
<int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
|
||||
<int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
|
||||
<int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
|
||||
<int>0x1680</int> <!-- OGHAM SPACE MARK -->
|
||||
<int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
|
||||
<int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
|
||||
<int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
|
||||
<int>0x2000</int> <!-- EN QUAD -->
|
||||
<int>0x2001</int> <!-- EM QUAD -->
|
||||
<int>0x2002</int> <!-- EN SPACE -->
|
||||
<int>0x2003</int> <!-- EM SPACE -->
|
||||
<int>0x2004</int> <!-- THREE-PER-EM SPACE -->
|
||||
<int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
|
||||
<int>0x2006</int> <!-- SIX-PER-EM SPACE -->
|
||||
<int>0x2007</int> <!-- FIGURE SPACE -->
|
||||
<int>0x2008</int> <!-- PUNCTUATION SPACE -->
|
||||
<int>0x2009</int> <!-- THIN SPACE -->
|
||||
<int>0x200A</int> <!-- HAIR SPACE -->
|
||||
<int>0x200B</int> <!-- ZERO WIDTH SPACE -->
|
||||
<int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
|
||||
<int>0x200D</int> <!-- ZERO WIDTH JOINER -->
|
||||
<int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
|
||||
<int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
|
||||
<int>0x2028</int> <!-- LINE SEPARATOR -->
|
||||
<int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
|
||||
<int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
|
||||
<int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
|
||||
<int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
|
||||
<int>0x202D</int> <!-- LEFT-TO-RIGHT OVERRIDE -->
|
||||
<int>0x202E</int> <!-- RIGHT-TO-LEFT OVERRIDE -->
|
||||
<int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
|
||||
<int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
|
||||
<int>0x2060</int> <!-- WORD JOINER -->
|
||||
<int>0x2061</int> <!-- FUNCTION APPLICATION -->
|
||||
<int>0x2062</int> <!-- INVISIBLE TIMES -->
|
||||
<int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
|
||||
<int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
|
||||
<int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
|
||||
<int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
|
||||
<int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
|
||||
<int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
|
||||
<int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
|
||||
<int>0x2800</int> <!-- BRAILLE PATTERN BLANK -->
|
||||
<int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
|
||||
<int>0x3164</int> <!-- HANGUL FILLER -->
|
||||
<int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
|
||||
<int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
|
||||
<int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
|
||||
<int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
|
||||
<int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
|
||||
</blank>
|
||||
<!--
|
||||
Rescan configuration every 30 seconds when FcFontSetList is called
|
||||
-->
|
||||
<rescan>
|
||||
<int>30</int>
|
||||
</rescan>
|
||||
</config>
|
||||
|
||||
</fontconfig>
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef __GST_IOS_INIT_H__
|
||||
#define __GST_IOS_INIT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_G_IO_MODULE_DECLARE(name) \
|
||||
extern void G_PASTE(g_io_, G_PASTE(name, _load)) (gpointer module)
|
||||
|
||||
#define GST_G_IO_MODULE_LOAD(name) \
|
||||
G_PASTE(g_io_, G_PASTE(name, _load)) (NULL)
|
||||
|
||||
/* Uncomment each line to enable the plugin categories that your application needs.
|
||||
* You can also enable individual plugins. See gst_ios_init.c to see their names
|
||||
*/
|
||||
|
||||
#define GST_IOS_PLUGINS_CORE
|
||||
#define GST_IOS_PLUGINS_CODECS
|
||||
//#define GST_IOS_PLUGINS_ENCODING
|
||||
#define GST_IOS_PLUGINS_NET
|
||||
#define GST_IOS_PLUGINS_PLAYBACK
|
||||
//#define GST_IOS_PLUGINS_VIS
|
||||
#define GST_IOS_PLUGINS_SYS
|
||||
//#define GST_IOS_PLUGINS_EFFECTS
|
||||
//#define GST_IOS_PLUGINS_CAPTURE
|
||||
#define GST_IOS_PLUGINS_CODECS_GPL
|
||||
#define GST_IOS_PLUGINS_CODECS_RESTRICTED
|
||||
#define GST_IOS_PLUGINS_NET_RESTRICTED
|
||||
//#define GST_IOS_PLUGINS_GES
|
||||
|
||||
|
||||
#define GST_IOS_GIO_MODULE_GNUTLS
|
||||
|
||||
void gst_ios_init (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#include "gst_ios_init.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
gst_ios_init();
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||