Skip to content

Commit

Permalink
Merge pull request #508 from shiguredo/feature/mp4-media-stream-vp8
Browse files Browse the repository at this point in the history
`Mp4MediaStream` の対応コーデックに VP8 を追加する
  • Loading branch information
sile authored Dec 6, 2024
2 parents 812fb5d + 6a1ee92 commit 8093dc4
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 26 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

## develop

- [ADD] `Mp4MediaStream` の対応コーデックに VP8 を追加する
- @sile
- [ADD] `Mp4MediaStream` の対応コーデックに AAC を追加する
- @sile

Expand Down
10 changes: 9 additions & 1 deletion packages/mp4-media-stream/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,18 @@ video.srcObject = stream

本ライブラリは Chrome や Edge 等の Chromium ベースのブラウザで動作します。

## 対応コーデック

- 映像:
- H.264
- VP8
- 音声:
- AAC
- Opus

## 未対応機能

以下の機能には現時点では対応していません:
- H.264 / Opus / AAC 以外のコーデックを含んだ MP4 の再生
- 再生開始位置の指定(シーク)
- 再生の一時停止・再開
- 数 GB を超える MP4 ファイルの再生
Expand Down
62 changes: 38 additions & 24 deletions packages/mp4-media-stream/src/mp4_media_stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ class Mp4MediaStream {
// JSON.parse() の結果では config.description の型は number[] となって期待とは異なるので
// ここで適切な型に変換している
config.description = new Uint8Array(config.description as object as number[])
if (config.description.byteLength === 0) {
// コーデックによっては description が存在しないので空なら削除する
config.description = undefined
}

if (!(await VideoDecoder.isConfigSupported(config)).supported) {
throw new Error(`Unsupported video decoder configuration: ${JSON.stringify(config)}`)
Expand Down Expand Up @@ -238,22 +242,29 @@ class Mp4MediaStream {
// JSON.parse() の結果では config.description の型は number[] となって期待とは異なるので
// ここで適切な型に変換している
config.description = new Uint8Array(config.description as object as number[])
if (config.description.byteLength === 0) {
// コーデックによっては description が存在しないので空なら削除する
config.description = undefined
}

const init = {
output: async (frame: VideoFrame) => {
if (player.canvas === undefined || player.canvasCtx === undefined) {
return
}

try {
player.canvas.width = frame.displayWidth
player.canvas.height = frame.displayHeight
player.canvasCtx.drawImage(frame, 0, 0)
if (player.canvas === undefined || player.canvasCtx === undefined) {
return
}

try {
player.canvas.width = frame.displayWidth
player.canvas.height = frame.displayHeight
player.canvasCtx.drawImage(frame, 0, 0)
} catch (error) {
// エラーが発生した場合には再生を停止する
await this.stopPlayer(playerId)
throw error
}
} finally {
frame.close()
} catch (error) {
// エラーが発生した場合には再生を停止する
await this.stopPlayer(playerId)
throw error
}
},
error: async (error: DOMException) => {
Expand Down Expand Up @@ -286,21 +297,24 @@ class Mp4MediaStream {
const config = this.wasmJsonToValue(configWasmJson) as AudioDecoderConfig
const init = {
output: async (data: AudioData) => {
if (player.audioInputNode === undefined) {
return
}

try {
const samples = new Float32Array(data.numberOfFrames * data.numberOfChannels)
data.copyTo(samples, { planeIndex: 0, format: 'f32' })
data.close()
if (player.audioInputNode === undefined) {
return
}

const timestamp = data.timestamp
player.audioInputNode.port.postMessage({ timestamp, samples }, [samples.buffer])
} catch (e) {
// エラーが発生した場合には再生を停止する
await this.stopPlayer(playerId)
throw e
try {
const samples = new Float32Array(data.numberOfFrames * data.numberOfChannels)
data.copyTo(samples, { planeIndex: 0, format: 'f32' })

const timestamp = data.timestamp
player.audioInputNode.port.postMessage({ timestamp, samples }, [samples.buffer])
} catch (e) {
// エラーが発生した場合には再生を停止する
await this.stopPlayer(playerId)
throw e
}
} finally {
data.close()
}
},
error: async (error: DOMException) => {
Expand Down
15 changes: 14 additions & 1 deletion packages/mp4-media-stream/wasm/src/mp4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use shiguredo_mp4::{
aux::SampleTableAccessor,
boxes::{
Avc1Box, FtypBox, HdlrBox, IgnoredBox, MoovBox, Mp4aBox, OpusBox, SampleEntry, StblBox,
TrakBox,
TrakBox, Vp08Box,
},
BaseBox, Decode, Either, Encode,
};
Expand Down Expand Up @@ -38,6 +38,15 @@ impl VideoDecoderConfig {
coded_height: b.visual.height,
}
}

pub fn from_vp08_box(b: &Vp08Box) -> Self {
Self {
codec: "vp8".to_owned(),
description: Vec::new(),
coded_width: b.visual.width,
coded_height: b.visual.height,
}
}
}

#[derive(Debug, Serialize)]
Expand Down Expand Up @@ -111,6 +120,7 @@ impl Track {

match sample_table.stbl_box().stsd_box.entries.first() {
Some(SampleEntry::Avc1(_)) => (),
Some(SampleEntry::Vp08(_)) => (),
Some(SampleEntry::Opus(_)) => (),
Some(SampleEntry::Mp4a(_)) => (),
Some(b) => {
Expand Down Expand Up @@ -201,6 +211,9 @@ impl Mp4 {
SampleEntry::Avc1(b) => {
video_configs.push(VideoDecoderConfig::from_avc1_box(b));
}
SampleEntry::Vp08(b) => {
video_configs.push(VideoDecoderConfig::from_vp08_box(b));
}
SampleEntry::Opus(b) => {
audio_configs.push(AudioDecoderConfig::from_opus_box(b));
}
Expand Down
4 changes: 4 additions & 0 deletions packages/mp4-media-stream/wasm/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ impl TrackPlayer {
let config = VideoDecoderConfig::from_avc1_box(b);
WasmApi::create_video_decoder(self.player_id, config).await
}
SampleEntry::Vp08(b) => {
let config = VideoDecoderConfig::from_vp08_box(b);
WasmApi::create_video_decoder(self.player_id, config).await
}
SampleEntry::Opus(b) => {
let config = AudioDecoderConfig::from_opus_box(b);
WasmApi::create_audio_decoder(self.player_id, config).await
Expand Down

0 comments on commit 8093dc4

Please sign in to comment.