-
-
Save nkallen/6e3f5cf4e174a9d4f9d915fa9ca7a1a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef pipeline_h | |
#define pipeline_h | |
#import <Foundation/Foundation.h> | |
#import <UIKit/UIKit.h> | |
NS_ASSUME_NONNULL_BEGIN | |
typedef NS_ENUM(NSInteger, State) { | |
playing, | |
paused, | |
null, | |
ready, | |
}; | |
@interface Context : NSObject | |
- (void)pushThreadDefault; | |
- (void)popThreadDefault; | |
@end | |
@interface Source : NSObject | |
- (void)setCallbackAsync; | |
- (void)attach:(Context *)context; | |
@end | |
@interface XObject : NSObject // FIXME | |
- (void)set:(NSString *)name to:(id)value; | |
@end | |
@interface Main: NSObject | |
- (id)initWithContext:(Context *)context; | |
- (void)run; | |
@end | |
// MARK: GStreamer | |
@interface Element : XObject | |
@property (readwrite) NSString *name; | |
- (BOOL)linkTo:(Element *)other; // FIXME error handling | |
- (BOOL)setState:(State)state; // FIXME error handling | |
@end | |
@interface StateChangeMessage: NSObject | |
@property (readonly) State oldState; | |
@property (readonly) State newState; | |
@property (readonly) State pendingState; | |
@end | |
@interface ErrorMessage: NSObject | |
@property (nonnull, readonly) NSError *error; | |
@property (nonnull, readonly) NSString *debugInfo; | |
@end | |
@interface Message : NSObject | |
@property (nonnull, readonly) Element *source; | |
- (nullable StateChangeMessage *)parseStateChanged; | |
- (nullable ErrorMessage *)parseError; | |
@end | |
@interface Bus : XObject | |
- (Source *)createWatch; | |
- (void)onMessage:(NSString *)name callback:(void (^)(Bus *bus, Message *message))callbackBlock; | |
@end | |
@interface Pipeline : Element | |
@property (readonly) Bus *bus; | |
- (void)add:(Element *)element; | |
- (nullable Element *)getByInterface; | |
@end | |
@interface ElementFactory : NSObject | |
+ (nullable Element *)make:(NSString *)name; | |
@end | |
@interface VideoOverlay: Element | |
- (void)setWindowHandle:(UIView *)windowHandle; | |
@end | |
@interface GStreamer: NSObject | |
+ (nullable Pipeline *)parseLaunch:(NSString *)command error:(NSError **)error; | |
@end | |
NS_ASSUME_NONNULL_END | |
#endif /* pipeline_h */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
protocol GStreamerDelegate { | |
func onError() | |
func onStateChange() | |
func onInitialized() | |
} | |
class GStreamerService { | |
let delegate: GStreamerDelegate | |
let videoView: UIView | |
var pipeline: Pipeline! | |
init(delegate: GStreamerDelegate, videoView: UIView) { | |
self.delegate = delegate | |
self.videoView = videoView | |
} | |
func play() { | |
pipeline.setState(.playing) | |
} | |
func pause() { | |
pipeline.setState(.paused) | |
} | |
private func onError() { | |
delegate.onError() | |
pipeline.setState(.null) | |
} | |
private func onStateChange(message: Message) { | |
if message.source == pipeline { | |
delegate.onStateChange() | |
} | |
} | |
func run() { | |
DispatchQueue.global().async { | |
let context = Context() | |
context.pushThreadDefault() | |
do { | |
try self.pipeline = GStreamer.parseLaunch("avfvideosrc device-index=1 ! video/x-raw,width=352,height=288 ! tee name=t t. ! queue ! autovideosink t. ! queue ! vtenc_h264 bitrate=800 max-keyframe-interval-duration=2000000000 max-keyframe-interval=60 allow-frame-reordering=false realtime=true quality=0.5 ! flvmux streamable=1 ! rtmpsink location='rtmp://ec2-54-152-176-149.compute-1.amazonaws.com:1935/live/iphone live=1'") | |
self.pipeline.setState(.ready) | |
let videoSink = self.pipeline.getByInterface()! | |
(videoSink as! VideoOverlay).setWindowHandle(self.videoView) | |
let bus = self.pipeline.bus | |
let source = bus.createWatch() | |
source.setCallbackAsync() | |
source.attach(context) | |
bus.onMessage("error") { (bus, message) in | |
print("error", message.parseError()!) | |
self.pipeline.setState(.null) | |
} | |
bus.onMessage("state-changed") { (bus, message) in | |
if message.source == self.pipeline { | |
print(message.source.name, message.parseStateChanged()!) | |
} | |
} | |
let mainLoop = Main(context: context) | |
self.delegate.onInitialized() | |
mainLoop.run() | |
context.popThreadDefault() | |
} catch let error { | |
print("error", error) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment