mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
e19506bf8b
We recorded a h264 + opus video and audio stream with chrome mediarecorder api. From debug mode, I noticed that transmuxed opus chunk was 3x shorter than video, but ffmpeg showed that they should be about the same. It turns out the duration is not set correctly. Before the fix, repeated seeking will eventually get stuck. After this fix, I can verify that seeking no longer is stuck. Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
159 lines
4.6 KiB
JavaScript
159 lines
4.6 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.provide('shaka.transmuxer.Opus');
|
|
|
|
goog.requireType('shaka.util.TsParser');
|
|
|
|
|
|
/**
|
|
* Opus utils
|
|
*/
|
|
shaka.transmuxer.Opus = class {
|
|
/**
|
|
* @param {!shaka.util.TsParser.OpusMetadata} metadata
|
|
* @return {!Uint8Array}
|
|
*/
|
|
static getAudioConfig(metadata) {
|
|
let mapping = [];
|
|
switch (metadata.channelConfigCode) {
|
|
case 0x01:
|
|
case 0x02:
|
|
mapping = [0x0];
|
|
break;
|
|
case 0x00: // dualmono
|
|
mapping = [0xFF, 1, 1, 0, 1];
|
|
break;
|
|
case 0x80: // dualmono
|
|
mapping = [0xFF, 2, 0, 0, 1];
|
|
break;
|
|
case 0x03:
|
|
mapping = [0x01, 2, 1, 0, 2, 1];
|
|
break;
|
|
case 0x04:
|
|
mapping = [0x01, 2, 2, 0, 1, 2, 3];
|
|
break;
|
|
case 0x05:
|
|
mapping = [0x01, 3, 2, 0, 4, 1, 2, 3];
|
|
break;
|
|
case 0x06:
|
|
mapping = [0x01, 4, 2, 0, 4, 1, 2, 3, 5];
|
|
break;
|
|
case 0x07:
|
|
mapping = [0x01, 4, 2, 0, 4, 1, 2, 3, 5, 6];
|
|
break;
|
|
case 0x08:
|
|
mapping = [0x01, 5, 3, 0, 6, 1, 2, 3, 4, 5, 7];
|
|
break;
|
|
case 0x82:
|
|
mapping = [0x01, 1, 2, 0, 1];
|
|
break;
|
|
case 0x83:
|
|
mapping = [0x01, 1, 3, 0, 1, 2];
|
|
break;
|
|
case 0x84:
|
|
mapping = [0x01, 1, 4, 0, 1, 2, 3];
|
|
break;
|
|
case 0x85:
|
|
mapping = [0x01, 1, 5, 0, 1, 2, 3, 4];
|
|
break;
|
|
case 0x86:
|
|
mapping = [0x01, 1, 6, 0, 1, 2, 3, 4, 5];
|
|
break;
|
|
case 0x87:
|
|
mapping = [0x01, 1, 7, 0, 1, 2, 3, 4, 5, 6];
|
|
break;
|
|
case 0x88:
|
|
mapping = [0x01, 1, 8, 0, 1, 2, 3, 4, 5, 6, 7];
|
|
break;
|
|
}
|
|
|
|
return new Uint8Array([
|
|
0x00, // Version (1)
|
|
metadata.channelCount, // OutputChannelCount: 2
|
|
0x00, 0x00, // PreSkip: 2
|
|
(metadata.sampleRate >>> 24) & 0xFF, // Audio sample rate: 4
|
|
(metadata.sampleRate >>> 17) & 0xFF,
|
|
(metadata.sampleRate >>> 8) & 0xFF,
|
|
(metadata.sampleRate >>> 0) & 0xFF,
|
|
0x00, 0x00, // Global Gain : 2
|
|
...mapping,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Returns the number of 48 kHz samples represented by an opus packet,
|
|
* derived from its TOC byte and (for code 3) the frame-count byte.
|
|
* See RFC 6716 §3.1.
|
|
*
|
|
* One PES packet in an MPEG-TS opus stream can carry a multi-frame opus
|
|
* packet (e.g. browser MediaRecorder emits code-3 packets with 3x 20ms
|
|
* CELT frames = 60ms per packet). Treating one PES packet as one frame's
|
|
* worth of samples under-counts duration and breaks timeline alignment
|
|
* in the resulting mp4.
|
|
*
|
|
* Returned counts are at 48 kHz. This assumes the caller writes the opus
|
|
* track with a 48 kHz mp4 timescale, which TsParser hardcodes for opus
|
|
* (RFC 6716: opus always decodes internally at 48 kHz; OpusHead's input
|
|
* sample rate is informational). If that hardcode ever becomes variable,
|
|
* scale the return value by (timescale / 48000).
|
|
*
|
|
* @param {!Uint8Array} packet Opus packet starting at the TOC byte.
|
|
* @return {number} Sample count at 48 kHz.
|
|
*/
|
|
static getPacketSampleCount(packet) {
|
|
if (packet.length < 1) {
|
|
return shaka.transmuxer.Opus.OPUS_AUDIO_SAMPLE_PER_FRAME;
|
|
}
|
|
const toc = packet[0];
|
|
const config = (toc >> 3) & 0x1F;
|
|
const code = toc & 0x03;
|
|
const spf = shaka.transmuxer.Opus.SAMPLES_PER_FRAME_BY_CONFIG_[config];
|
|
let frames;
|
|
if (code === 0) {
|
|
frames = 1;
|
|
} else if (code === 1 || code === 2) {
|
|
frames = 2;
|
|
} else {
|
|
// Code 3: number of frames is in bits 0-5 of the second byte.
|
|
if (packet.length < 2) {
|
|
return spf;
|
|
}
|
|
frames = packet[1] & 0x3F;
|
|
if (frames === 0) {
|
|
return spf;
|
|
}
|
|
}
|
|
return spf * frames;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Samples per opus frame at 48 kHz, indexed by the 5-bit config field from
|
|
* the TOC byte. See RFC 6716 §3.1, Table 2.
|
|
*
|
|
* @private @const {!Array<number>}
|
|
*/
|
|
shaka.transmuxer.Opus.SAMPLES_PER_FRAME_BY_CONFIG_ = [
|
|
480, 960, 1920, 2880, // SILK NB 10/20/40/60 ms
|
|
480, 960, 1920, 2880, // SILK MB 10/20/40/60 ms
|
|
480, 960, 1920, 2880, // SILK WB 10/20/40/60 ms
|
|
480, 960, // Hybrid SWB 10/20 ms
|
|
480, 960, // Hybrid FB 10/20 ms
|
|
120, 240, 480, 960, // CELT NB 2.5/5/10/20 ms
|
|
120, 240, 480, 960, // CELT WB
|
|
120, 240, 480, 960, // CELT SWB
|
|
120, 240, 480, 960, // CELT FB
|
|
];
|
|
|
|
/**
|
|
* Retained for backward compatibility. Prefer getPacketSampleCount(), which
|
|
* handles multi-frame opus packets correctly.
|
|
*
|
|
* @const {number}
|
|
*/
|
|
shaka.transmuxer.Opus.OPUS_AUDIO_SAMPLE_PER_FRAME = 960;
|