Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for large video file uploads (tus.io) #46

Merged
merged 70 commits into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
963e7b9
Adding Tus .NET implementation and changing the .NET version to 4 to …
juniorxsound Sep 28, 2018
2af780b
Adding a method to request an upload using the `TusClient()`
juniorxsound Sep 28, 2018
592e69f
Merge branch 'master' into tus-upload
juniorxsound Oct 2, 2018
4e54e8f
Setting up initial arch sturctue for the Vimeo Tus uploader
juniorxsound Oct 3, 2018
0705197
Improving VideoChunk class and finishing byte reading
juniorxsound Oct 3, 2018
4064cb7
Calling the VimeoUploader from the VimeoApi class - and it works 🤩
juniorxsound Oct 3, 2018
9cff299
Making the VimeoUploader upload videos using the Vimeo tus API
juniorxsound Oct 4, 2018
f386cda
Testing long recording and implementing more events
juniorxsound Oct 4, 2018
6c4ad8c
Adding events, proper styling and splitting VideoChunk from VimeoUplo…
juniorxsound Oct 5, 2018
d6b52c6
Hiding all components additional components in recorder scene from th…
juniorxsound Oct 5, 2018
0246e39
Hiding video controller from the inspector as well
juniorxsound Oct 5, 2018
c242d0e
Fixing typo in file name
juniorxsound Oct 5, 2018
77a0b23
Implementing VimeoUploader into the VimeoPublisher class
juniorxsound Oct 5, 2018
bb82d0c
Style formatting for all changed files
juniorxsound Oct 5, 2018
741a516
Implementing the missing events for the VimeoUploader
juniorxsound Oct 15, 2018
fbdf947
Changing the architecture so VimeoUploader extends the VimeoApi inste…
juniorxsound Oct 16, 2018
4e0c477
Setting the video information right after a tus video resource has be…
juniorxsound Oct 16, 2018
2427d90
Cleaning up the VimeoUploader refactor
juniorxsound Oct 16, 2018
1be6de6
Fixing the message sent on upload complete, and making sure the tests…
juniorxsound Oct 17, 2018
6e9a088
Deleting all legacy upload code from VimeoApi 🛠
juniorxsound Oct 17, 2018
cd23dcb
Moving the OnUploadProgress and OnUploadComplete events from the Api …
juniorxsound Oct 17, 2018
d684763
Minor styling fixes in VimeoPublisher and VimeoUploader
juniorxsound Oct 17, 2018
65b761e
Changing the RequestComplete event to OnUploadInit
juniorxsound Oct 17, 2018
b3d586b
Adding getters and unit testing for all variables in VideoChunk.cs
juniorxsound Oct 17, 2018
36b9857
Setting up the VimeoUploader unit testing and getters for all private…
juniorxsound Oct 17, 2018
f8b63e3
Destorying the gameobjects properly in unit tests
juniorxsound Oct 17, 2018
6ab74a9
Add new default to test config
caseypugh Oct 18, 2018
ebbf9dd
Resolve issues with regex matching (and add tests)
caseypugh Oct 18, 2018
94ac29a
Added more tests to VimeoUploader and VideoChunk
caseypugh Oct 18, 2018
c540ad4
Include particle system so that the Recorder scene works
caseypugh Oct 18, 2018
37af769
Changing naming convention to m_camelCase for private and camelCase f…
juniorxsound Oct 18, 2018
ca18fda
Getting rid of unneeded argument in CreateChunks in VimeoUploader and…
juniorxsound Oct 18, 2018
7e57f3e
Removing the tus response event after the video resource was created …
juniorxsound Oct 18, 2018
6ab4a89
Fixing unreachable code block and and unit test for it
juniorxsound Oct 18, 2018
5e61dd1
Testing uploading & adding to folder
caseypugh Oct 18, 2018
49a7ce4
DRYing up some reusable logic
caseypugh Oct 18, 2018
b51816c
Nicer handling of VimeoPublisher when video doesn't exist on hard drive
juniorxsound Oct 18, 2018
072d30b
Fix test on Windows
caseypugh Oct 18, 2018
4b019e9
Moving the response handling to VimeoVideo and deleting the functions…
juniorxsound Oct 18, 2018
a1e7eee
Writing a generic VimeoVideo that supports the legacy style and tus s…
juniorxsound Oct 19, 2018
59ac6e3
decrease timeout to 30s
caseypugh Oct 19, 2018
c2f8125
DRY up all the response handling
caseypugh Oct 19, 2018
8cac312
remove duplicate error log
caseypugh Oct 19, 2018
c0528fa
Removed OnPatchComplete and updated VimeoPublisher
caseypugh Oct 19, 2018
2ed7f59
also remove event from VimeoApi
caseypugh Oct 19, 2018
a67bc69
Multiple upload testing (and add support for paging in api)
caseypugh Oct 19, 2018
2846a06
Adding chunk based upload progress so the progress bar is responsive …
juniorxsound Oct 19, 2018
f62acef
Add optional argument to the VimeoPublisher for easier testing of dif…
juniorxsound Oct 22, 2018
7971c3f
Changing the `VimeoUploader` Queue to a List so it stores all the chu…
juniorxsound Oct 22, 2018
93bb99a
Implementing the onProgress in the VimeoUploader and DRY'ing VideoChunk
juniorxsound Oct 22, 2018
7b4ec62
Changing naming convention in VimeoRecorder
juniorxsound Oct 22, 2018
c96d027
Adding a bunch of tests to `VimeoUploader` and `VideoChunk` and handl…
juniorxsound Oct 22, 2018
3af05f4
Adding `VideoChunk` tests
juniorxsound Oct 22, 2018
253b068
Adding VimeoUploader test for uploading a very big file without recor…
juniorxsound Oct 23, 2018
1f946de
Changing the gitignore so it ignores all videos in the test folder
juniorxsound Oct 23, 2018
a71fc12
Fix name to specify the type of test
caseypugh Oct 23, 2018
7b4f825
adjust naming to specify platform and unity version
caseypugh Oct 23, 2018
7d4c412
Adding temporary naming / privacy workaround so the VimeoUploader cou…
juniorxsound Oct 23, 2018
17a0e9c
AVPro Movie Capture test
caseypugh Oct 23, 2018
464e14a
Merge branch 'tus-upload' of https://github.com/vimeo/vimeo-unity-sdk…
caseypugh Oct 23, 2018
7a0b413
Move version error check into _before
caseypugh Oct 23, 2018
2a30f4e
check that fileinfo is not null
caseypugh Oct 23, 2018
86ef91e
Fixes unhandled onProgress error when calling GetBytesUploaded
juniorxsound Oct 23, 2018
a932b46
Moving all the tests into an Editor folder so they don't get included…
juniorxsound Oct 23, 2018
ee67c7b
Move tests back into root folder
caseypugh Oct 23, 2018
237a52f
Ignore all mp4 when commiting
juniorxsound Oct 23, 2018
5e74e22
Add support for resumable chunks (for spotty internet connections)
caseypugh Oct 23, 2018
97d5f3d
Updating it so it should ignore the meta file created for the video a…
juniorxsound Oct 24, 2018
1ebaa82
Updating gitignore to ignore all mp4's and meta files created by Unity
juniorxsound Oct 24, 2018
9732b46
Move version init to _Before
caseypugh Oct 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion Assets/Tests/Play/VimeoRecorderPlayTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,25 @@ public IEnumerator Can_Record_Video_From_MainCamera_With_Valid_Token()

yield return new WaitUntil(()=> uploaded == true);
Assert.IsTrue(uploaded);
}
}

[UnityTest]
[Timeout(100000)]
public IEnumerator Can_Record_And_Upload_Multiple_Chunks()
{
UnityEngine.TestTools.LogAssert.NoUnexpectedReceived();

recorder.videoName = "MainCamera Test " + recorder.videoName;
recorder.defaultVideoInput = VideoInputType.Camera;
recorder.customByteChunkSize = 50000;
recorder.SignIn(VALID_RECORDING_TOKEN);
recorder.BeginRecording();

recorder.OnUploadComplete += UploadComplete;

yield return new WaitUntil(()=> uploaded == true);
Assert.IsTrue(uploaded);
}

private void UploadComplete()
{
Expand Down
24 changes: 12 additions & 12 deletions Assets/Tests/Unit/VimeoUploaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,23 @@ public void CreateChunks_Makes_One_Chunk_For_Small_Files()
uploader.Init("xtokenx", 100000000);
uploader.CreateChunks(testFile, "xxx");
Assert.AreEqual(uploader.chunks.Count, 1);
Assert.AreEqual(uploader.chunks.Peek().chunkSize, testFileSize);
Assert.AreEqual(uploader.chunks[0].chunkSize, testFileSize);
}

[Test]
public void CreateChunks_Properly_Sets_Up_Chunk()
{
uploader.CreateChunks(testFile, "upload url");
Assert.AreEqual(uploader.chunks.Peek().url, "upload url");
Assert.AreEqual(uploader.chunks.Peek().filePath, new FileInfo(TEST_IMAGE_PATH).FullName);
Assert.AreEqual(uploader.chunks[0].url, "upload url");
Assert.AreEqual(uploader.chunks[0].filePath, new FileInfo(TEST_IMAGE_PATH).FullName);
}

[Test]
public void CreateChunks_Sets_Size_Of_Each_Chunk()
{
uploader.Init("xtokenx", 1234);
uploader.CreateChunks(testFile, "xxx");
Assert.AreEqual(uploader.chunks.Peek().chunkSize, 1234);
Assert.AreEqual(uploader.chunks[0].chunkSize, 1234);
}

[Test]
Expand All @@ -99,15 +99,15 @@ public void CreateChunks_Last_Chunk_Is_Remainder()
);
}

[Test]
public void UploadNextChunk_Dequeues()
{
uploader.CreateChunks(testFile, "xxx");
// [Test]
// public void UploadNextChunk_Dequeues()
// {
// uploader.CreateChunks(testFile, "xxx");

int length = uploader.chunks.Count;
uploader.UploadNextChunk();
Assert.AreEqual(uploader.chunks.Count, length - 1);
}
// int length = uploader.chunks.Count;
// uploader.UploadNextChunk();
// Assert.AreEqual(uploader.chunks.Count, length - 1);
// }

// TODO setup way to load mock json file
// [Test]
Expand Down
8 changes: 7 additions & 1 deletion Assets/Vimeo/Scripts/Recorder/VimeoRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ public class VimeoRecorder : RecorderSettings
public bool isRecording = false;
public bool isUploading = false;
public float uploadProgress = 0;
private int m_customByteChunkSize = 1024 * 1024 * 128;
public int customByteChunkSize {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just call this byteChunkSize ?

set {
m_customByteChunkSize = value;
}
}

public void Start()
{
Expand Down Expand Up @@ -74,7 +80,7 @@ public void PublishVideo()

if (publisher == null) {
publisher = gameObject.AddComponent<VimeoPublisher>();
publisher.Init(this);
publisher.Init(this, m_customByteChunkSize);

publisher.OnUploadProgress += UploadProgress;
publisher.OnNetworkError += NetworkError;
Expand Down
81 changes: 59 additions & 22 deletions Assets/Vimeo/Scripts/Services/VimeoUploader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class VimeoUploader : VimeoApi
public event UploadAction OnUploadProgress;
public event RequestAction OnUploadComplete;

private Queue<VideoChunk> m_chunks;
public Queue<VideoChunk> chunks {
private List<VideoChunk> m_chunks;
public List<VideoChunk> chunks {
get {
return m_chunks;
}
Expand Down Expand Up @@ -61,10 +61,13 @@ public float uploadProgress {
return m_uploadProgress;
}
}
private int currentChunkIndex = 0;
private VideoChunk lastChunk;

public void Init(string _token, int _maxChunkByteSize = 1024 * 1024 * 128)
{
m_chunks = new Queue<VideoChunk>();
currentChunkIndex = 0;
m_chunks = new List<VideoChunk>();
token = _token;
m_maxChunkSize = _maxChunkByteSize;
}
Expand All @@ -79,8 +82,19 @@ private void RequestComplete(string response)
m_vimeoUrl = rawJSON["link"].Value;
CreateChunks(m_fileInfo, tusUploadLink);

VideoChunk firstChunk = m_chunks.Dequeue();
firstChunk.Upload();
VideoChunk currentChunk = GetNextChunk();
currentChunk.Upload();
}
juniorxsound marked this conversation as resolved.
Show resolved Hide resolved

private VideoChunk GetNextChunk()
{
if (HasChunksLeftToUpload()) {
VideoChunk currentChunk = m_chunks[currentChunkIndex];
RegisterChunkEvents(currentChunk);
return currentChunk;
}

juniorxsound marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

public void Upload(string _file)
Expand All @@ -92,30 +106,44 @@ public void Upload(string _file)
StartCoroutine(RequestTusResource("me/videos", m_fileInfo.Length));
}

private void OnCompleteChunk(VideoChunk chunk, string msg)
private void OnCompleteChunk(VideoChunk chunk, string latestUploadedByte)
{
//Emit the event
if (OnChunckUploadComplete != null) {
OnChunckUploadComplete(chunk, msg);
// OnUploadProgress("Uploading", 1f);
OnChunckUploadComplete(chunk, latestUploadedByte);
}

Destroy(chunk);

UploadNextChunk();
currentChunkIndex++;
UploadNextChunk(GetNextChunk());
}

private void OnUploadChunkProgress(VideoChunk chunk, float progress)
{
//Calculate the addition of each chunk to the total file length (in bytes)
m_uploadProgress = (chunks.Count + 1) * ((progress * (float)chunk.chunkSize) / (float)m_fileInfo.Length);
if (OnUploadProgress != null) {
OnUploadProgress("Uploading", m_uploadProgress);
}
}

private void RegisterChunkEvents(VideoChunk chunk)
{
chunk.OnChunkUploadComplete += OnCompleteChunk;
chunk.OnChunkUploadError += OnChunkError;
chunk.OnChunkUploadProgress += OnUploadChunkProgress;
}

private void DisposeChunkEvents(VideoChunk chunk)
{
chunk.OnChunkUploadComplete -= OnCompleteChunk;
chunk.OnChunkUploadError -= OnChunkError;
chunk.OnChunkUploadProgress -= OnUploadChunkProgress;
}

private void OnChunkError(VideoChunk chunk, string err)
{
if (OnChunckUploadError != null) {
if (OnChunckUploadError != null && chunk != null) {
OnChunckUploadError(chunk, err);
}
}
Expand All @@ -127,7 +155,7 @@ public void CreateChunks(FileInfo fileInfo, string tusUploadLink)

for (int i = 0; i < m_numChunks; i++) {
int indexByte = m_maxChunkSize * i;
VideoChunk chunk = this.gameObject.AddComponent<VideoChunk>();
VideoChunk chunk = gameObject.AddComponent<VideoChunk>();
chunk.hideFlags = HideFlags.HideInInspector;

//If we are at the last chunk set the max chunk size to the fractional remainder
Expand All @@ -137,21 +165,22 @@ public void CreateChunks(FileInfo fileInfo, string tusUploadLink)
} else {
chunk.Init(indexByte, tusUploadLink, fileInfo.FullName, m_maxChunkSize);
}

chunk.OnChunkUploadComplete += OnCompleteChunk;
chunk.OnChunkUploadError += OnChunkError;
chunk.OnChunkUploadProgress += OnUploadChunkProgress;
m_chunks.Enqueue(chunk);
m_chunks.Add(chunk);
}
}

public void UploadNextChunk()
public void UploadNextChunk(VideoChunk currentChunk)
{
//Make sure the queue is not empty
if (m_chunks.Count != 0) {
VideoChunk currentChunk = m_chunks.Dequeue();

if (lastChunk != null) {
DisposeChunkEvents(lastChunk);
}

//Make sure there are still chunks to upload
if (currentChunk != null) {
currentChunk.Upload();

//Store the reference to latest uploaded chunk to de-register events
lastChunk = currentChunk;
} else {
if (OnUploadProgress != null) {
OnUploadProgress("UploadComplete", 1f);
Expand All @@ -161,5 +190,13 @@ public void UploadNextChunk()
}
}
}

private bool HasChunksLeftToUpload()
{
if (currentChunkIndex < m_chunks.Count) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a more future proof way of checking would be to create a method called GetTotalRemainingChunks which loops through to check status. e.g.

private bool HasPendingChunksToUpload()
{
   return GetTotalRemainingChunks() == 0;
}

return true;
}
return false;
}
}
}
15 changes: 0 additions & 15 deletions Assets/Vimeo/Scripts/Utils/VideoChunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,6 @@ private IEnumerator SendTusRequest()
DisposeBytes();
}

private void Update() {
caseypugh marked this conversation as resolved.
Show resolved Hide resolved

//Make sure that we are uploading this chunk and the request is not null
if (m_isUploadingChunk && chunkUploadRequest != null) {

//We only emit events if the upload started (i.e > 0) or it hasn't finished yet (i.e < 1)
if (chunkUploadRequest.uploadProgress > 0.0f &&
chunkUploadRequest.uploadProgress < 1.0f) {
OnChunkUploadProgress(this, chunkUploadRequest.uploadProgress);
}

}

}

public void Upload()
{
StartCoroutine(SendTusRequest());
Expand Down