MSESpy.js
Overview
This is a tool to spy on most MSE-related browser API calls. It mainly has been
used for debugging and reverse-engineering purposes on media-oriented
web-applications.
It logs and registers when any of the following actions take place:
-
A MediaSource object is instanciated
-
the following MediaSource methods are called:
MediaSource.prototype.addSourceBuffer
MediaSource.prototype.removeSourceBuffer
MediaSource.prototype.endOfStream
MediaSource.prototype.setLiveSeekableRange
MediaSource.prototype.clearLiveSeekableRange
MediaSource.isTypeSupported
MediaSource.prototype.addEventListener
MediaSource.prototype.removeEventListener
MediaSource.prototype.dispatchEvent
- Those MediaSource properties are get/set:
MediaSource.prototype.duration
MediaSource.prototype.onsourceopen
MediaSource.prototype.onsourceended
MediaSource.prototype.onsourceclose
MediaSource.prototype.sourceBuffers
MediaSource.prototype.activeSourceBuffers
MediaSource.prototype.readyState
- Those SourceBuffer methods are called:
SourceBuffer.prototype.appendBuffer
SourceBuffer.prototype.abort
SourceBuffer.prototype.remove
SourceBuffer.prototype.appendBuffer
SourceBuffer.prototype.addEventListener
SourceBuffer.prototype.removeEventListener
SourceBuffer.prototype.dispatchEvent
- Those SourceBuffer properties are get/set:
SourceBuffer.prototype.mode
SourceBuffer.prototype.timestampOffset
SourceBuffer.prototype.appendWindowStart
SourceBuffer.prototype.appendWindowEnd
SourceBuffer.prototype.onupdate
SourceBuffer.prototype.onupdatestart
SourceBuffer.prototype.onupdateend
SourceBuffer.prototype.onerror
SourceBuffer.prototype.onabort
SourceBuffer.prototype.updating
SourceBuffer.prototype.buffered
The registered data is:
-
the date at which the API has been called or the property as been interacted
with
-
the returned value for an API call or a property access
-
the argument(s) for API calls
-
the value set on properties
-
the context (this
) at the time of the call
-
the error if the API call threw
It can then be used to produced useful reports on how those APIs are exploited
by any application.
How to install it
Including the script directly
Because this is mainly a debugging application, the most straightforward way of
using it is just to copy the code of the compiled bundle
directly, and to copy-paste it into your console.
You will also have a global MSESpy
object, through which you can call any
API defined here.
Example:
MSESpy.start();
const MSECalls = MSESpy.getMSECalls();
This configuration can also be useful by including this script automatically in
multimedia pages. This can be done through userscript managers, such as
Tampermonkey for Chrome
or Greasemonkey for Firefox
.
Adding as a dependency
You can also add this module as a dependency through npm:
npm install mse-spy
Then use this module as you want.
import MSESpy from "mse-spy";
MSESpy.start();
API
The API is basically as follow:
MSESpy.start();
console.log(MSESpy.getMSECalls());
console.log(MSESpy.resetMSECalls());
MSESpy.stop();
MSESpy.start();
MSESpy.Logger.onPropertyAccess = CustomLogger;
MSECalls object
The MSECalls object contains information about every call performed while the
spy was active.
It can be obtained by calling the MSESpy.getMSECalls()
API.
Here is its basic structure:
{
MediaSource: {
new: [
{
id: 1,
date: 1533722155401,
args: [],
response: mediaSource,
responseDate: 1533722155401,
error: someError,
errorDate: 1533722155401
}
],
methods: {
addSourceBuffer: [
{
self: mediaSource,
id: 4,
date: 1533722155401,
args: [],
response: sourceBuffer,
responseDate: 1533722155401,
error: someError,
errorDate: 1533722155401
}
]
},
properties: {
duration: {
get: [
{
self: mediaSource,
id: 3,
date: 1533722155401,
value: 10
}
],
set: [
{
self: mediaSource,
id: 2,
date: 1533722155401,
value: 15
}
]
}
}
},
SourceBuffer: {
new: [],
methods: {},
properties: {},
},
}
Custom Logger
If you don't like the default logging strategy or find it too verbose, a custom
Logger can be defined.
It is accessible through the MSESpy.Logger
object. All it contains are
several functions automatically called at various key points:
Logger = {
onPropertyAccess(pathString, value) {},
onSettingProperty(pathString, value) {},
onObjectInstanciation(objectName, args) {},
onObjectInstanciationError(objectName, error) {},
onObjectInstanciationSuccess(objectName, value) {},
onFunctionCall(pathName, args) {},
onFunctionCallError(pathName, error) {},
onFunctionCallSuccess(pathName, value) {},
};
Note: if the code above were to be implemented, you wouldn't have any logs
displaying in the console, as all functions declared here are empty.
You can look at src/utils/logger.js
for default implementations.
Left to do
The next steps would be to: