Skip to content

Instantly share code, notes, and snippets.

@nkallen

nkallen/gst.m Secret

Created January 30, 2017 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nkallen/67f5d19d1198d86e7f6b4ee6a8e53571 to your computer and use it in GitHub Desktop.
Save nkallen/67f5d19d1198d86e7f6b4ee6a8e53571 to your computer and use it in GitHub Desktop.
#import "pipeline.h"
#include <gst/gst.h>
#import <Foundation/Foundation.h>
#include <gst/video/video.h>
GstState state2GstState(State state) {
switch (state) {
case playing:
return GST_STATE_PLAYING;
break;
case paused:
return GST_STATE_PAUSED;
break;
case null:
return GST_STATE_NULL;
break;
case ready:
return GST_STATE_READY;
break;
default:
break;
}
}
State gstState2State(GstState state) {
switch (state) {
case GST_STATE_PLAYING:
return playing;
break;
case GST_STATE_PAUSED:
return paused;
break;
case GST_STATE_READY:
return ready;
break;
default:
return null;
break;
}
}
NSError *parseError(GError *err) {
NSDictionary *userInfo = @{@"message": [NSString stringWithUTF8String:err->message]};
NSError *error = [[NSError alloc] initWithDomain:[NSString stringWithUTF8String:g_quark_to_string(err->domain)] code:err->code userInfo:userInfo];
return error;
}
@interface XObject ()
@property (readonly) GObject *underlying;
@end
@implementation XObject // FIXME
@synthesize underlying = _underlying;
- (id)initWithUnderlying:(GObject *)underlying {
if ( self = [super init] ) {
if (underlying) {
_underlying = underlying;
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (void)set:(NSString *)name to:(id)value {
if ([value isKindOfClass:[NSString class]]) {
g_object_set(self.underlying, [name UTF8String], [value UTF8String], NULL);
} else if ([value isKindOfClass:[NSNumber class]]) {
g_object_set(self.underlying, [name UTF8String], [value intValue], NULL);
}
}
-(void)dealloc {
g_object_unref(_underlying);
}
@end
@interface Context ()
@property (readonly) GMainContext *underlying;
@end
@implementation Context
@synthesize underlying = _underlying;
- (id)initWithUnderlying:(GMainContext *)underlying {
if ( self = [super init] ) {
if (underlying) {
_underlying = underlying;
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (void)pushThreadDefault {
g_main_context_push_thread_default(self.underlying);
}
- (void)popThreadDefault {
g_main_context_pop_thread_default(self.underlying);
}
@end
@interface Source ()
@property (readonly) GSource *underlying;
@end
@implementation Source
@synthesize underlying = _underlying;
- (id)initWithUnderlying:(GSource *)underlying {
if ( self = [super init] ) {
if (underlying) {
_underlying = underlying;
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (void)setCallbackAsync {
g_source_set_callback(self.underlying, (GSourceFunc) gst_bus_async_signal_func, NULL, NULL);
}
- (void)attach:(Context *)context {
g_source_attach(self.underlying, context.underlying);
}
-(void)dealloc {
g_source_unref (self.underlying);
}
@end
@interface Main ()
@property (readonly) GMainLoop *underlying;
@end
@implementation Main
@synthesize underlying = _underlying;
- (id)initWithContext:(Context *)context {
if ( self = [super init] ) {
GMainLoop *underlying = g_main_loop_new (context.underlying, FALSE);
if (underlying) {
_underlying = underlying;
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (void)run {
g_main_loop_run(self.underlying);
}
- (void)dealloc {
g_main_loop_unref(self.underlying);
}
@end
// MARK: GStreamer
@interface Element ()
+ (Element *)wrap: (GstElement *)element;
@end
@interface Message ()
@property (readonly) GstMessage *underlying;
@end
@implementation StateChangeMessage
@synthesize oldState = _oldState;
@synthesize newState = _newState;
@synthesize pendingState = _pendingState;
- (id)initWithOldState:(State)oldState newState:(State)newState pendingState:(State)pendingState {
if ( self = [super init] ) {
_oldState = oldState;
_newState = newState;
_pendingState = pendingState;
return self;
} else {
return nil;
}
}
- (NSString *)description {
NSString *descriptionString = [NSString stringWithFormat:@"State Change: %s -> %s", gst_element_state_get_name(state2GstState(self.oldState)), gst_element_state_get_name(state2GstState(self.newState))];
return descriptionString;
}
@end
@implementation ErrorMessage
@synthesize error = _error;
@synthesize debugInfo = _debugInfo;
- (id)initWithError:(NSError *)error debugInfo:(NSString *)debugInfo {
if ( self = [super init] ) {
_error = error;
_debugInfo = debugInfo;
return self;
} else {
return nil;
}
}
@end
@implementation Message
- (id)initWithUnderlying:(GstMessage *)underlying {
if ( self = [super init] ) {
if (underlying) {
_underlying = underlying;
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (Element *)source {
GstObject *object = GST_MESSAGE_SRC(self.underlying);
gst_object_ref(object);
return [Element wrap: GST_ELEMENT(object)]; // FIXME bad cast
}
- (StateChangeMessage *)parseStateChanged {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(self.underlying, &old_state, &new_state, &pending_state);
return [[StateChangeMessage alloc] initWithOldState:gstState2State(old_state) newState:gstState2State(new_state) pendingState:gstState2State(pending_state)];
}
- (ErrorMessage *)parseError {
GError *err;
gchar *debug_info;
gst_message_parse_error(self.underlying, &err, &debug_info);
NSError *error = parseError(err);
g_clear_error (&err);
g_free (debug_info);
return [[ErrorMessage alloc] initWithError:error debugInfo:[NSString stringWithUTF8String:debug_info]];
}
@end
@implementation Bus
- (Source *)createWatch {
return [[Source alloc] initWithUnderlying: gst_bus_create_watch(GST_BUS(self.underlying))];
}
void CallbackAdapter(GstBus *bus, GstMessage *msg, void *data) {
void (^block)(Bus *bus, Message *message) = (__bridge void (^)(Bus *bus, Message *message))data;
gst_object_ref(bus);
block([[Bus alloc] initWithUnderlying:G_OBJECT(bus)], [[Message alloc] initWithUnderlying:msg]);
}
- (void)onMessage:(NSString *)name callback:(void (^)(Bus *bus, Message *message))callbackBlock {
NSString *preciseMessage = [NSString stringWithFormat:@"message::%@", name];
g_signal_connect (self.underlying, [preciseMessage UTF8String], (GCallback)CallbackAdapter, (__bridge_retained void *)callbackBlock);
}
@end
@implementation Element
+ (nonnull Element *)wrap: (nonnull GstElement *)element {
if (GST_IS_PIPELINE(element)) {
return [[Pipeline alloc] initWithUnderlying:G_OBJECT(element)];
} else if (GST_IS_VIDEO_OVERLAY(element)) {
return [[VideoOverlay alloc] initWithUnderlying:G_OBJECT(element)];
} else {
return [[Element alloc] initWithUnderlying:G_OBJECT(element)];
}
}
- (BOOL)linkTo:(Element *)other {
return gst_element_link(GST_ELEMENT(self.underlying), GST_ELEMENT(other.underlying));
}
- (BOOL)setState:(State)state {
return gst_element_set_state(GST_ELEMENT(self.underlying), state2GstState(state));
}
- (NSString *)name {
return [NSString stringWithUTF8String:gst_element_get_name(self.underlying)];
}
- (void) setName: (NSString *) name {
gst_element_set_name(self.underlying, [name UTF8String]);
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[Element class]]) {
return NO;
}
return self.underlying == ((Element *)object).underlying;
}
@end
@implementation Pipeline
- (id)init {
GObject *underlying = G_OBJECT(gst_pipeline_new(NULL));
if (underlying) {
if ([super initWithUnderlying: underlying]) {
return self;
} else {
return nil;
}
} else {
return nil;
}
}
- (void)add:(Element *)element {
gst_bin_add(GST_BIN(self.underlying), GST_ELEMENT(element.underlying));
}
- (Element *)getByInterface { // FIXME should take GType as arg
GType type = GST_TYPE_VIDEO_OVERLAY;
GstElement *element = gst_bin_get_by_interface(GST_BIN(self.underlying), type);
if (element == NULL) return NULL;
return [Element wrap:element];
}
- (Bus *)bus {
return [[Bus alloc] initWithUnderlying:G_OBJECT(gst_element_get_bus(GST_ELEMENT(self.underlying)))];
}
@end
@implementation ElementFactory
+ (Element *)make:(NSString *)name {
Element *element = [Element wrap:gst_element_factory_make([name UTF8String], NULL)];
return element;
}
@end
@implementation VideoOverlay
- (void)setWindowHandle:(UIView *)windowHandle {
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(self.underlying), (guintptr) (id) windowHandle);
}
@end
@implementation GStreamer
+ (nullable Pipeline *)parseLaunch:(NSString *)command error:(NSError **)error {
GError *err = NULL;
GstElement *pipeline = gst_parse_launch([command UTF8String], &err);
if (err) {
*error = parseError(err);
g_clear_error (&err);
return NULL;
}
return [[Pipeline alloc] initWithUnderlying:G_OBJECT(pipeline)];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment