Skip to content
This repository has been archived by the owner on Jan 29, 2019. It is now read-only.

Commit

Permalink
Merge pull request #65 from imbcmdth/array-buffer
Browse files Browse the repository at this point in the history
Fix handling of arraybuffers
  • Loading branch information
imbcmdth committed Jan 18, 2016
2 parents ea9081b + c709595 commit 224f67b
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 119 deletions.
9 changes: 6 additions & 3 deletions src/transmuxer_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ var wireTransmuxerEvents = function (transmuxer) {
// transfer ownership of the underlying ArrayBuffer instead of doing a copy to save memory
// ArrayBuffers are transferable but generic TypedArrays are not
// see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
segment.data = segment.data.buffer;
var typedArray = segment.data;
segment.data = typedArray.buffer;
postMessage({
action: 'data',
segment: segment
segment: segment,
byteOffset: typedArray.byteOffset,
byteLength: typedArray.byteLength
}, [segment.data]);
});

Expand Down Expand Up @@ -87,7 +90,7 @@ var messageHandlers = {
*/
push: function (data) {
// Cast array buffer to correct type for transmuxer
var segment = new Uint8Array(data.data);
var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
transmuxer.push(segment);
},
/**
Expand Down
18 changes: 15 additions & 3 deletions src/videojs-media-sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,8 @@ addTextTrackData = function (sourceHandler, captionArray, metadataArray) {
segment = event.data.segment,
nativeMediaSource = this.mediaSource_.mediaSource_;

// Cast to type
segment.data = new Uint8Array(segment.data);
// Cast ArrayBuffer to TypedArray
segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);

// If any sourceBuffers have not been created, do so now
if (segment.type === 'video') {
Expand Down Expand Up @@ -508,7 +508,19 @@ addTextTrackData = function (sourceHandler, captionArray, metadataArray) {
// Start the internal "updating" state
this.bufferUpdating_ = true;

this.transmuxer_.postMessage({action: 'push', data: segment.buffer}, [segment.buffer]);
this.transmuxer_.postMessage({
action: 'push',
// Send the typed-array of data as an ArrayBuffer so that
// it can be sent as a "Transferable" and avoid the costly
// memory copy
data: segment.buffer,

// To recreate the original typed-array, we need information
// about what portion of the ArrayBuffer it was a view into
byteOffset: segment.byteOffset,
byteLength: segment.byteLength
},
[segment.buffer]);
this.transmuxer_.postMessage({action: 'flush'});
},
remove: function(start, end) {
Expand Down
211 changes: 98 additions & 113 deletions test/media-sources_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,35 @@
},
initializeNativeSourceBuffers,
oldFlashTransmuxer,
MockSegmentParser;
MockSegmentParser,

// Create a WebWorker-style message that simulates data being emitted
// by the transmuxer
createDataMessage = function(type, typedArray, extraObject) {
var message = {
data: {
action: 'data',
segment: {
type: type,
data: typedArray.buffer
},
byteOffset: typedArray.byteOffset,
byteLength: typedArray.byteLength
}
};

return Object.keys(extraObject || {}).reduce(function (obj, key) {
obj.data.segment[key] = extraObject[key];
return obj;
}, message);
},

// Create a WebWorker-style message that signals the transmuxer is done
doneMessage = {
data: {
action: 'done'
}
};

// Override default webWorkerURI for karma
if (!videojs.MediaSource.webWorkerURI) {
Expand Down Expand Up @@ -115,32 +143,13 @@
// native source buffers
initializeNativeSourceBuffers = function(sourceBuffer) {
// initialize an audio source buffer
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'audio',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('audio', new Uint8Array(1)));
// initialize a video source buffer
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'video',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('video', new Uint8Array(1)));

// instruct the transmuxer to flush the "data" it has buffered so
// far
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'done'
}
});
sourceBuffer.transmuxer_.onmessage(doneMessage);
};

test('creates mp4 source buffers for mp2t segments', function(){
Expand Down Expand Up @@ -299,46 +308,72 @@
equal(mp2tSegments[0][0], data[0], 'did not alter the segment');

// an init segment
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'video',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('video', new Uint8Array(1)));

// Source buffer is not created until after the muxer starts emitting data
mediaSource.mediaSource_.sourceBuffers[0].appendBuffer = function(segment) {
mp4Segments.push(segment);
};

// a media segment
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'video',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('audio', new Uint8Array(1)));

// Segments are concatenated
equal(mp4Segments.length, 0, 'segments are not appended until after the `done` message');

// send `done` message
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'done',
}
});
sourceBuffer.transmuxer_.onmessage(doneMessage);

// Segments are concatenated
equal(mp4Segments.length, 1, 'appended the segments');
});

test('handles typed-arrays that are subsets of their underlying buffer', function(){
var
mp2tSegments = [],
mp4Segments = [],
buffer = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
data = buffer.subarray(5, 7),
mediaSource,
sourceBuffer;
mediaSource = new videojs.MediaSource();
sourceBuffer = mediaSource.addSourceBuffer('video/mp2t');

sourceBuffer.transmuxer_.postMessage = function(segment) {
if (segment.action === 'push') {
var buffer = new Uint8Array(segment.data, segment.byteOffset, segment.byteLength);
mp2tSegments.push(buffer);
}
};

sourceBuffer.appendBuffer(data);

equal(mp2tSegments.length, 1, 'emitted the fragment');
equal(mp2tSegments[0].length, 2, 'correctly handled a typed-array that is a subset');
equal(mp2tSegments[0][0], 5, 'fragment contains the correct first byte');
equal(mp2tSegments[0][1], 6, 'fragment contains the correct second byte');

// an init segment
sourceBuffer.transmuxer_.onmessage(createDataMessage('video', data));

// Source buffer is not created until after the muxer starts emitting data
mediaSource.mediaSource_.sourceBuffers[0].appendBuffer = function(segment) {
mp4Segments.push(segment);
};

// Segments are concatenated
equal(mp4Segments.length, 0, 'segments are not appended until after the `done` message');

// send `done` message
sourceBuffer.transmuxer_.onmessage(doneMessage);

// Segments are concatenated
equal(mp4Segments.length, 1, 'emitted the fragment');
equal(mp4Segments[0].length, 2, 'correctly handled a typed-array that is a subset');
equal(mp4Segments[0][0], 5, 'fragment contains the correct first byte');
equal(mp4Segments[0][1], 6, 'fragment contains the correct second byte');
});

test('handles codec strings in reverse order', function() {
var mediaSource = new videojs.MediaSource(),
sourceBuffer = mediaSource.addSourceBuffer('video/mp2t; codecs="mp4a.40.5,avc1.64001f"');
Expand All @@ -356,15 +391,7 @@
var mediaSource = new videojs.MediaSource(),
sourceBuffer = mediaSource.addSourceBuffer('video/mp2t; codecs="avc1.64001f,mp4a.40.5"');

sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'combined',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('combined', new Uint8Array(1)));
equal(mediaSource.mediaSource_.sourceBuffers[0].type,
'video/mp4;codecs="avc1.64001f,mp4a.40.5"',
'passed the codec along');
Expand All @@ -374,15 +401,7 @@
var mediaSource = new videojs.MediaSource(),
sourceBuffer = mediaSource.addSourceBuffer('video/mp2t; codecs="avc1.100.31,mp4a.40.5"');

sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'combined',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('combined', new Uint8Array(1)));
equal(mediaSource.mediaSource_.sourceBuffers[0].type,
'video/mp4;codecs="avc1.64001f,mp4a.40.5"',
'passed the codec along');
Expand All @@ -392,15 +411,7 @@
var mediaSource = new videojs.MediaSource(),
sourceBuffer = mediaSource.addSourceBuffer('video/mp2t');

sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'combined',
data: new Uint8Array(1).buffer
}
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('combined', new Uint8Array(1)));
equal(mediaSource.mediaSource_.sourceBuffers[0].type,
'video/mp4;codecs="avc1.4d400d,mp4a.40.2"',
'passed the codec along');
Expand Down Expand Up @@ -495,11 +506,7 @@
equal(updates, 0, 'no updates before a `done` message is received');
equal(updateends, 0, 'no updateends before a `done` message is received');

sourceBuffer.transmuxer_.onmessage({
data: {
action: 'done'
}
});
sourceBuffer.transmuxer_.onmessage(doneMessage);

// the video buffer begins updating first:
sourceBuffer.videoBuffer_.updating = true;
Expand Down Expand Up @@ -546,25 +553,14 @@
}
};
sourceBuffer.timestampOffset = 10;
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'video',
data: new Uint8Array(1),
captions: [{
startTime: 1,
endTime: 3,
text: 'This is an in-band caption'
}]
}
}
});
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'done'
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('video', new Uint8Array(1), {
captions:[{
startTime: 1,
endTime: 3,
text: 'This is an in-band caption'
}]
}));
sourceBuffer.transmuxer_.onmessage(doneMessage);

equal(types.length, 1, 'created one text track');
equal(types[0], 'captions', 'the type was captions');
Expand Down Expand Up @@ -611,21 +607,10 @@
}
};
sourceBuffer.timestampOffset = 10;
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'data',
segment: {
type: 'video',
data: new Uint8Array(1),
metadata: metadata
}
}
});
sourceBuffer.transmuxer_.onmessage({
data: {
action: 'done'
}
});
sourceBuffer.transmuxer_.onmessage(createDataMessage('video', new Uint8Array(1), {
metadata: metadata
}));
sourceBuffer.transmuxer_.onmessage(doneMessage);

equal(
sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType,
Expand Down

0 comments on commit 224f67b

Please sign in to comment.