Skip to content

Commit

Permalink
fix: export more data from the HTTP+MP4 pipeline
Browse files Browse the repository at this point in the history
Expose details about response headers and stream end so that
the player can use that data.
  • Loading branch information
steabert committed Jan 15, 2025
1 parent 9e1d7ed commit 5f36202
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 21 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 14.0.0-beta.4
## 14.0.0-beta.5

- Added some missing info to the HTTP MP4 pipeline.
- Updated documentation regarding release flow, which now
requires manual update of version and changelog.
- Fixed player non-relative imports from within the package.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "media-stream-library",
"version": "14.0.0-beta.4",
"version": "14.0.0-beta.5",
"description": "Media libraries for Node and the Web.",
"repository": {
"type": "git",
Expand Down
16 changes: 8 additions & 8 deletions src/player/HttpMp4Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,13 @@ export const HttpMp4Video: React.FC<HttpMp4VideoProps> = ({
const videoEl = videoRef.current

if (src !== undefined && src.length > 0 && videoEl !== null) {
const endedCallback = () => {
__onEndedRef.current?.()
}
logDebug('create pipeline', src)
const newPipeline = new HttpMp4Pipeline({
uri: src,
mediaElement: videoEl,
})
setPipeline(newPipeline)

newPipeline.onServerClose = endedCallback

return () => {
logDebug('close pipeline and clear video')
newPipeline.close()
Expand All @@ -162,13 +157,18 @@ export const HttpMp4Video: React.FC<HttpMp4VideoProps> = ({

useEffect(() => {
if (play && pipeline && !fetching) {
pipeline.onHeaders = (headers) => {
const endedCallback = () => {
__onEndedRef.current?.()
}
pipeline.start().then(({ headers, finished }) => {
__sensorTmRef.current = parseTransformHeader(
headers.get('video-sensor-transform') ??
headers.get('video-metadata-transform')
)
}
pipeline.start()
finished.finally(() => {
endedCallback()
})
})
logDebug('initiated data fetching')
setFetching(true)
}
Expand Down
30 changes: 19 additions & 11 deletions src/streams/http-mp4-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,14 @@ export interface HttpMp4Config {
*
* A pipeline that connects to an HTTP server and can process an MP4 data stream
* that is then sent to a HTML video element
*
* Handlers that can be set on the pipeline:
* - `onServerClose`: called when the server closes the connection
*/
export class HttpMp4Pipeline {
public onHeaders?: (headers: Headers) => void
public onServerClose?: () => void

private abortController?: AbortController
private downloadedBytes: number = 0
private options?: RequestInit
private readonly mediaElement: HTMLVideoElement
private uri: string
public readonly mediaElement: HTMLVideoElement
public streamStart?: number

constructor(config: HttpMp4Config) {
const { uri, options, mediaElement } = config
Expand All @@ -36,13 +31,16 @@ export class HttpMp4Pipeline {
this.mediaElement = mediaElement
}

/** Initiates the stream and resolves when the media stream has completed. */
public async start(msgHandler?: (msg: IsomMessage) => void) {
/** Initiates the stream and resolves when the media stream has completed.
* Returns the original response headers and a result promise. */
public async start(
msgHandler?: (msg: IsomMessage) => void
): Promise<{ headers: Headers; finished: Promise<void> }> {
this.abortController?.abort('stream restarted')

this.abortController = new AbortController()

const { ok, status, statusText, headers, body } = await fetch(this.uri, {
const { ok, headers, status, statusText, body } = await fetch(this.uri, {
signal: this.abortController.signal,
...this.options,
})
Expand Down Expand Up @@ -70,7 +68,8 @@ export class HttpMp4Pipeline {
mseSink.onMessage = msgHandler
}

body
this.streamStart = performance.now()
const finished = body
.pipeThrough(adapter)
.pipeTo(mseSink.writable)
.then(() => {
Expand All @@ -79,6 +78,8 @@ export class HttpMp4Pipeline {
.catch((err) => {
logDebug(`http-mp4 pipeline ended: ${err}`)
})

return { headers, finished }
}

public close() {
Expand All @@ -101,6 +102,13 @@ export class HttpMp4Pipeline {
return this.downloadedBytes
}

public get bitrate() {
return this.streamStart !== undefined
? (8 * this.downloadedBytes) /
((performance.now() - this.streamStart) / 1000)
: 0
}

/** Refresh the stream and passes the captured MP4 data to the provided
* callback. Capture can be ended by calling the returned trigger, or
* if the buffer reaches max size. */
Expand Down

0 comments on commit 5f36202

Please sign in to comment.