diff --git a/Video.js b/Video.js
index e430e41900..fd5e9568e7 100644
--- a/Video.js
+++ b/Video.js
@@ -81,6 +81,13 @@ export default class Video extends Component {
}
};
+ // ZEROLABS
+ _onLoadUpdate = (event) => {
+ if (this.props.onLoadUpdate) {
+ this.props.onLoadUpdate(event.nativeEvent);
+ }
+ };
+
_onLoad = (event) => {
if (this.props.onLoad) {
this.props.onLoad(event.nativeEvent);
@@ -209,6 +216,14 @@ export default class Video extends Component {
const isNetwork = !!(uri && uri.match(/^https?:/));
const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/));
+ // ZEROLABS - audioSource url
+ const audioSource = resolveAssetSource(this.props.audioSource) || {};
+ let audioUri = audioSource.uri || '';
+ if (audioUri && audioUri.match(/^\//)) {
+ audioUri = `file://${audioUri}`;
+ }
+ // ZEROLABS END
+
let nativeResizeMode;
if (resizeMode === VideoResizeMode.stretch) {
nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleToFill;
@@ -233,6 +248,22 @@ export default class Video extends Component {
patchVer: source.patchVer || 0,
requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}
},
+
+ // ZEROLABS BEGIN
+ audioSrc: {
+ uri: audioUri,
+ isNetwork,
+ isAsset,
+ type: audioSource.type || '',
+ mainVer: audioSource.mainVer || 0,
+ patchVer: audioSource.patchVer || 0,
+ requestHeaders: audioSource.headers
+ ? this.stringsOnlyObject(audioSource.headers)
+ : {},
+ },
+ onVideoLoadUpdate: this._onLoadUpdate,
+ // ZEROLABS END
+
onVideoLoadStart: this._onLoadStart,
onVideoLoad: this._onLoad,
onVideoError: this._onError,
@@ -281,6 +312,21 @@ Video.propTypes = {
PropTypes.number,
PropTypes.object
]),
+
+ // ZEROLABS BEGIN
+ audioSrc: PropTypes.object,
+ sendLoadUpdate: PropTypes.bool,
+ audioSource: PropTypes.oneOfType([
+ PropTypes.shape({
+ uri: PropTypes.string,
+ }),
+ PropTypes.number
+ ]),
+ onVideoLoadUpdate: PropTypes.func,
+ onLoadUpdate: PropTypes.func,
+ // ZEROLABS END
+
+
fullscreen: PropTypes.bool,
onVideoLoadStart: PropTypes.func,
onVideoLoad: PropTypes.func,
@@ -388,6 +434,8 @@ Video.propTypes = {
const RCTVideo = requireNativeComponent('RCTVideo', Video, {
nativeOnly: {
src: true,
+ // ZEROLABS BOOL 1-LINE
+ audioSrc: true,
seek: true,
fullscreen: true,
},
diff --git a/android-exoplayer/.project b/android-exoplayer/.project
new file mode 100644
index 0000000000..c81c2f8e63
--- /dev/null
+++ b/android-exoplayer/.project
@@ -0,0 +1,17 @@
+
+
+ android-exoplayer
+ Project android-exoplayer created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/android-exoplayer/.settings/org.eclipse.buildship.core.prefs b/android-exoplayer/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000..7a23d112fd
--- /dev/null
+++ b/android-exoplayer/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,13 @@
+arguments=
+auto.sync=false
+build.scans.enabled=false
+connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(5.6.1))
+connection.project.dir=
+eclipse.preferences.version=1
+gradle.user.home=
+java.home=
+jvm.arguments=
+offline.mode=false
+override.workspace.settings=true
+show.console.view=true
+show.executions.view=true
diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle
index 4546a5975a..810686fd99 100644
--- a/android-exoplayer/build.gradle
+++ b/android-exoplayer/build.gradle
@@ -18,7 +18,8 @@ android {
dependencies {
compileOnly "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
- implementation('com.google.android.exoplayer:exoplayer:2.9.0') {
+ // ZL: CHANGED TO VERSION 2.9.6
+ implementation('com.google.android.exoplayer:exoplayer:2.9.6') {
exclude group: 'com.android.support'
}
@@ -27,9 +28,11 @@ dependencies {
implementation "com.android.support:support-compat:${safeExtGet('supportLibVersion', '+')}"
implementation "com.android.support:support-media-compat:${safeExtGet('supportLibVersion', '+')}"
- implementation('com.google.android.exoplayer:extension-okhttp:2.9.0') {
+ // ZL: CHANGED TO VERSION 2.9.6
+ implementation('com.google.android.exoplayer:extension-okhttp:2.9.6') {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}
- implementation 'com.squareup.okhttp3:okhttp:3.11.0'
+ // ZL: CHANGED TO VERSION 3.14.0
+ implementation 'com.squareup.okhttp3:okhttp:3.14.0'
}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 3d141f3cd3..b0585e3389 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -115,6 +115,10 @@ class ReactExoplayerView extends FrameLayout implements
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
+ // ZEROLABS props
+ private Uri audioSrcUri;
+ private String audioExtension;
+
// Props from React
private Uri srcUri;
private String extension;
@@ -261,7 +265,20 @@ private void initializePlayer() {
MediaSource videoSource = buildMediaSource(srcUri, extension);
MediaSource mediaSource;
if (mediaSourceList.size() == 0) {
+
+ // ZEROLABS START
+ // Our merge-video-and-audio hack
+ if (audioSrcUri != null) {
+ // Gotta merge them both
+ MediaSource audioSource =
+ buildMediaSource(audioSrcUri, audioExtension);
+ MediaSource[] mergeCollection = {videoSource, audioSource};
+ mediaSource = new MergingMediaSource(mergeCollection);
+ } else {
+ // default behaviour
+ // ZEROLABS END
mediaSource = videoSource;
+ } // ZL
} else {
mediaSourceList.add(0, videoSource);
MediaSource[] textSourceArray = mediaSourceList.toArray(
@@ -700,6 +717,26 @@ public void onMetadata(Metadata metadata) {
// ReactExoplayerViewManager public api
+ // ZEROLABS method
+ public void setAudioSrc(final Uri uri, final String extension,
+ Map headers) {
+ if (uri != null) {
+ boolean isOriginalSourceNull = audioSrcUri == null;
+ boolean isSourceEqual = uri.equals(audioSrcUri);
+
+ this.audioSrcUri = uri;
+ this.audioExtension = extension;
+ // this.requestHeaders = headers;
+ // this.mediaDataSourceFactory =
+ // DataSourceUtil.getDefaultDataSourceFactory(
+ // this.themedReactContext, BANDWIDTH_METER, this.requestHeaders);
+
+ if (!isOriginalSourceNull && !isSourceEqual) {
+ reloadSource();
+ }
+ }
+ }
+
public void setSrc(final Uri uri, final String extension, Map headers) {
if (uri != null) {
boolean isOriginalSourceNull = srcUri == null;
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
index 4d1ec286ec..7521da32d6 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
+++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
@@ -1,5 +1,7 @@
package com.brentvatne.exoplayer;
+import android.util.Log; // ZEROLABS
+
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
@@ -21,6 +23,9 @@
public class ReactExoplayerViewManager extends ViewGroupManager {
+ // ZEROLABS
+ private static final String PROP_AUDIO_SRC = "audioSrc";
+
private static final String REACT_CLASS = "RCTVideo";
private static final String PROP_SRC = "src";
@@ -126,6 +131,35 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src
}
}
+ // ZEROLABS METHOD
+ @ReactProp(name = PROP_AUDIO_SRC)
+ public void setAudioSrc(final ReactExoplayerView videoView,
+ @Nullable ReadableMap src) {
+ Context context = videoView.getContext().getApplicationContext();
+ String uriString =
+ src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
+ String extension =
+ src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
+ Map headers =
+ src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS))
+ : null;
+
+ if (TextUtils.isEmpty(uriString)) {
+ return;
+ }
+
+ if (startsWithValidScheme(uriString)) {
+ Uri srcUri = Uri.parse(uriString);
+
+ if (srcUri != null) {
+ videoView.setAudioSrc(srcUri, extension, headers);
+ }
+ } else {
+ Log.e("ReactExoPlayerViewManager",
+ "Video audio - from asset not supported, go for it");
+ }
+ }
+
@ReactProp(name = PROP_RESIZE_MODE)
public void setResizeMode(final ReactExoplayerView videoView, final String resizeModeOrdinalString) {
videoView.setResizeModeModifier(convertToIntDef(resizeModeOrdinalString));
diff --git a/android/.project b/android/.project
new file mode 100644
index 0000000000..b6ece595ff
--- /dev/null
+++ b/android/.project
@@ -0,0 +1,17 @@
+
+
+ android__
+ Project android__ created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000..7a23d112fd
--- /dev/null
+++ b/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,13 @@
+arguments=
+auto.sync=false
+build.scans.enabled=false
+connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(5.6.1))
+connection.project.dir=
+eclipse.preferences.version=1
+gradle.user.home=
+java.home=
+jvm.arguments=
+offline.mode=false
+override.workspace.settings=true
+show.console.view=true
+show.executions.view=true
diff --git a/dom/RCTVideoManager.js b/dom/RCTVideoManager.js
index 2eb5309b98..617d0427af 100644
--- a/dom/RCTVideoManager.js
+++ b/dom/RCTVideoManager.js
@@ -32,6 +32,7 @@ class RCTVideoManager extends RCTViewManager {
.addDirectEvent("onVideoError")
.addDirectEvent("onVideoLoad")
.addDirectEvent("onVideoLoadStart")
+ .addDirectEvent("onVideoLoadUpdate") // ZEROLABS
.addDirectEvent("onVideoProgress");
}
diff --git a/examples/basic/android/.project b/examples/basic/android/.project
new file mode 100644
index 0000000000..3964dd3f5b
--- /dev/null
+++ b/examples/basic/android/.project
@@ -0,0 +1,17 @@
+
+
+ android
+ Project android created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/examples/basic/android/.settings/org.eclipse.buildship.core.prefs b/examples/basic/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000..e8895216fd
--- /dev/null
+++ b/examples/basic/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/examples/video-caching/android/.project b/examples/video-caching/android/.project
new file mode 100644
index 0000000000..0e0a1bac2d
--- /dev/null
+++ b/examples/video-caching/android/.project
@@ -0,0 +1,17 @@
+
+
+ android_
+ Project android_ created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/examples/video-caching/android/.settings/org.eclipse.buildship.core.prefs b/examples/video-caching/android/.settings/org.eclipse.buildship.core.prefs
new file mode 100644
index 0000000000..e8895216fd
--- /dev/null
+++ b/examples/video-caching/android/.settings/org.eclipse.buildship.core.prefs
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/ios/AssetChunk/ChunkAssetLoaderDelegate.h b/ios/AssetChunk/ChunkAssetLoaderDelegate.h
new file mode 100644
index 0000000000..3436d15d43
--- /dev/null
+++ b/ios/AssetChunk/ChunkAssetLoaderDelegate.h
@@ -0,0 +1,46 @@
+//
+// ChunkAssetLoaderDelegate.h
+// RCTVideo
+//
+// The delegate, and the defacto boss of the whole process
+//
+
+#ifndef ChunkAssetLoaderDelegate_h
+#define ChunkAssetLoaderDelegate_h
+
+#import
+#import
+
+@class RCTVideo;
+@class DataRequest;
+@class SingleChunk;
+@class HunkLoad;
+
+typedef enum : NSUInteger {
+ VIDEO,
+ AUDIO
+} CALGFormat;
+
+
+
+@interface ChunkAssetLoaderDelegate : NSObject
+
+@property (nonatomic,strong) NSURL *fileUrl;
+
+@property (nonatomic, strong) NSMutableArray *dataRequests;
+@property (nonatomic, strong) NSMutableArray *chunks;
+@property (nonatomic, strong) NSMutableArray *hunkLoads;
+@property (nonatomic, weak) RCTVideo* vidviewlink;
+
+@property (nonatomic) CALGFormat format;
+@property (nonatomic) long int totalSize;
+@property (nonatomic) long int highestChunkRequestedSoFar;
+
+
+-(id) initWithUrl:(NSURL *)url format:(CALGFormat)format vidview:(RCTVideo*) vidview;
+- (void)chunkFinishedLoading:(SingleChunk*)who fromHunkLoad:(HunkLoad*)hunk;
+- (void)startLoadingWantedChunks;
+
+@end
+
+#endif /* ChunkAssetLoaderDelegate_h */
diff --git a/ios/AssetChunk/ChunkAssetLoaderDelegate.m b/ios/AssetChunk/ChunkAssetLoaderDelegate.m
new file mode 100644
index 0000000000..b1e62de086
--- /dev/null
+++ b/ios/AssetChunk/ChunkAssetLoaderDelegate.m
@@ -0,0 +1,311 @@
+//
+// ChunkAssetLoaderDelegate.m
+// RCTVideo
+//
+
+#import
+
+#import "ChunkAssetLoaderDelegate.h"
+#import "DataRequest.h"
+#import "HunkLoad.h"
+#import "MP4Cacher.h"
+#import "RCTVideo.h"
+#import "SingleChunk.h"
+#import
+#import
+
+@implementation ChunkAssetLoaderDelegate
+
+
+- (id)initWithUrl:(NSURL *)url
+ format:(CALGFormat)format
+ vidview:(RCTVideo *)vidview {
+ /*
+ Initialise
+
+ Get started straight away by loading in chunk number zero;
+ */
+
+ if (self = [super init]) {
+ _chunks = [NSMutableArray array];
+ _hunkLoads = [NSMutableArray array];
+ _dataRequests = [NSMutableArray array];
+
+ _fileUrl = url;
+ _totalSize = -1;
+ _format = format;
+
+ _highestChunkRequestedSoFar = -1;
+
+ SingleChunk *chunk0 = [[SingleChunk alloc] initWithIndex:0];
+ [_chunks addObject:chunk0];
+ chunk0.state = WANTED;
+
+ _vidviewlink = vidview;
+
+ [self startLoadingWantedChunks];
+ }
+ return self;
+}
+
+//#pragma mark - AVURLAsset resource loading
+//
+//- (void)processPendingRequests{
+// // NSLog(@"FUDGE - four");
+//
+// NSLog(@"FUDGE processPendingRequests:%lu",(unsigned
+// long)self.pendingRequests.count); NSMutableArray *requestsCompleted =
+// [NSMutableArray array];
+//
+// for (AVAssetResourceLoadingRequest *loadingRequest in
+// self.pendingRequests){
+// [self
+// fillInContentInformation:loadingRequest.contentInformationRequest];
+//
+// BOOL didRespondCompletely = [self
+// respondWithDataForRequest:loadingRequest.dataRequest];
+//
+// if (didRespondCompletely){
+// [requestsCompleted addObject:loadingRequest];
+//
+// [loadingRequest finishLoading];
+// }
+// }
+//
+// [self.pendingRequests removeObjectsInArray:requestsCompleted];
+//}
+//
+//-
+//(void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest
+//*)contentInformationRequest{
+// // NSLog(@"FUDGE - five");
+//
+// if (contentInformationRequest == nil || self.response == nil){
+// return;
+// }
+//
+// NSString *mimeType = [self.response MIMEType];
+// CFStringRef contentType =
+// UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge
+// CFStringRef)(mimeType), NULL);
+//
+// contentInformationRequest.byteRangeAccessSupported = YES;
+// contentInformationRequest.contentType = CFBridgingRelease(contentType);
+// contentInformationRequest.contentLength = [self.response
+// expectedContentLength];
+//}
+//
+//- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest
+//*)dataRequest{
+// // NSLog(@"FUDGE - six");
+//
+// long long startOffset = dataRequest.requestedOffset;
+// if (dataRequest.currentOffset != 0){
+// startOffset = dataRequest.currentOffset;
+// }
+//
+// // Don't have any data at all for this request
+// if (self.movieData.length < startOffset){
+// return NO;
+// }
+//
+// // This is the total data we have from startOffset to whatever has been
+// downloaded so far NSUInteger unreadBytes = self.movieData.length -
+// (NSUInteger)startOffset;
+// // Respond with whatever is available if we can't satisfy the request
+// fully yet NSUInteger numberOfBytesToRespondWith =
+// MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
+//
+// NSLog(@"FUDGE data:%lu,,,(%lld,%lu)",(unsigned
+// long)self.movieData.length,startOffset,(unsigned
+// long)numberOfBytesToRespondWith); [dataRequest
+// respondWithData:[self.movieData
+// subdataWithRange:NSMakeRange((NSUInteger)startOffset,
+// numberOfBytesToRespondWith)]];
+//
+// long long endOffset = startOffset + dataRequest.requestedLength;
+// BOOL didRespondFully = self.movieData.length >= endOffset;
+//
+// return didRespondFully;
+//}
+
+- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
+ shouldWaitForLoadingOfRequestedResource:
+ (AVAssetResourceLoadingRequest *)loadingRequest {
+
+ /*
+ When an asset request comes in:
+
+ * Hold onto it
+ * Make sure we are requesting the chunks in its range
+ * Send it for processing in case we already have the data
+
+ */
+
+ // NSLog(@"FUDGEPACK - Request Made - %i
+ // %i",loadingRequest.dataRequest.requestedOffset,loadingRequest.dataRequest.requestedLength);
+
+ DataRequest *cDR = [[DataRequest alloc] initWithDR:loadingRequest owner:self];
+ [_dataRequests addObject:cDR];
+
+ // REQUEST SHIT IN RANGE
+ // TRIGGER REQUESTOR REFRESH
+ [self startLoadingWantedChunks];
+ [self chanceForDataRequestsToSendChunkData];
+
+ return YES;
+ // NEW SHIT
+ // ChunkLoader * loader = [[ChunkLoader alloc] init];
+ // [loader LoadChunk: [NSURL URLWithString:self.fileUrl]
+ // startAt:loadingRequest.dataRequest.requestedOffset
+ // loadBytes:loadingRequest.dataRequest.requestedLength
+ // resource:loadingRequest]; [self.chunks addObject:loader]; return YES;
+}
+
+- (void)startLoadingWantedChunks {
+ /*
+ Go thru all of our chunks and make sure that anything wanted is loading.
+ Create HunkLoads to do the work as necessary.
+ */
+ long int length = [_chunks count];
+ long int startChunk = -1;
+
+ for (long int n = 0; n < length; n++) {
+ SingleChunk *cur = _chunks[n];
+
+ if (cur.state == WANTED) {
+ // Start building our load request from this chunk
+ if (startChunk == -1) {
+ startChunk = n;
+ }
+
+ // Check the next to see if we should keep growing
+ SingleChunk *next = nil;
+ if (n < length - 1) {
+ next = _chunks[n + 1];
+ }
+ bool bKeepGrowing = false;
+
+ if (next) {
+ if (next.state == WANTED)
+ bKeepGrowing = true;
+ }
+
+ if (!bKeepGrowing) {
+ // We aren't growing so request this chunk range.
+
+ // Invoke the load
+ HunkLoad *Load =
+ [[HunkLoad alloc] initWithChunkRange:startChunk to:n ownedBy:self];
+ [_hunkLoads addObject:Load];
+
+ // Mark chunks as being loaded
+ for (long int m = startChunk; m <= n; m++) {
+ _chunks[m].state = LOADING;
+ }
+
+ // Reset our scanner
+ startChunk = -1;
+ }
+ }
+ }
+ // NSLog(@"HUNKY POST LOADING CHECK MAP:");
+ [self PrintChunkMap];
+}
+
+- (void)chunkFinishedLoading:(SingleChunk *)who fromHunkLoad:(HunkLoad *)hunk {
+ /*
+ A chunk has finished loading.
+
+ Special case for chunk 0:
+ * We now know the length and headers so:
+ * Allocate all the necessary chunks
+ * Get the header information out
+ * Also parse out the MP4 keyframe points which will get asked for.
+
+ For every chunk:
+ * Send to any assetRequests that may be asking for it
+
+ */
+ if ((who.index == 0) && [_chunks count] == 1) {
+ _totalSize = [hunk getTotalSizeFromHeaders];
+ long int finalChunk = ByteToContainingChunk(_totalSize);
+
+ for (long int n = 1; n <= finalChunk; n++) {
+ SingleChunk *chunk = [[SingleChunk alloc] initWithIndex:n];
+ [_chunks addObject:chunk];
+ }
+
+// if (_format == VIDEO) {
+ CacheMP4BasedOffChunk(self, who);
+ [self startLoadingWantedChunks];
+// }
+ }
+
+ // Allow DataRequests to have a crack at all chunks
+ [self chanceForDataRequestsToSendChunkData];
+
+ [self PrintChunkMap];
+}
+
+- (void)chanceForDataRequestsToSendChunkData {
+ NSMutableArray *dataRequestsCompleted = [NSMutableArray array];
+
+ for (DataRequest *r in _dataRequests) {
+ BOOL done = [r chanceToSendData:self];
+
+ if (done) {
+ [dataRequestsCompleted addObject:r];
+ }
+ }
+
+ [_dataRequests removeObjectsInArray:dataRequestsCompleted];
+}
+
+- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader
+ didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
+ NSLog(@"PANTS - cancel received - TODO");
+ // [self.pendingRequests removeObject:loadingRequest];
+}
+
+- (void)dealloc {
+ // Tell all http requests to DIE
+ NSLog(@"CALD PISS DEALLOC");
+ _vidviewlink = nil;
+ for (HunkLoad *h in _hunkLoads) {
+ [h cleanup];
+ }
+}
+
+- (void)PrintChunkMap {
+ if (_vidviewlink) {
+ if (_vidviewlink.sendLoadUpdate) {
+ NSMutableString *Map =
+ [[NSMutableString alloc] initWithCapacity:[_chunks count]];
+ for (long int n = 0; n < [_chunks count]; n++) {
+ SingleChunk *c = _chunks[n];
+ switch (c.state) {
+ case EMPTY:
+ [Map appendString:@"."];
+ break;
+ case WANTED:
+ [Map appendString:@"_"];
+ break;
+ case LOADING:
+ [Map appendString:@"="];
+ break;
+ case READY:
+ [Map appendString:@"X"];
+ break;
+ default:
+ [Map appendString:@"?"];
+ break;
+ }
+ }
+// NSLog(@"CHUNKY CHUNKMAP %@", Map);
+ [_vidviewlink performSendLoadUpdate:Map format:_format==VIDEO?@"VIDEO":@"AUDIO"];
+ }
+ }
+}
+
+@end
diff --git a/ios/AssetChunk/DataRequest.h b/ios/AssetChunk/DataRequest.h
new file mode 100644
index 0000000000..46d6e959b4
--- /dev/null
+++ b/ios/AssetChunk/DataRequest.h
@@ -0,0 +1,30 @@
+//
+// DataRequest.h
+// RCTVideo
+//
+// Represent a single asset delegate data request
+//
+
+#ifndef DataRequest_h
+#define DataRequest_h
+
+#import
+#import "ChunkAssetLoaderDelegate.h"
+
+@interface DataRequest : NSObject
+
+@property (nonatomic,strong) AVAssetResourceLoadingRequest *DR;
+
+@property (nonatomic) long int firstChunk;
+@property (nonatomic) long int lastChunk;
+
+@property (nonatomic) long int nextByteToSend;
+@property (nonatomic) long int bytesRemaining;
+@property (nonatomic) long int nextChunkToSendFrom;
+
+- (id) initWithDR:(AVAssetResourceLoadingRequest *) DR owner:(ChunkAssetLoaderDelegate*)owner;
+- (bool) chanceToSendData:(ChunkAssetLoaderDelegate*)owner;
+@end
+
+
+#endif /* DataRequest_h */
diff --git a/ios/AssetChunk/DataRequest.m b/ios/AssetChunk/DataRequest.m
new file mode 100644
index 0000000000..b2669c9777
--- /dev/null
+++ b/ios/AssetChunk/DataRequest.m
@@ -0,0 +1,138 @@
+//
+// DataRequest.m
+// RCTVideo
+//
+
+#import
+#import "DataRequest.h"
+#import "SingleChunk.h"
+
+@implementation DataRequest
+
+
+- (id) initWithDR:(AVAssetResourceLoadingRequest*) DR owner:(ChunkAssetLoaderDelegate*)owner{
+ if (self = [super init]) {
+ _DR = DR;
+
+ long int chunkCount = [owner.chunks count];
+
+ _nextByteToSend = DR.dataRequest.requestedOffset;
+ _bytesRemaining = DR.dataRequest.requestedLength;
+ _nextChunkToSendFrom = ByteToContainingChunk(_nextByteToSend);
+
+ _firstChunk = ByteToContainingChunk(_nextByteToSend);
+ _lastChunk = ByteToContainingChunk(StartAndLengthToLastByte(_nextByteToSend,_bytesRemaining));
+
+ if (_firstChunk >= owner.highestChunkRequestedSoFar) {
+// NSLog(@"ROGER: RISING %i %i",_firstChunk,owner.highestChunkRequestedSoFar);
+ owner.highestChunkRequestedSoFar = _firstChunk;
+ [self GoDemandingSomeChunksStartingFrom:_firstChunk EndingAt:_lastChunk ChunkCount:chunkCount Owner:owner];
+ [owner startLoadingWantedChunks];
+ } else {
+
+ // Make sure we have the basics
+ SingleChunk * fosty = owner.chunks[_firstChunk];
+ if (_lastChunk - _firstChunk < 4) {
+ _lastChunk = _firstChunk + 4;
+ if (_lastChunk >= chunkCount) _lastChunk = chunkCount-1;
+ }
+ [self GoDemandingSomeChunksStartingFrom:_firstChunk EndingAt:_lastChunk ChunkCount:chunkCount Owner:owner];
+ [owner startLoadingWantedChunks];
+
+ // Proactive reading ahead
+#define BIG_BLOCK_SIZE 8
+ long int bigBlock;
+ long int bigBlockStart;
+ long int bigBlockEnd;
+
+ bigBlock =_lastChunk / BIG_BLOCK_SIZE;
+ bigBlockStart = bigBlock * BIG_BLOCK_SIZE;
+ bigBlockEnd = bigBlockStart + BIG_BLOCK_SIZE - 1;
+ if (bigBlockEnd >= chunkCount) bigBlockEnd = chunkCount-1;
+
+ [self GoDemandingSomeChunksStartingFrom:bigBlockStart EndingAt:bigBlockEnd ChunkCount:chunkCount Owner:owner];
+ [owner startLoadingWantedChunks];
+
+ bigBlock = bigBlock + 1;
+ bigBlockStart = bigBlock * BIG_BLOCK_SIZE;
+ bigBlockEnd = bigBlockStart + BIG_BLOCK_SIZE - 1;
+ if (bigBlockStart < chunkCount) {
+ if (bigBlockEnd >= chunkCount) bigBlockEnd = chunkCount-1;
+
+ [self GoDemandingSomeChunksStartingFrom:bigBlockStart EndingAt:bigBlockEnd ChunkCount:chunkCount Owner:owner];
+ [owner startLoadingWantedChunks];
+ }
+
+ }
+ }
+ return self;
+}
+
+- (void) GoDemandingSomeChunksStartingFrom:(long int) firstChunk EndingAt:(long int) lastChunk ChunkCount:(long int)chunkCount Owner:(ChunkAssetLoaderDelegate*)owner{
+ for (long int n = firstChunk; n <= lastChunk; n++) {
+ if (n < chunkCount) {
+ SingleChunk * c = owner.chunks[n];
+ if (c.state == EMPTY) c.state = WANTED;
+ } else {
+ NSLog(@"ERROR ASSETCHUNK - DataRequest is asking for chunks we havent allocated - want = %li max = %li",n,chunkCount);
+ }
+ }
+}
+
+- (bool) chanceToSendData:(ChunkAssetLoaderDelegate*)owner{
+ long int chunkCount = [owner.chunks count];
+
+ while (_bytesRemaining > 0) {
+ // Check if chunk is ready
+ if (chunkCount <= _nextChunkToSendFrom) {
+ NSLog(@"ERROR ASSETCHUNK - DataRequest wants to send from chunk %li but maximum is %li",_nextChunkToSendFrom,chunkCount);
+ return false;
+ }
+
+ SingleChunk * chunk = owner.chunks[_nextChunkToSendFrom];
+ if (chunk.state != READY) {
+// NSLog(@"NATTY: Cant send chunk yet its not ready");
+ return false;
+ }
+
+ // Chunk is ready so let 'er rip
+ long int byteInsideChunk = ByteToByteInsideChunk(_nextByteToSend);
+ long int bytesToSendFromChunk = chunk.loaded - byteInsideChunk;
+ long int bytesSent;
+
+ if ((byteInsideChunk == 0) && (bytesToSendFromChunk <= _bytesRemaining)) {
+ // We can take them all
+ [self.DR.dataRequest respondWithData:chunk.chunkData];
+
+ bytesSent = bytesToSendFromChunk;
+ } else {
+ // We need to chop out the bytes we are taking
+ long int bytesToTake = (_bytesRemaining < bytesToSendFromChunk) ? _bytesRemaining:bytesToSendFromChunk;
+// NSLog(@"NATTY: Partial - taking %li",bytesToTake);
+ [self.DR.dataRequest respondWithData:[chunk.chunkData subdataWithRange:NSMakeRange(byteInsideChunk, bytesToTake)]];
+ bytesSent = bytesToTake;
+ }
+
+ _nextByteToSend += bytesSent;
+ _bytesRemaining -= bytesSent;
+ _nextChunkToSendFrom++;
+ }
+
+ // If we make it out of the while loop, we are DONE
+
+ [self fillInContentInformation:owner];
+ [_DR finishLoading];
+ return true;
+}
+
+-(void) fillInContentInformation:(ChunkAssetLoaderDelegate*)owner{
+ if (_DR.contentInformationRequest == nil) {
+ return;
+ }
+
+ _DR.contentInformationRequest.byteRangeAccessSupported = YES;
+ _DR.contentInformationRequest.contentType = owner.format == VIDEO ? AVFileTypeMPEG4 : AVFileTypeAppleM4A; // Hardcoded - we ignore response content type
+ _DR.contentInformationRequest.contentLength = owner.totalSize;
+}
+
+@end
diff --git a/ios/AssetChunk/HunkLoad.h b/ios/AssetChunk/HunkLoad.h
new file mode 100644
index 0000000000..4953d38213
--- /dev/null
+++ b/ios/AssetChunk/HunkLoad.h
@@ -0,0 +1,36 @@
+//
+// HunkLoad.h
+// RCTVideo
+//
+// A single http request to load one or more chunks, which is a hunk of chunks.
+//
+
+#ifndef HunkLoad_h
+#define HunkLoad_h
+
+#import
+#import "ChunkAssetLoaderDelegate.h"
+
+@interface HunkLoad : NSObject
+
+@property (nonatomic, strong) NSHTTPURLResponse *response;
+@property (nonatomic, strong) ChunkAssetLoaderDelegate* owner;
+@property (nonatomic, strong) NSURLSession * session;
+@property (nonatomic, strong) NSURLSessionDataTask * task;
+
+@property (nonatomic) long int firstChunk;
+@property (nonatomic) long int lastChunk;
+
+@property (nonatomic) long int nextByte; // next byte to arrive will be this position in the file
+
+@property (nonatomic) bool cancelled;
+
+//- (void)LoadChunk: (NSURL*)url startAt:(long long)offset loadBytes:(long long)size resource:(AVAssetResourceLoadingRequest *)loadingRequest;
+
+-(id) initWithChunkRange:(long int)firstChunk to:(long int)lastChunk ownedBy:(ChunkAssetLoaderDelegate*)owner;
+- (long int)getTotalSizeFromHeaders;
+-(void)cleanup;
+
+@end
+
+#endif /* ChunkLoad_h */
diff --git a/ios/AssetChunk/HunkLoad.m b/ios/AssetChunk/HunkLoad.m
new file mode 100644
index 0000000000..eb150b9c87
--- /dev/null
+++ b/ios/AssetChunk/HunkLoad.m
@@ -0,0 +1,198 @@
+//
+// ChunkLoad.m
+// RCTVideo
+//
+//
+
+#import
+#import "HunkLoad.h"
+#import "SingleChunk.h"
+#import
+
+#define REQUEST_TIMEOUT 5.0
+#define INVOKE_FAKE_ERRORS 0
+
+@implementation HunkLoad
+
+-(id) initWithChunkRange:(long int)firstChunk to:(long int)lastChunk ownedBy:(ChunkAssetLoaderDelegate*)owner {
+
+ if (self = [super init]) {
+ _firstChunk = firstChunk;
+ _lastChunk = lastChunk;
+ _owner = owner;
+ _cancelled = false;
+ _nextByte = FirstByteOfChunk(_firstChunk);
+
+ [self makeTheRequest];
+ }
+
+ return self;
+}
+
+-(void)makeTheRequest {
+
+ // if (_firstChunk == _lastChunk) {
+ // NSLog(@"%li, // CHONK",_firstChunk);
+ // }
+
+ // NSLog(@"HUNKY LOAD REQUESTED %i to %i",_firstChunk,_lastChunk);
+
+ NSURL * theUrl = _owner.fileUrl;
+
+#if INVOKE_FAKE_ERRORS
+ static int hunknum = 0;
+ hunknum++;
+ if (hunknum % 40 == 0) {
+ theUrl = [NSURL URLWithString:@"http://thepisspot.org/asfjkl"];
+ NSLog(@"FUCKSAKE GONNA THROW THIS ONE");
+ }
+// NSLog(@"FUCKSAKE HUNKNUM:%i",hunknum);
+#endif
+
+ NSMutableURLRequest *request =
+ [NSMutableURLRequest requestWithURL:theUrl
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:REQUEST_TIMEOUT];
+
+
+ long int lastByte =LastByteOfChunk(_lastChunk);
+ if (_owner.totalSize > -1) {
+ if (_owner.totalSize <= lastByte) lastByte = _owner.totalSize - 1;
+ }
+
+ // Set the range for our request
+ NSString *range = @"bytes=";
+ range = [range stringByAppendingString:[[NSNumber numberWithLongLong:_nextByte]
+ stringValue]];
+ range = [range stringByAppendingString:@"-"];
+ range = [range
+ stringByAppendingString:[[NSNumber numberWithLongLong:lastByte]
+ stringValue]];
+ // NSLog(@"HUNKY - range: %@", range);
+
+ [request setValue:range forHTTPHeaderField:@"Range"];
+
+
+ _session = [NSURLSession sessionWithConfiguration:[HunkLoad getSessionConfig]
+ delegate:self
+ delegateQueue:[NSOperationQueue mainQueue]];
+ _task = [_session dataTaskWithRequest:request];
+ [_task resume];
+
+}
+
+-(void)cleanup {
+ _cancelled = true;
+ if (_task) {
+ [_task cancel];
+ _task = nil;
+ }
+ if (_session) {
+ [_session invalidateAndCancel];
+ _session = nil;
+ }
+}
+
+#pragma mark - NSURLConnection delegate
+
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+didReceiveResponse:(NSURLResponse *)response
+ completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
+ self.response = (NSHTTPURLResponse *)response;
+ completionHandler(NSURLSessionResponseAllow);
+}
+
+ - (void)URLSession:(NSURLSession *)session
+dataTask:(NSURLSessionDataTask *)dataTask
+ didReceiveData:(NSData *)data{
+
+ if (_cancelled) {
+ return;
+ }
+
+ long int bytesLeft = data.length;
+ long int dataPos = 0;
+
+ // Offer the data up to the various chunks
+
+ while (bytesLeft > 0) {
+ long int targetChunkIdx = ByteToContainingChunk(_nextByte);
+
+ // Grab our target chunk
+ SingleChunk *targetChunk;
+ if (_owner.chunks.count < targetChunkIdx) {
+ NSLog(@"ERROR ASSETCHUNK - Try to send bytes to a chunk that's not allocated - idx %li",targetChunkIdx);
+ return;
+ }
+ targetChunk = _owner.chunks[targetChunkIdx];
+
+ // work out if this is a finishing move
+ bool finishing = false;
+ if (_owner.totalSize > -1) {
+ if (_nextByte + bytesLeft >= _owner.totalSize-1) {
+ finishing = true;
+ }
+ }
+
+
+ long int bytesTaken = [targetChunk loadBytesFrom:data maximum:bytesLeft filePos:_nextByte dataPos:dataPos owner:_owner hunk:self finishing:finishing];
+
+ _nextByte += bytesTaken;
+ bytesLeft -= bytesTaken;
+ dataPos += bytesTaken;
+
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session
+ task:(NSURLSessionTask *)task
+didCompleteWithError:(NSError *)error; {
+ if (error) {
+ NSLog(@"FUCKSAKE ERROR: %@ %@ %@",[error localizedDescription],[error localizedFailureReason],[error localizedRecoverySuggestion]);
+ if ((_session == session) && (_task == task) && (!_cancelled)) {
+ NSLog(@"FUCKSAKE GONNA TRY AGAIN");
+ [self cleanup];
+ _cancelled = false;
+
+ [self makeTheRequest];
+ } else {
+ NSLog(@"FUCKSAKE It's not me");
+ }
+ return;
+ }
+ _owner = nil; // Hopefully garbage will get collected.
+ [self cleanup];
+}
+
+
+- (long int)getTotalSizeFromHeaders {
+ NSDictionary *headers = [self.response allHeaderFields];
+ NSString *range = [headers objectForKey:@"Content-Range"];
+ NSRange slash = [range rangeOfString:@"/"];
+ NSString *totalbit = [range substringFromIndex:slash.location + 1];
+
+// NSString *mimeType = [self.response MIMEType];
+// NSLog(@"KEVIN MIMETYPE: %@",mimeType);
+
+ return [totalbit integerValue];
+}
+
++ (NSURLSessionConfiguration*) getSessionConfig
+{
+ static NSURLSessionConfiguration* sessionConfig;
+ static dispatch_once_t once;
+ dispatch_once(&once, ^{
+ sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
+
+ sessionConfig.timeoutIntervalForRequest = REQUEST_TIMEOUT;
+ sessionConfig.HTTPMaximumConnectionsPerHost = 15;
+ sessionConfig.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
+ sessionConfig.HTTPShouldUsePipelining = NO;
+// sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+
+ });
+ return sessionConfig;
+}
+
+@end
diff --git a/ios/AssetChunk/MP4Cacher.h b/ios/AssetChunk/MP4Cacher.h
new file mode 100644
index 0000000000..b2105b1907
--- /dev/null
+++ b/ios/AssetChunk/MP4Cacher.h
@@ -0,0 +1,14 @@
+//
+// MP4Cacher.h
+// RCTVideo
+//
+// Created by Beau Ner Chesluk on 05/11/2018.
+// Copyright © 2018 Facebook. All rights reserved.
+//
+
+#ifndef MP4Cacher_h
+#define MP4Cacher_h
+
+void CacheMP4BasedOffChunk(ChunkAssetLoaderDelegate * owner, SingleChunk * chunk);
+
+#endif /* MP4Cacher_h */
diff --git a/ios/AssetChunk/MP4Cacher.m b/ios/AssetChunk/MP4Cacher.m
new file mode 100644
index 0000000000..35fc036a30
--- /dev/null
+++ b/ios/AssetChunk/MP4Cacher.m
@@ -0,0 +1,117 @@
+//
+// MP4Cacher.m
+// RCTVideo
+//
+// Created by Beau Ner Chesluk on 05/11/2018.
+// Copyright © 2018 Facebook. All rights reserved.
+//
+
+#import
+#import "ChunkAssetLoaderDelegate.h"
+#import "SingleChunk.h"
+
+NSInteger Chonks[36] = {
+ 0, // CHONK
+ 2, // CHONK
+ 6, // CHONK
+ 10, // CHONK
+ 19, // CHONK2018-11-05 20:51:29.208420+0000 wrath[9935:3322689] 23, // CHONK
+ 31, // CHONK
+ 38, // CHONK
+ 42, // CHONK
+ 53, // CHONK
+ 58, // CHONK
+ 67, // CHONK
+ 70, // CHONK
+ 79, // CHONK
+ 85, // CHONK
+ 94, // CHONK
+ 100, // CHONK
+ 103, // CHONK
+ 107, // CHONK
+ 116, // CHONK
+ 126, // CHONK
+ 136, // CHONK
+ 137, // CHONK
+ 144, // CHONK
+ 147, // CHONK
+ 152, // CHONK
+ 158, // CHONK
+ 165, // CHONK
+ 173, // CHONK
+ 184, // CHONK
+ 193, // CHONK
+ 199, // CHONK
+ 215, // CHONK
+ 229, // CHONK
+ 241, // CHONK
+ 258, // CHONK
+ 270, // CHONK
+};
+
+unsigned int const SIDX_TAG = 0x73696478;
+
+void CacheMP4BasedOffChunk(ChunkAssetLoaderDelegate * owner, SingleChunk * chunk) {
+ /* Our mission: Scan through this partial MP4 and find the sidx tag in the hopes that it is present. Then cache all the spots that apple are gonna want. */
+// return;
+ int pointer = 0;
+ unsigned long maxChunk = [owner.chunks count];
+
+ while (pointer < chunk.loaded-8) {
+ unsigned int length = CFSwapInt32BigToHost(((unsigned int*)&((char*)chunk.chunkData.bytes)[pointer])[0]);
+ unsigned int type = CFSwapInt32BigToHost(((unsigned int*)&((char*)chunk.chunkData.bytes)[pointer+4])[0]);
+// NSLog(@"MIFFY LENGTH= %u",length);
+// NSLog(@"MIFFY TYPE= %u",type);
+
+
+ unsigned int interestingSpotToIos = pointer + length;
+
+ if (type == SIDX_TAG) {
+// NSLog(@"MIFFY IT IS SIDX");
+
+ // HURRAH LETS PARSE THE FUCK OUT OF ITQ
+ char version = ((char*)chunk.chunkData.bytes)[pointer+8];
+// NSLog(@"MIFFY VERSION %i",version);
+
+ int subPointer = pointer+(version == 0? 30:38);
+ unsigned short count = CFSwapInt16BigToHost(((unsigned short*)&((char*)chunk.chunkData.bytes)[subPointer])[0]);
+
+ subPointer += 2;
+ while (count > 0) {
+
+ // Mark the interesting spot to ios as a chunk to preload
+ unsigned long interestingChunk = ByteToContainingChunk(interestingSpotToIos);
+ if (interestingChunk < maxChunk) {
+ SingleChunk * chunk = owner.chunks[interestingChunk];
+ if (chunk.state == EMPTY){
+ chunk.state = WANTED;
+ }
+ }
+
+
+
+
+ // get the size of this chunk thing and advance our interesting spot pointer, so next time around we are on top of it.
+ unsigned int referenced_size = CFSwapInt32BigToHost(((unsigned int*)&((char*)chunk.chunkData.bytes)[subPointer])[0]) & 0x7fffffff;
+// NSLog(@"MIFFY REFSIZE %u %i",referenced_size,count);
+ subPointer += 12;
+ count--;
+ interestingSpotToIos += referenced_size;
+ }
+
+// NSLog(@"MIFFY COUNT=%i",count);
+ return; // We are done
+
+ }
+ pointer += length;
+ }
+
+}
+
+
+
+
+
+
+
+
diff --git a/ios/AssetChunk/SingleChunk.h b/ios/AssetChunk/SingleChunk.h
new file mode 100644
index 0000000000..c52a48f6bb
--- /dev/null
+++ b/ios/AssetChunk/SingleChunk.h
@@ -0,0 +1,44 @@
+//
+// SingleChunk.h
+// RCTVideo
+//
+// Represents a single chunk of data being loaded in
+//
+
+#ifndef SingleChunk_h
+#define SingleChunk_h
+
+#import
+#import
+#import "ChunkAssetLoaderDelegate.h"
+
+#define CHUNK_SIZE 65536
+
+typedef enum : NSUInteger {
+ EMPTY,
+ WANTED,
+ LOADING,
+ READY,
+} ChunkState;
+
+@interface SingleChunk : NSObject
+
+@property (nonatomic, strong) NSMutableData *chunkData;
+@property (nonatomic) bool keystone;
+@property (nonatomic) ChunkState state;
+@property (nonatomic) long int index;
+@property (nonatomic) long int loaded;
+
+-(id) initWithIndex:(long int)index;
+-(long int) loadBytesFrom:(NSData*)data maximum:(long int)bytesLeft filePos:(long int)filePos dataPos:(long int)dataPos owner:(ChunkAssetLoaderDelegate*)_owner hunk:(HunkLoad*)hunk finishing:(bool)finishing;
+
+@end
+
+long int FirstByteOfChunk(long int chunk);
+long int LastByteOfChunk(long int chunk);
+long int ByteToContainingChunk(long int byte);
+long int StartAndLengthToLastByte(long int start, long int length);
+long int InclusiveBytesToLength(long int start, long int end);
+long int ByteToByteInsideChunk(long int byte);
+
+#endif /* SingleChunk_h */
diff --git a/ios/AssetChunk/SingleChunk.m b/ios/AssetChunk/SingleChunk.m
new file mode 100644
index 0000000000..94964e02f7
--- /dev/null
+++ b/ios/AssetChunk/SingleChunk.m
@@ -0,0 +1,83 @@
+//
+// SingleChunk.m
+// RCTVideo
+//
+//
+
+#import
+#import "SingleChunk.h"
+
+@implementation SingleChunk
+
+-(id) initWithIndex:(long int)index {
+ if (self = [super init]) {
+ _index = index;
+ _keystone = false;
+ _state = EMPTY;
+ _loaded = 0;
+ _chunkData = [NSMutableData data];
+ }
+
+ return self;
+
+}
+
+-(long int) loadBytesFrom:(NSData*)data maximum:(long int)bytesLeft filePos:(long int)filePos dataPos:(long int)dataPos owner:(ChunkAssetLoaderDelegate*)_owner hunk:(HunkLoad*)hunk finishing:(bool)finishing{
+ long int targetOffset = ByteToByteInsideChunk(filePos);
+ if (_loaded != targetOffset) {
+ NSLog(@"ERROR ASSETCHUNK - Stuffing bytes in expected file pos to be %li but its %li",_loaded,targetOffset);
+ return 1;
+ }
+
+ // Check how many bytes we can take
+ long int bytesCanTake = CHUNK_SIZE-_loaded;
+ long int bytesTaken = 0;
+
+ if ((dataPos == 0) && (bytesLeft <= bytesCanTake)) {
+ // We can take them all
+ [self.chunkData appendData:data];
+ bytesTaken = bytesLeft;
+ } else {
+ // We need to chop out the bytes we are taking
+ long int bytesToTake = (bytesLeft < bytesCanTake) ? bytesLeft:bytesCanTake;
+ [self.chunkData appendData:[data subdataWithRange:NSMakeRange(dataPos, bytesToTake)]];
+ bytesTaken = bytesToTake;
+ }
+
+ // Check if the chunk is now complete
+ _loaded += bytesTaken;
+ if ((_loaded >= CHUNK_SIZE) || finishing) {
+ _state = READY;
+ [_owner chunkFinishedLoading:self fromHunkLoad: hunk];
+ }
+
+ return bytesTaken;
+}
+
+@end
+
+
+long int FirstByteOfChunk(long int chunk){
+ return chunk * CHUNK_SIZE;
+}
+
+long int LastByteOfChunk(long int chunk){
+ return chunk * CHUNK_SIZE + (CHUNK_SIZE-1);
+
+}
+
+long int ByteToContainingChunk(long int byte){
+ return byte / CHUNK_SIZE; // I hope it auto floors...
+}
+
+long int StartAndLengthToLastByte(long int start, long int length){
+ return start + length - 1;
+}
+
+long int InclusiveBytesToLength(long int start, long int end){
+ return end - start + 1;
+}
+
+long int ByteToByteInsideChunk(long int byte) {
+ return byte % CHUNK_SIZE;
+}
diff --git a/ios/AssetLoaderDelegate.h b/ios/AssetLoaderDelegate.h
new file mode 100644
index 0000000000..b6fc9e4c4b
--- /dev/null
+++ b/ios/AssetLoaderDelegate.h
@@ -0,0 +1,34 @@
+#ifndef AssetLoaderDelegate_h
+#define AssetLoaderDelegate_h
+
+//
+// AssetLoaderDelegate.h
+// TimeTag
+//
+// Created by Renjith N on 23/02/15.
+// Copyright (c) 2015 MBP1. All rights reserved.
+//
+
+#import
+#import
+
+@interface AssetLoaderDelegate : NSObject
+
+@property (nonatomic, strong) NSMutableData *movieData;
+@property (nonatomic, strong) NSURLConnection *connection;
+
+@property (nonatomic, strong) NSHTTPURLResponse *response;
+@property (nonatomic, strong) NSMutableArray *pendingRequests;
+
+@property (nonatomic, strong) NSMutableArray *chunks;
+
+@property (nonatomic,strong) NSString *cacheDir;
+@property (nonatomic,strong) NSString *fileName;
+@property (nonatomic,strong) NSString *fileUrl;
+
++ (NSString*) preViewFoundInCacheDirectory:(NSString*) url;
+- (id)init;
+
+@end
+
+#endif /* AssetLoaderDelegate_h */
diff --git a/ios/AssetLoaderDelegate.m b/ios/AssetLoaderDelegate.m
new file mode 100644
index 0000000000..979bafcc49
--- /dev/null
+++ b/ios/AssetLoaderDelegate.m
@@ -0,0 +1,187 @@
+//
+// AssetLoaderDelegate.m
+// TimeTag
+//
+// Created by Renjith N on 23/02/15.
+// Copyright (c) 2015 MBP1. All rights reserved.
+//
+
+#import "AssetLoaderDelegate.h"
+#import
+#import
+#import "ChunkLoader.h"
+
+
+@implementation AssetLoaderDelegate
+
+
+- (id)init{
+ NSLog(@"FUDGE init this fucker");
+ if (self = [super init]) {
+ self.cacheDir = [AssetLoaderDelegate cacheDirectory];
+ self.pendingRequests = [NSMutableArray array];
+ NSLog(@"FUDGE cache = %@",self.cacheDir);
+ }
+ return self;
+}
+
+#pragma mark - NSURLConnection delegate
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
+ NSLog(@"FUDGE - connected");
+ self.movieData = [NSMutableData data];
+ self.response = (NSHTTPURLResponse *)response;
+ [self processPendingRequests];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
+// NSLog(@"FUDGE - two");
+ [self.movieData appendData:data];
+ [self processPendingRequests];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
+// NSLog(@"FUDGE - three");
+
+ [self processPendingRequests];
+ NSLog(@"FUDGE Download complete");
+ NSString *fileName = [NSURL URLWithString:self.fileUrl].absoluteString.lastPathComponent;
+ NSString *cachedFilePath = [[NSString alloc] initWithFormat:@"%@/%@",self.cacheDir,[fileName componentsSeparatedByString:@"?"].firstObject];
+ BOOL writen = [self.movieData writeToFile:cachedFilePath atomically:YES];
+ if(!writen){
+ NSLog(@"FUDGE Error writing cache, what a surprise");
+
+ }
+}
+
+#pragma mark - AVURLAsset resource loading
+
+- (void)processPendingRequests{
+// NSLog(@"FUDGE - four");
+
+ NSLog(@"FUDGE processPendingRequests:%lu",(unsigned long)self.pendingRequests.count);
+ NSMutableArray *requestsCompleted = [NSMutableArray array];
+
+ for (AVAssetResourceLoadingRequest *loadingRequest in self.pendingRequests){
+ [self fillInContentInformation:loadingRequest.contentInformationRequest];
+
+ BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest];
+
+ if (didRespondCompletely){
+ [requestsCompleted addObject:loadingRequest];
+
+ [loadingRequest finishLoading];
+ }
+ }
+
+ [self.pendingRequests removeObjectsInArray:requestsCompleted];
+}
+
+- (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest{
+// NSLog(@"FUDGE - five");
+
+ if (contentInformationRequest == nil || self.response == nil){
+ return;
+ }
+
+ NSString *mimeType = [self.response MIMEType];
+ CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);
+
+ contentInformationRequest.byteRangeAccessSupported = YES;
+ contentInformationRequest.contentType = CFBridgingRelease(contentType);
+ contentInformationRequest.contentLength = [self.response expectedContentLength];
+}
+
+- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest{
+// NSLog(@"FUDGE - six");
+
+ long long startOffset = dataRequest.requestedOffset;
+ if (dataRequest.currentOffset != 0){
+ startOffset = dataRequest.currentOffset;
+ }
+
+ // Don't have any data at all for this request
+ if (self.movieData.length < startOffset){
+ return NO;
+ }
+
+ // This is the total data we have from startOffset to whatever has been downloaded so far
+ NSUInteger unreadBytes = self.movieData.length - (NSUInteger)startOffset;
+ // Respond with whatever is available if we can't satisfy the request fully yet
+ NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
+
+ NSLog(@"FUDGE data:%lu,,,(%lld,%lu)",(unsigned long)self.movieData.length,startOffset,(unsigned long)numberOfBytesToRespondWith);
+ [dataRequest respondWithData:[self.movieData subdataWithRange:NSMakeRange((NSUInteger)startOffset, numberOfBytesToRespondWith)]];
+
+ long long endOffset = startOffset + dataRequest.requestedLength;
+ BOOL didRespondFully = self.movieData.length >= endOffset;
+
+ return didRespondFully;
+}
+
+- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest{
+ NSLog(@"FUDGEPACK - Request Made - %i %i",loadingRequest.dataRequest.requestedOffset,loadingRequest.dataRequest.requestedLength);
+
+ // NEW SHIT
+ ChunkLoader * loader = [[ChunkLoader alloc] init];
+ [loader LoadChunk: [NSURL URLWithString:self.fileUrl] startAt:loadingRequest.dataRequest.requestedOffset loadBytes:loadingRequest.dataRequest.requestedLength resource:loadingRequest];
+ [self.chunks addObject:loader];
+ return YES;
+
+
+ // OLD SHIT
+
+
+
+// if (self.connection == nil){
+// NSURL *interceptedURL = [loadingRequest.request URL];
+// NSURLComponents *actualURLComponents = [[NSURLComponents alloc] initWithURL:interceptedURL resolvingAgainstBaseURL:NO];
+// actualURLComponents.scheme = @"https";
+// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.fileUrl]];
+// self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
+// [self.connection setDelegateQueue:[NSOperationQueue mainQueue]];
+// [self.connection start];
+// }
+//
+// NSLog(@"FUDGE SWFLORR pendingRequests:%@",loadingRequest);
+// [self.pendingRequests addObject:loadingRequest];
+// [self processPendingRequests];
+// return YES;
+}
+
+- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest{
+// NSLog(@"FUDGE - eight");
+ [self.pendingRequests removeObject:loadingRequest];
+}
+
++ (NSString*) preViewFoundInCacheDirectory:(NSString*) url{
+// NSLog(@"FUDGE - nine");
+
+ NSString *fileName = [NSURL URLWithString:url].absoluteString.lastPathComponent;
+
+ NSString *cachedFilePath = [[NSString alloc] initWithFormat:@"%@/%@",[AssetLoaderDelegate cacheDirectory],[fileName componentsSeparatedByString:@"?"].firstObject];
+ if([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath]){
+ return cachedFilePath;
+ }
+ else{
+ return nil;
+ }
+}
+
++ (NSString *)cacheDirectory{
+// NSLog(@"FUDGE - ten");
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+ NSString *cacheDir = [paths objectAtIndex:0];
+ NSString *videoCacheDir = [NSString stringWithFormat:@"%@/%@",cacheDir,@"Previews"];
+
+ BOOL isDir = NO;
+ NSError *error;
+ if (! [[NSFileManager defaultManager] fileExistsAtPath:videoCacheDir isDirectory:&isDir] && isDir == NO) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:videoCacheDir withIntermediateDirectories:YES attributes:nil error:&error];
+ }
+
+ return videoCacheDir;
+}
+
+@end
diff --git a/ios/ChunkLoader.h b/ios/ChunkLoader.h
new file mode 100644
index 0000000000..cec958e124
--- /dev/null
+++ b/ios/ChunkLoader.h
@@ -0,0 +1,24 @@
+//
+// ChunkLoader.h
+// RCTVideo
+//
+
+#ifndef ChunkLoader_h
+#define ChunkLoader_h
+
+
+#import
+#import
+
+@interface ChunkLoader : NSObject
+
+@property (nonatomic, strong) NSHTTPURLResponse *response;
+@property (nonatomic, strong) NSURLConnection *connection;
+@property (nonatomic, strong) NSMutableData *chunkData;
+@property (nonatomic, strong) AVAssetResourceLoadingRequest * loadingRequest;
+
+- (void)LoadChunk: (NSURL*)url startAt:(long long)offset loadBytes:(long long)size resource:(AVAssetResourceLoadingRequest *)loadingRequest;
+
+@end
+
+#endif /* ChunkLoader_h */
diff --git a/ios/ChunkLoader.m b/ios/ChunkLoader.m
new file mode 100644
index 0000000000..5475571d87
--- /dev/null
+++ b/ios/ChunkLoader.m
@@ -0,0 +1,163 @@
+#import "ChunkLoader.h"
+#import
+#import
+
+@implementation ChunkLoader
+
+- (id)init {
+ NSLog(@"RECCE ChunkLoader INIT");
+ // if (self = [super init]) {
+ // self.cacheDir = [AssetLoaderDelegate cacheDirectory];
+ // self.pendingRequests = [NSMutableArray array];
+ // NSLog(@"FUDGE cache = %@",self.cacheDir);
+ // }
+
+ return self;
+}
+
+- (void)LoadChunk:(NSURL *)url
+ startAt:(long long)offset
+ loadBytes:(long long)size
+ resource:(AVAssetResourceLoadingRequest *)loadingRequest {
+ self.loadingRequest = loadingRequest;
+
+ NSLog(@"RECCEU CHUNK URL %@", url);
+
+ NSMutableURLRequest *request =
+ [NSMutableURLRequest requestWithURL:url
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:60.0];
+
+ // Set the range for our request
+ NSString *range = @"bytes=";
+ range = [range stringByAppendingString:[[NSNumber numberWithLongLong:offset]
+ stringValue]];
+ range = [range stringByAppendingString:@"-"];
+ range = [range
+ stringByAppendingString:[[NSNumber numberWithLongLong:(offset + size - 1)]
+ stringValue]];
+ NSLog(@"RECCE - range: %@", range);
+
+ [request setValue:range forHTTPHeaderField:@"Range"];
+
+ self.connection = [[NSURLConnection alloc] initWithRequest:request
+ delegate:self
+ startImmediately:NO];
+ [self.connection setDelegateQueue:[NSOperationQueue mainQueue]];
+ [self.connection start];
+}
+
+#pragma mark - NSURLConnection delegate
+
+- (void)connection:(NSURLConnection *)connection
+ didReceiveResponse:(NSURLResponse *)response {
+ NSLog(@"RECCE - connected");
+ self.chunkData = [NSMutableData data];
+ self.response = (NSHTTPURLResponse *)response;
+ [self fillInContentInformation:self.loadingRequest.contentInformationRequest];
+ // [self processPendingRequests];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ NSLog(@"RECCE - data");
+ [self.chunkData appendData:data];
+ // [self processPendingRequests];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ NSLog(@"RECCE - done, length: %i requestedLength: %i requestedOffset: %i",
+ self.chunkData.length, self.loadingRequest.dataRequest.requestedLength,
+ self.loadingRequest.dataRequest.requestedOffset);
+ // NSLog(@"FUDGE - three");
+
+ // [self processPendingRequests];
+ // NSLog(@"FUDGE Download complete");
+ // NSString *fileName = [NSURL
+ // URLWithString:self.fileUrl].absoluteString.lastPathComponent; NSString
+ // *cachedFilePath = [[NSString alloc]
+ // initWithFormat:@"%@/%@",self.cacheDir,[fileName
+ // componentsSeparatedByString:@"?"].firstObject]; BOOL writen =
+ // [self.movieData writeToFile:cachedFilePath atomically:YES]; if(!writen){
+ // NSLog(@"FUDGE Error writing cache, what a surprise");
+ //
+ // }
+
+ [self fillInContentInformation:self.loadingRequest.contentInformationRequest];
+
+ // BOOL didRespondCompletely = [self
+ // respondWithDataForRequest:loadingRequest.dataRequest];
+ // // NSLog(@"FUDGE - six");
+ //
+ // long long startOffset = dataRequest.requestedOffset;
+ // if (dataRequest.currentOffset != 0){
+ // startOffset = dataRequest.currentOffset;
+ // }
+
+ // Don't have any data at all for this request
+ // if (self.movieData.length < startOffset){
+ // return NO;
+ // }
+
+ // This is the total data we have from startOffset to whatever has been
+ // downloaded so far
+ // NSUInteger unreadBytes = self.movieData.length -
+ // (NSUInteger)startOffset;
+ // Respond with whatever is available if we can't satisfy the request fully
+ // yet
+ // NSUInteger numberOfBytesToRespondWith =
+ // MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
+
+ // NSLog(@"FUDGE data:%lu,,,(%lld,%lu)",(unsigned
+ // long)self.movieData.length,startOffset,(unsigned
+ // long)numberOfBytesToRespondWith);
+ [self.loadingRequest.dataRequest
+ respondWithData:[self.chunkData
+ subdataWithRange:NSMakeRange(0,
+ self.chunkData.length)]];
+
+ // [self.loadingRequest.dataRequest respondWithData:self.chunkData];
+
+ [self.loadingRequest finishLoading];
+ // [self.connection cancel];
+}
+
+- (void)fillInContentInformation:
+ (AVAssetResourceLoadingContentInformationRequest *)
+ contentInformationRequest {
+ // NSLog(@"FUDGE - five");
+
+ if (contentInformationRequest == nil || self.response == nil) {
+ return;
+ }
+
+ NSString *mimeType = [self.response MIMEType];
+ CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(
+ kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);
+
+ // Work out the length
+ long length = [self.response expectedContentLength];
+ NSDictionary *headers = [self.response allHeaderFields];
+ NSString *range = [headers objectForKey:@"Content-Range"];
+ NSRange slash = [range rangeOfString:@"/"];
+ NSString *totalbit = [range substringFromIndex:slash.location + 1];
+ length = [totalbit integerValue];
+
+ NSLog(@"RECCEFART range: %@ becomes %@ length:%i", range, totalbit, length);
+
+ // if (range) {
+ //
+ // }
+
+ if (headers)
+ NSLog(@"RECCE: Headers: %@", headers);
+
+ contentInformationRequest.byteRangeAccessSupported = YES;
+ // contentInformationRequest.contentType = AVFileTypeAppleM4V;
+ contentInformationRequest.contentType = CFBridgingRelease(contentType);
+ contentInformationRequest.contentLength = length;
+
+ NSLog(@"RECCE: mimeType %@ length %i", mimeType,
+ contentInformationRequest.contentLength);
+}
+
+@end
diff --git a/ios/RCTVideo.xcodeproj/project.pbxproj b/ios/RCTVideo.xcodeproj/project.pbxproj
index 4e675ac782..41d70f7c50 100644
--- a/ios/RCTVideo.xcodeproj/project.pbxproj
+++ b/ios/RCTVideo.xcodeproj/project.pbxproj
@@ -7,6 +7,20 @@
objects = {
/* Begin PBXBuildFile section */
+ 3D292809219077D700DCBA34 /* SingleChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D292808219077D700DCBA34 /* SingleChunk.m */; };
+ 3D29280A219077D700DCBA34 /* SingleChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D292808219077D700DCBA34 /* SingleChunk.m */; };
+ 3D2928102190795500DCBA34 /* HunkLoad.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29280F2190795500DCBA34 /* HunkLoad.m */; };
+ 3D2928112190795500DCBA34 /* HunkLoad.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29280F2190795500DCBA34 /* HunkLoad.m */; };
+ 3D29281821907B3300DCBA34 /* ChunkAssetLoaderDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29281721907B3300DCBA34 /* ChunkAssetLoaderDelegate.m */; };
+ 3D29281921907B3300DCBA34 /* ChunkAssetLoaderDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29281721907B3300DCBA34 /* ChunkAssetLoaderDelegate.m */; };
+ 3D29281C21907DAB00DCBA34 /* DataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29281B21907DAB00DCBA34 /* DataRequest.m */; };
+ 3D29281D21907DAB00DCBA34 /* DataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D29281B21907DAB00DCBA34 /* DataRequest.m */; };
+ 3D5571A82190E52B00E80B9D /* MP4Cacher.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5571A72190E52B00E80B9D /* MP4Cacher.m */; };
+ 3D5571A92190E52B00E80B9D /* MP4Cacher.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5571A72190E52B00E80B9D /* MP4Cacher.m */; };
+ 3D7FAAEB2188674A0050403B /* AssetLoaderDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7FAAEA2188674A0050403B /* AssetLoaderDelegate.m */; };
+ 3D7FAAEC2188674A0050403B /* AssetLoaderDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7FAAEA2188674A0050403B /* AssetLoaderDelegate.m */; };
+ 3D7FAB38218885F40050403B /* ChunkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7FAB37218885F40050403B /* ChunkLoader.m */; };
+ 3D7FAB39218885F40050403B /* ChunkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7FAB37218885F40050403B /* ChunkLoader.m */; };
D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; };
D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; };
D1107C0C2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; };
@@ -40,6 +54,20 @@
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3D292807219077C600DCBA34 /* SingleChunk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SingleChunk.h; sourceTree = ""; };
+ 3D292808219077D700DCBA34 /* SingleChunk.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SingleChunk.m; sourceTree = ""; };
+ 3D29280B2190790500DCBA34 /* HunkLoad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HunkLoad.h; sourceTree = ""; };
+ 3D29280F2190795500DCBA34 /* HunkLoad.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HunkLoad.m; sourceTree = ""; };
+ 3D29281621907B2700DCBA34 /* ChunkAssetLoaderDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChunkAssetLoaderDelegate.h; sourceTree = ""; };
+ 3D29281721907B3300DCBA34 /* ChunkAssetLoaderDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChunkAssetLoaderDelegate.m; sourceTree = ""; };
+ 3D29281A21907DA000DCBA34 /* DataRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataRequest.h; sourceTree = ""; };
+ 3D29281B21907DAB00DCBA34 /* DataRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DataRequest.m; sourceTree = ""; };
+ 3D5571A62190E51E00E80B9D /* MP4Cacher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MP4Cacher.h; sourceTree = ""; };
+ 3D5571A72190E52B00E80B9D /* MP4Cacher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MP4Cacher.m; sourceTree = ""; };
+ 3D7FAAE92188672D0050403B /* AssetLoaderDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AssetLoaderDelegate.h; sourceTree = ""; };
+ 3D7FAAEA2188674A0050403B /* AssetLoaderDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AssetLoaderDelegate.m; sourceTree = ""; };
+ 3D7FAB37218885F40050403B /* ChunkLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChunkLoader.m; sourceTree = ""; };
+ 3D7FAB3A2188860B0050403B /* ChunkLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChunkLoader.h; sourceTree = ""; };
641E28441F0EEC8500443AF6 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; };
D1107C012110259000073188 /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewController.h; path = Video/RCTVideoPlayerViewController.h; sourceTree = ""; };
D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewControllerDelegate.h; path = Video/RCTVideoPlayerViewControllerDelegate.h; sourceTree = ""; };
@@ -78,6 +106,23 @@
name = Products;
sourceTree = "";
};
+ 3D2928062190779900DCBA34 /* AssetChunk */ = {
+ isa = PBXGroup;
+ children = (
+ 3D292807219077C600DCBA34 /* SingleChunk.h */,
+ 3D292808219077D700DCBA34 /* SingleChunk.m */,
+ 3D29280B2190790500DCBA34 /* HunkLoad.h */,
+ 3D29280F2190795500DCBA34 /* HunkLoad.m */,
+ 3D29281A21907DA000DCBA34 /* DataRequest.h */,
+ 3D29281B21907DAB00DCBA34 /* DataRequest.m */,
+ 3D29281621907B2700DCBA34 /* ChunkAssetLoaderDelegate.h */,
+ 3D29281721907B3300DCBA34 /* ChunkAssetLoaderDelegate.m */,
+ 3D5571A62190E51E00E80B9D /* MP4Cacher.h */,
+ 3D5571A72190E52B00E80B9D /* MP4Cacher.m */,
+ );
+ path = AssetChunk;
+ sourceTree = "";
+ };
49E995712048B4CE00EA7890 /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -88,6 +133,11 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
+ 3D2928062190779900DCBA34 /* AssetChunk */,
+ 3D7FAB3A2188860B0050403B /* ChunkLoader.h */,
+ 3D7FAB37218885F40050403B /* ChunkLoader.m */,
+ 3D7FAAEA2188674A0050403B /* AssetLoaderDelegate.m */,
+ 3D7FAAE92188672D0050403B /* AssetLoaderDelegate.h */,
D1107C072110259000073188 /* RCTVideo.h */,
D1107C052110259000073188 /* RCTVideo.m */,
D1107C092110259000073188 /* RCTVideoManager.h */,
@@ -181,10 +231,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 3D7FAB38218885F40050403B /* ChunkLoader.m in Sources */,
D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */,
D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */,
D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */,
+ 3D29281821907B3300DCBA34 /* ChunkAssetLoaderDelegate.m in Sources */,
+ 3D5571A82190E52B00E80B9D /* MP4Cacher.m in Sources */,
D1107C0C2110259000073188 /* RCTVideo.m in Sources */,
+ 3D29281C21907DAB00DCBA34 /* DataRequest.m in Sources */,
+ 3D292809219077D700DCBA34 /* SingleChunk.m in Sources */,
+ 3D7FAAEB2188674A0050403B /* AssetLoaderDelegate.m in Sources */,
+ 3D2928102190795500DCBA34 /* HunkLoad.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -192,10 +249,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 3D7FAB39218885F40050403B /* ChunkLoader.m in Sources */,
D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */,
D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */,
D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */,
+ 3D29281921907B3300DCBA34 /* ChunkAssetLoaderDelegate.m in Sources */,
+ 3D5571A92190E52B00E80B9D /* MP4Cacher.m in Sources */,
D1107C0D2110259000073188 /* RCTVideo.m in Sources */,
+ 3D29281D21907DAB00DCBA34 /* DataRequest.m in Sources */,
+ 3D29280A219077D700DCBA34 /* SingleChunk.m in Sources */,
+ 3D7FAAEC2188674A0050403B /* AssetLoaderDelegate.m in Sources */,
+ 3D2928112190795500DCBA34 /* HunkLoad.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/ios/Video/RCTVideo.h b/ios/Video/RCTVideo.h
index e43fbe50bb..8b7aca72ae 100644
--- a/ios/Video/RCTVideo.h
+++ b/ios/Video/RCTVideo.h
@@ -18,6 +18,8 @@
@interface RCTVideo : UIView
#endif
+@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadUpdate; // ZEROLABS
+@property (nonatomic) bool sendLoadUpdate; // ZEROLABS
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
@property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad;
@property (nonatomic, copy) RCTBubblingEventBlock onVideoBuffer;
@@ -41,4 +43,6 @@
- (AVPlayerViewController*)createPlayerViewController:(AVPlayer*)player withPlayerItem:(AVPlayerItem*)playerItem;
+- (void)performSendLoadUpdate:(NSString *)Map format:(NSString*)format; // ZEROLABS
+
@end
diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m
index 61b8757850..202f0b33d2 100644
--- a/ios/Video/RCTVideo.m
+++ b/ios/Video/RCTVideo.m
@@ -5,6 +5,7 @@
#import
#include
#include
+#import "ChunkAssetLoaderDelegate.h" // ZEROLABS
static NSString *const statusKeyPath = @"status";
static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp";
@@ -24,6 +25,18 @@
@implementation RCTVideo
{
+ // ZEROLABS Start
+ NSDictionary * _theSource;
+ NSDictionary * _theAudioSource;
+ AVURLAsset * _audioAsset;
+ AVURLAsset * _videoAsset;
+ BOOL inView; // We are here
+ ChunkAssetLoaderDelegate * videoAssetLoader;
+ ChunkAssetLoaderDelegate * audioAssetLoader;
+ int thisInst;
+ BOOL _separateAudioTrack;
+ // ZEROLABS END
+
AVPlayer *_player;
AVPlayerItem *_playerItem;
BOOL _playerItemObserversSet;
@@ -74,8 +87,13 @@ @implementation RCTVideo
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
+ static int instid = 0; // ZEROLABS
+
if ((self = [super init])) {
_eventDispatcher = eventDispatcher;
+ thisInst = instid++; // ZEROLABS
+ inView = true; // ZEROLABS
+ _separateAudioTrack = YES; // ZEROLABS
_playbackRateObserverRegistered = NO;
_isExternalPlaybackActiveObserverRegistered = NO;
@@ -323,6 +341,176 @@ - (void)removePlayerItemObservers
- (void)setSrc:(NSDictionary *)source
{
+ // ZEROLABS - setSRC has massively changed.
+
+ // NSLog(@"PANTIES VAL EXISTS=%@
+ // inst:%i",videoAssetLoader?@"YES":@"NO",thisInst);
+ // Start loading it immediately
+ NSString *uri = [source objectForKey:@"uri"];
+ // uri = @"http://192.168.1.86:8987/piss.mp4";
+ // uri = @"http://192.168.2.228:8987/piss.mp4";
+
+ BOOL doAssetLoader = false;
+
+ NSOperatingSystemVersion ios12 = (NSOperatingSystemVersion){12, 0, 0};
+ if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios12]) {
+ doAssetLoader = true;
+ }
+
+ AVURLAsset *asset;
+
+ if (doAssetLoader) {
+ videoAssetLoader =
+ [[ChunkAssetLoaderDelegate alloc] initWithUrl:[NSURL URLWithString:uri]
+ format:VIDEO
+ vidview:self];
+
+ NSDictionary *options2 =
+ @{AVURLAssetPreferPreciseDurationAndTimingKey: @YES};
+
+ // NSDictionary *options2 = @{};
+
+ // Change our URL
+
+ asset =
+ [AVURLAsset URLAssetWithURL:[self url:[NSURL URLWithString:uri]
+ WithCustomScheme:@"pixi"]
+ options:options2];
+ [asset.resourceLoader setDelegate:videoAssetLoader
+ queue:dispatch_get_main_queue()];
+
+ } else {
+ asset =
+ [AVURLAsset URLAssetWithURL:[NSURL URLWithString:uri] options:nil];
+ }
+
+ NSArray *requestedKeys =
+ [NSArray arrayWithObjects:@"duration", @"tracks", nil];
+
+ [asset loadValuesAsynchronouslyForKeys:requestedKeys
+ completionHandler:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _videoAsset = asset;
+ [self crankVideo2];
+ });
+ }];
+}
+
+// ZEROLABS method
+- (void)setAudioSrc:(NSDictionary *)source
+{
+
+ NSString *uri = [source objectForKey:@"uri"];
+
+ if ([uri isEqualToString:@""]) {
+ _separateAudioTrack = NO;
+ [self crankVideo2];
+ return;
+ }
+
+#define AUDIO_DO_ASSET_LOADER 1
+
+#if AUDIO_DO_ASSET_LOADER
+ audioAssetLoader =
+ [[ChunkAssetLoaderDelegate alloc] initWithUrl:[NSURL URLWithString:uri]
+ format:AUDIO
+ vidview:self];
+
+ NSDictionary *options2 =
+ @{AVURLAssetPreferPreciseDurationAndTimingKey: @YES};
+
+ // NSDictionary *options2 = @{};
+ AVURLAsset *asset =
+ [AVURLAsset URLAssetWithURL:[self url:[NSURL URLWithString:uri]
+ WithCustomScheme:@"pixi"]
+ options:options2];
+ [asset.resourceLoader setDelegate:audioAssetLoader
+ queue:dispatch_get_main_queue()];
+
+#else
+ NSDictionary *options2 =
+ @{AVURLAssetPreferPreciseDurationAndTimingKey: @YES};
+
+ AVURLAsset *asset =
+ [AVURLAsset URLAssetWithURL:[NSURL URLWithString:uri] options:options2];
+#endif
+
+ NSArray *requestedKeys =
+ [NSArray arrayWithObjects:@"duration", @"tracks", nil];
+
+ [asset loadValuesAsynchronouslyForKeys:requestedKeys
+ completionHandler:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _audioAsset = asset;
+ [self crankVideo2];
+ });
+ }];
+
+ // Old way:
+ // _theAudioSource = source;
+ // [self crankVideo];
+}
+
+// ZEROLABS method
+- (NSURL *)url:(NSURL *)url WithCustomScheme:(NSString *)scheme {
+ NSURLComponents *components =
+ [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
+ components.scheme = scheme;
+ return [components URL];
+}
+
+// ZEROLABS method
+- (void)playerItemFromReadyAssets:(void (^)(AVPlayerItem *))handler {
+
+#define DO_MIX_COMPOSITION 1
+ // AUDIO SOURCE BEGIN
+
+ // sideload text tracks
+ if (_separateAudioTrack) {
+
+ AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
+
+ AVAssetTrack *videoTrack =
+ [_videoAsset tracksWithMediaType:AVMediaTypeVideo].firstObject;
+ AVMutableCompositionTrack *videoCompTrack = [mixComposition
+ addMutableTrackWithMediaType:AVMediaTypeVideo
+ preferredTrackID:kCMPersistentTrackID_Invalid];
+ [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
+ videoTrack.timeRange.duration)
+ ofTrack:videoTrack
+ atTime:kCMTimeZero
+ error:nil];
+
+ AVAssetTrack *audioTrack =
+ [_audioAsset tracksWithMediaType:AVMediaTypeAudio].firstObject;
+ AVMutableCompositionTrack *audioCompTrack = [mixComposition
+ addMutableTrackWithMediaType:AVMediaTypeAudio
+ preferredTrackID:kCMPersistentTrackID_Invalid];
+ [audioCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
+ audioTrack.timeRange.duration)
+ ofTrack:audioTrack
+ atTime:kCMTimeZero
+ error:nil];
+
+ handler([AVPlayerItem playerItemWithAsset:mixComposition]);
+ } else {
+ handler([AVPlayerItem playerItemWithAsset:_videoAsset]);
+ }
+ return;
+}
+
+// ZEROLABS method - varied from original setSrc.
+- (void)crankVideo2 {
+ if ((_videoAsset == nil) || (_audioAsset == nil && _separateAudioTrack)) {
+ return;
+ }
+ // NSLog(@"PANTIES CRANK inst:%i",thisInst);
+
+ if (!inView) {
+ // NSLog(@"PANTIES FUCK ME <=-=----= %i",thisInst);
+ return;
+ }
+
[self removePlayerLayer];
[self removePlayerTimeObserver];
[self removePlayerItemObservers];
@@ -330,7 +518,7 @@ - (void)setSrc:(NSDictionary *)source
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 0), dispatch_get_main_queue(), ^{
// perform on next run loop, otherwise other passed react-props may not be set
- [self playerItemForSource:source withCallback:^(AVPlayerItem * playerItem) {
+ [self playerItemFromReadyAssets:^(AVPlayerItem * playerItem) { // ZEROLABS
_playerItem = playerItem;
[self addPlayerItemObservers];
@@ -360,12 +548,13 @@ - (void)setSrc:(NSDictionary *)source
//Perform on next run loop, otherwise onVideoLoadStart is nil
if (self.onVideoLoadStart) {
- id uri = [source objectForKey:@"uri"];
- id type = [source objectForKey:@"type"];
+ // ZEROLABS - souce => _theSource
+ id uri = [_theSource objectForKey:@"uri"];
+ id type = [_theSource objectForKey:@"type"];
self.onVideoLoadStart(@{@"src": @{
@"uri": uri ? uri : [NSNull null],
@"type": type ? type : [NSNull null],
- @"isNetwork": [NSNumber numberWithBool:(bool)[source objectForKey:@"isNetwork"]]},
+ @"isNetwork": [NSNumber numberWithBool:(bool)[_theSource objectForKey:@"isNetwork"]]}, // ZEROLABS
@"target": self.reactTag
});
}
@@ -374,6 +563,88 @@ - (void)setSrc:(NSDictionary *)source
_videoLoadStarted = YES;
}
+// ZEROLABS method
+- (void)crankVideo {
+ if ((_theSource == nil) || (_theAudioSource == nil)) {
+ return;
+ }
+ [self removePlayerLayer];
+ [self removePlayerTimeObserver];
+ [self removePlayerItemObservers];
+
+ dispatch_after(
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)0), dispatch_get_main_queue(),
+ ^{
+ // perform on next run loop, otherwise other passed react-props may not
+ // be set
+ [self
+ playerItemForSource:_theSource
+ withAudio:_theAudioSource
+ withCallback:^(AVPlayerItem *playerItem) {
+ _playerItem = playerItem;
+ [self addPlayerItemObservers];
+
+ [_player pause];
+ [_playerViewController.view removeFromSuperview];
+ _playerViewController = nil;
+
+ if (_playbackRateObserverRegistered) {
+ [_player removeObserver:self
+ forKeyPath:playbackRate
+ context:nil];
+ _playbackRateObserverRegistered = NO;
+ }
+ if (_isExternalPlaybackActiveObserverRegistered) {
+ [_player removeObserver:self
+ forKeyPath:externalPlaybackActive
+ context:nil];
+ _isExternalPlaybackActiveObserverRegistered = NO;
+ }
+
+ _player = [AVPlayer playerWithPlayerItem:_playerItem];
+ _player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
+
+ [_player addObserver:self
+ forKeyPath:playbackRate
+ options:0
+ context:nil];
+ _playbackRateObserverRegistered = YES;
+
+ [_player addObserver:self
+ forKeyPath:externalPlaybackActive
+ options:0
+ context:nil];
+ _isExternalPlaybackActiveObserverRegistered = YES;
+
+ [self addPlayerTimeObserver];
+
+ // Perform on next run loop, otherwise onVideoLoadStart is
+ // nil
+ if (self.onVideoLoadStart) {
+ id uri = [_theSource objectForKey:@"uri"];
+ id type = [_theSource objectForKey:@"type"];
+ self.onVideoLoadStart(@{
+ @"src": @{
+ @"uri": uri ? uri: [NSNull null],
+ @"type": type ? type: [NSNull null],
+ @"isNetwork": [NSNumber
+ numberWithBool:(bool)[_theSource
+ objectForKey:@"isNetwork"]]
+ },
+ @"target": self.reactTag // ZL
+ }); // ZL
+ } // ZL
+ }]; // ZL
+ }); // ZL
+ _videoLoadStarted = YES; // ZL
+}
+
+// ZEROLABS METHOD
+- (void)setSendLoadUpdate:(BOOL)sendLoadUpdate
+{
+ _sendLoadUpdate = sendLoadUpdate;
+}
+
- (NSURL*) urlFilePath:(NSString*) filepath {
if ([filepath containsString:@"file://"]) {
return [NSURL URLWithString:filepath];
@@ -446,11 +717,15 @@ - (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nu
handler([AVPlayerItem playerItemWithAsset:mixComposition]);
}
-- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler
-{
+// ZEROLABS method
+- (void)playerItemForSource:(NSDictionary *)source
+ withAudio:(NSDictionary *)audio // NEW
+ withCallback:(void (^)(AVPlayerItem *))handler {
bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]];
bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]];
NSString *uri = [source objectForKey:@"uri"];
+ NSString *audioUri = [audio objectForKey:@"uri"]; // ZL
+ uri = @"http://192.168.2.228:8987/piss.mp4"; // ZL
NSString *type = [source objectForKey:@"type"];
NSURL *url = isNetwork || isAsset
@@ -481,8 +756,180 @@ - (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlaye
}
#endif
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions];
- [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler];
+#define DO_ASSET_LOADER 0
+
+#if DO_ASSET_LOADER
+ _assetLoader = [[ChunkAssetLoaderDelegate alloc] initWithUrl];
+ _assetLoader.fileUrl = uri; // S3 url in this case
+
+ // NSDictionary *options2 =
+ // @{AVURLAssetPreferPreciseDurationAndTimingKey: @YES};
+
+ NSDictionary *options2 = @{};
+
+ AVURLAsset *asset =
+ [AVURLAsset URLAssetWithURL:[NSURL URLWithString:@"piss:poagkhsdkgjh"]
+ // URLAssetWithURL:[self url:url
+ // WithCustomScheme:@"pixi"]
+ options:options2];
+ [asset.resourceLoader setDelegate:_assetLoader
+ queue:dispatch_get_main_queue()];
+
+#else
+ AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
+#endif
+
+#define DO_MIX_COMPOSITION 1
+ // AUDIO SOURCE BEGIN
+
+ // sideload text tracks
+#if (DO_MIX_COMPOSITION)
+
+ NSArray *requestedKeys =
+ [NSArray arrayWithObjects:@"duration", @"tracks", nil];
+
+ [asset
+ loadValuesAsynchronouslyForKeys:requestedKeys
+ completionHandler:
+ ^{
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ AVURLAsset *audioAss = [AVURLAsset
+ URLAssetWithURL:
+ [NSURL
+ URLWithString:audioUri]
+ options:nil];
+
+ [audioAss loadValuesAsynchronouslyForKeys:
+ requestedKeys
+ completionHandler:
+ ^{
+ dispatch_async(dispatch_get_main_queue(),
+ ^{
+ AVMutableComposition
+ *mixComposition =
+ [[AVMutableComposition
+ alloc]
+ init];
+
+ AVAssetTrack *videoAsset =
+ [asset
+ tracksWithMediaType:
+ AVMediaTypeVideo]
+ .firstObject;
+ AVMutableCompositionTrack
+ *videoCompTrack = [mixComposition
+ addMutableTrackWithMediaType:
+ AVMediaTypeVideo
+ preferredTrackID:
+ kCMPersistentTrackID_Invalid];
+ [videoCompTrack
+ insertTimeRange:
+ CMTimeRangeMake(
+ kCMTimeZero,
+ videoAsset
+ .timeRange
+ .duration)
+ ofTrack:
+ videoAsset
+ atTime:
+ kCMTimeZero
+ error:
+ nil];
+
+ AVAssetTrack *audioAsset =
+ [audioAss
+ tracksWithMediaType:
+ AVMediaTypeAudio]
+ .firstObject;
+ AVMutableCompositionTrack
+ *audioCompTrack = [mixComposition
+ addMutableTrackWithMediaType:
+ AVMediaTypeAudio
+ preferredTrackID:
+ kCMPersistentTrackID_Invalid];
+ [audioCompTrack
+ insertTimeRange:
+ CMTimeRangeMake(
+ kCMTimeZero,
+ videoAsset
+ .timeRange
+ .duration)
+ ofTrack:
+ audioAsset
+ atTime:
+ kCMTimeZero
+ error:
+ nil];
+
+ handler([AVPlayerItem
+ playerItemWithAsset:
+ mixComposition]);
+ });
+ }];
+ });
+ }];
+
+#endif
+
+ // NSDictionary *urlAssetOptions =
+ // @{AVURLAssetPreferPreciseDurationAndTimingKey: [NSNumber
+ // numberWithBool:NO]};
+ //
+ // AVMutableComposition *composition = [AVMutableComposition
+ // composition];
+ //
+ //
+ //// NSLog(@"CHICKEN URL:%@",[audio objectForKey:@"uri"]);
+ //// NSURL *audioUrl = [NSURL URLWithString:[audio
+ /// objectForKey:@"uri"]]; / AVURLAsset *audioAsset = [AVURLAsset
+ /// URLAssetWithURL:audioUrl options:urlAssetOptions];
+ ////
+ //// AVMutableCompositionTrack *audioTrack = [composition
+ /// addMutableTrackWithMediaType:AVMediaTypeAudio
+ /// preferredTrackID:kCMPersistentTrackID_Invalid]; / [audioTrack
+ /// insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
+ /// ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio]
+ /// objectAtIndex:0] atTime:kCMTimeZero error:nil];
+ //
+ //
+ //// NSURL *videoUrl = [NSURL URLWithString:@"http://..."];
+ //// AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoUrl
+ /// options:urlAssetOptions];
+ //
+ // AVMutableCompositionTrack *videoTrack = [composition
+ // addMutableTrackWithMediaType:AVMediaTypeVideo
+ // preferredTrackID:kCMPersistentTrackID_Invalid]; [videoTrack
+ // insertTimeRange:CMTimeRangeMake(kCMTimeZero,
+ // videoAsset.timeRangeduration) ofTrack:[[videoAsset
+ // tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
+ // atTime:kCMTimeZero error:nil];
+ //
+ // AVMutableCompositionTrack *videoCompTrack = [mixComposition
+ // addMutableTrackWithMediaType:AVMediaTypeVideo
+ // preferredTrackID:kCMPersistentTrackID_Invalid];
+ // [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,
+ // videoAsset.timeRange.duration)
+ // ofTrack:videoAsset
+ // atTime:kCMTimeZero
+ // error:nil];
+ //
+ //
+
+ // AVPlayerItem *playerItem = [AVPlayerItem
+ // playerItemWithAsset:composition];
+
+ // AUDIO SOURCE END
+
+ // [self playerItemPrepareText:asset
+ // assetOptions:assetOptions
+ // withCallback:handler];
+
+#if (DO_MIX_COMPOSITION)
+ // handler([AVPlayerItem playerItemWithAsset:mixComposition]);
+#else
+ handler([AVPlayerItem playerItemWithAsset:asset]);
+#endif
return;
} else if (isAsset) {
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
@@ -1220,6 +1667,9 @@ - (void)setProgressUpdateInterval:(float)progressUpdateInterval
- (void)removePlayerLayer
{
+ // ZEROLABS 1-liner
+ if (!_playerLayer) return;
+
[_playerLayer removeFromSuperlayer];
if (_playerLayerObserverSet) {
[_playerLayer removeObserver:self forKeyPath:readyForDisplayKeyPath];
@@ -1313,6 +1763,8 @@ - (void)layoutSubviews
- (void)removeFromSuperview
{
+ inView = false; // ZEROLABS
+
[_player pause];
if (_playbackRateObserverRegistered) {
[_player removeObserver:self forKeyPath:playbackRate context:nil];
@@ -1338,4 +1790,11 @@ - (void)removeFromSuperview
[super removeFromSuperview];
}
+// ZEROLABS method
+- (void)performSendLoadUpdate:(NSString *)Map format:(NSString*)format {
+ if (self.onVideoLoadUpdate) {
+ self.onVideoLoadUpdate(@{@"target": self.reactTag, @"frag":Map, @"format":format});
+ }
+}
+
@end
diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m
index aa3c46705f..9455ad70ac 100644
--- a/ios/Video/RCTVideoManager.m
+++ b/ios/Video/RCTVideoManager.m
@@ -1,7 +1,8 @@
#import "RCTVideoManager.h"
#import "RCTVideo.h"
-#import
+// ZEROLABS: AVFoundation moved up for some reason
#import
+#import
@implementation RCTVideoManager
@@ -19,6 +20,12 @@ - (dispatch_queue_t)methodQueue
return dispatch_get_main_queue();
}
+// ZEROLABS begin
+RCT_EXPORT_VIEW_PROPERTY(audioSrc, NSDictionary);
+RCT_EXPORT_VIEW_PROPERTY(sendLoadUpdate, BOOL);
+RCT_EXPORT_VIEW_PROPERTY(onVideoLoadUpdate, RCTBubblingEventBlock);
+// ZEROLABS end
+
RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
diff --git a/ios/Video/RCTVideoPlayerViewController.h b/ios/Video/RCTVideoPlayerViewController.h
index 99b1349b2f..f6b6e2ba61 100644
--- a/ios/Video/RCTVideoPlayerViewController.h
+++ b/ios/Video/RCTVideoPlayerViewController.h
@@ -6,9 +6,10 @@
// Copyright © 2016 Facebook. All rights reserved.
//
-#import
#import "RCTVideo.h"
#import "RCTVideoPlayerViewControllerDelegate.h"
+// ZEROLABS: AVKit moved to end for some reason
+#import
@interface RCTVideoPlayerViewController : AVPlayerViewController
@property (nonatomic, weak) id rctDelegate;
diff --git a/ios/Video/RCTVideoPlayerViewControllerDelegate.h b/ios/Video/RCTVideoPlayerViewControllerDelegate.h
index e84b3f525a..79f761616c 100644
--- a/ios/Video/RCTVideoPlayerViewControllerDelegate.h
+++ b/ios/Video/RCTVideoPlayerViewControllerDelegate.h
@@ -1,5 +1,6 @@
-#import
+// ZEROLABS: Put AVKit first for some reason
#import "AVKit/AVKit.h"
+#import
@protocol RCTVideoPlayerViewControllerDelegate
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController;
diff --git a/jsdoc.md b/jsdoc.md
new file mode 100644
index 0000000000..aedd5447d7
--- /dev/null
+++ b/jsdoc.md
@@ -0,0 +1,3 @@
+
+
+### Table of Contents
diff --git a/jsdoc_private.md b/jsdoc_private.md
new file mode 100644
index 0000000000..aedd5447d7
--- /dev/null
+++ b/jsdoc_private.md
@@ -0,0 +1,3 @@
+
+
+### Table of Contents
diff --git a/package.json b/package.json
index 92366be35b..29d4e98804 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
- "name": "react-native-video",
- "version": "3.2.1",
+ "name": "@zerolabs/react-native-video",
+ "version": "0.0.8",
"description": "A element for react-native",
"main": "Video.js",
"license": "MIT",