diff --git a/Headers/Public/VLCMedia.h b/Headers/Public/VLCMedia.h index 8cc033601104a00c65901e5c13526cdb388a0de9..aaefad44086d2ffb4906e03dc146b31ee844ecd2 100644 --- a/Headers/Public/VLCMedia.h +++ b/Headers/Public/VLCMedia.h @@ -160,6 +160,20 @@ typedef NS_ENUM(NSInteger, VLCMediaState) { */ - (instancetype)initWithPath:(NSString *)aPath; +/** + * Initializes a new VLCMedia object to use an input stream. + * + * \note By default, NSStream instances that are not file-based are non-seekable, + * you may subclass NSInputStream whose instances are capable of seeking through a stream. + * This subclass must allow setting NSStreamFileCurrentOffsetKey property. + * \note VLCMedia will open stream if it is not already opened, and will close eventually. + * You can't pass an already closed input stream. + * \note VLCHTTPInputStream instance can be passed in order to stream remote media. + * \param stream Input stream for media to be accessed. + * \return A new VLCMedia object, only if there were no errors. + */ +- (instancetype)initWithStream:(NSInputStream *)stream; + /** * TODO * \param aName TODO diff --git a/Sources/VLCMedia.m b/Sources/VLCMedia.m index e57243f0be005cc6cbd756201ce48e30f40edd03..9395d4e30e05cb5e3ebe3d42cc6503603d4e8563 100644 --- a/Sources/VLCMedia.m +++ b/Sources/VLCMedia.m @@ -64,6 +64,54 @@ NSString *const VLCMetaInformationDiscNumber = @"discNumber"; /* Notification Messages */ NSString *const VLCMediaMetaChanged = @"VLCMediaMetaChanged"; +/****************************************************************************** + * VLC callbacks for streaming. + */ +int open_cb(void *opaque, void **datap, uint64_t *sizep) { + NSInputStream *stream = (__bridge NSInputStream *)(opaque); + + // Once a stream is closed, it cannot be reopened. + if (stream && stream.streamStatus == NSStreamStatusNotOpen) { + [stream open]; + *datap = opaque; + *sizep = UINT64_MAX; + return 0; + } + return stream.streamStatus == NSStreamStatusOpen ? 0 : -1; +} + +ssize_t read_cb(void *opaque, unsigned char *buf, size_t len) { + NSInputStream *stream = (__bridge NSInputStream *)(opaque); + if (!stream) { + return -1; + } + + return [stream read:buf maxLength:len]; +} + +int seek_cb(void *opaque, uint64_t offset) { + NSInputStream *stream = (__bridge NSInputStream *)(opaque); + if (!stream) { + return -1; + } + + /* + By default, NSStream instances that are not file-based are non-seekable, one-way streams (although custom seekable subclasses are possible). + Once the data has been provided or consumed, the data cannot be retrieved from the stream. + + However, you may want a peer subclass to NSInputStream whose instances are capable of seeking through a stream. + */ + return [stream setProperty:@(offset) forKey:NSStreamFileCurrentOffsetKey] ? 0 : -1; +} + +void close_cb(void *opaque) { + NSInputStream *stream = (__bridge NSInputStream *)(opaque); + if (stream && stream.streamStatus != NSStreamStatusClosed && stream.streamStatus != NSStreamStatusNotOpen) { + [stream close]; + } + return; +} + /****************************************************************************** * VLCMedia () */ @@ -74,6 +122,7 @@ NSString *const VLCMediaMetaChanged = @"VLCMediaMetaChanged"; BOOL areOthersMetaFetched; ///< Value used to determine of the other meta has been parsed BOOL isArtURLFetched; ///< Value used to determine of the other meta has been preparsed NSMutableDictionary *_metaDictionary; ///< Dictionary to cache metadata read from libvlc + NSInputStream *stream; ///< Stream object if instance is initialized via NSInputStream to pass to callbacks } /* Make our properties internally readwrite */ @@ -237,6 +286,23 @@ static void HandleMediaParsedChanged(const libvlc_event_t * event, void * self) return self; } +- (instancetype)initWithStream:(NSInputStream *)stream +{ + if (self = [super init]) { + VLCLibrary *library = [VLCLibrary sharedLibrary]; + NSAssert(library.instance, @"no library instance when creating media"); + NSAssert(stream.streamStatus != NSStreamStatusClosed, @"Passing closed stream to VLCMedia.init does not work"); + + self->stream = stream; + p_md = libvlc_media_new_callbacks(library.instance, open_cb, read_cb, seek_cb, close_cb, (__bridge void *)(stream)); + + _metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3]; + + [self initInternalMediaDescriptor]; + } + return self; +} + - (instancetype)initAsNodeWithName:(NSString *)aName { if (self = [super init]) {