Skip to content

Commit 59eb489

Browse files
committed
fix: parse audio codec incorrect #396
1 parent 2786ce8 commit 59eb489

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

.changeset/common-squids-act.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@webav/av-canvas': patch
3+
'@webav/av-cliper': patch
4+
---
5+
6+
fix: parse audio codec incorrect #396

packages/av-canvas/demo/video-editor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ function App() {
271271
clipSource === 'local'
272272
? (await loadFile({ 'video/*': ['.mp4', '.mov'] })).stream()
273273
: (await fetch('./video/bunny_0.mp4')).body!;
274-
const spr = new VisibleSprite(new MP4Clip(stream));
274+
const clip = new MP4Clip(stream);
275+
const spr = new VisibleSprite(clip);
275276
await avCvs?.addSprite(spr);
276277
addSprite2Track('1-video', spr, '视频');
277278
}}

packages/av-cliper/src/clips/mp4-clip.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ async function mp4FileToSamples(otFile: OPFSToolFile, opts: MP4ClipOpts = {}) {
530530
const reader = await otFile.createReader();
531531
await quickParseMP4File(
532532
reader,
533-
(data) => {
533+
async (data) => {
534534
mp4Info = data.info;
535535
const ftyp = data.mp4boxFile.ftyp!;
536536
headerBoxPos.push({ start: ftyp.start, size: ftyp.size });
@@ -546,6 +546,18 @@ async function mp4FileToSamples(otFile: OPFSToolFile, opts: MP4ClipOpts = {}) {
546546
if (vc == null && ac == null) {
547547
Log.error('MP4Clip no video and audio track');
548548
}
549+
if (ac != null) {
550+
const { supported } = await AudioDecoder.isConfigSupported(ac);
551+
if (!supported) {
552+
Log.error(`MP4Clip audio codec is not supported: ${ac.codec}`);
553+
}
554+
}
555+
if (vc != null) {
556+
const { supported } = await VideoDecoder.isConfigSupported(vc);
557+
if (!supported) {
558+
Log.error(`MP4Clip video codec is not supported: ${vc.codec}`);
559+
}
560+
}
549561
Log.info(
550562
'mp4BoxFile moov ready',
551563
{

packages/av-cliper/src/mp4-utils/mp4box-utils.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,22 @@ export function extractFileConfig(file: MP4File, info: MP4Info) {
5050
const aTrack = info.audioTracks[0];
5151
if (aTrack != null) {
5252
const esdsBox = getESDSBoxFromMP4File(file);
53+
const audioInfo = esdsBox == null ? {} : parseAudioInfoFromESDSBox(esdsBox);
54+
5355
rs.audioTrackConf = {
5456
timescale: aTrack.timescale,
55-
samplerate: aTrack.audio.sample_rate,
56-
channel_count: aTrack.audio.channel_count,
57+
samplerate: audioInfo.sampleRate ?? aTrack.audio.sample_rate,
58+
channel_count: audioInfo.numberOfChannels ?? aTrack.audio.channel_count,
5759
hdlr: 'soun',
5860
type: aTrack.codec.startsWith('mp4a') ? 'mp4a' : aTrack.codec,
59-
description: getESDSBoxFromMP4File(file),
61+
description: esdsBox,
6062
};
63+
6164
rs.audioDecoderConf = {
62-
codec: aTrack.codec.startsWith('mp4a')
63-
? DEFAULT_AUDIO_CONF.codec
64-
: aTrack.codec,
65-
numberOfChannels: aTrack.audio.channel_count,
66-
sampleRate: aTrack.audio.sample_rate,
67-
...(esdsBox == null ? {} : parseAudioInfo4ESDSBox(esdsBox)),
65+
codec: audioInfo.codec ?? DEFAULT_AUDIO_CONF.codec,
66+
numberOfChannels:
67+
audioInfo.numberOfChannels ?? aTrack.audio.channel_count,
68+
sampleRate: audioInfo.sampleRate ?? aTrack.audio.sample_rate,
6869
};
6970
}
7071
return rs;
@@ -97,12 +98,25 @@ function getESDSBoxFromMP4File(file: MP4File, codec = 'mp4a') {
9798
return mp4aBox?.esds;
9899
}
99100

100-
// 解决封装层音频信息标识错误,导致解码异常
101-
function parseAudioInfo4ESDSBox(esds: ESDSBoxParser) {
102-
const decoderConf = esds.esd.descs[0]?.descs[0];
103-
if (decoderConf == null) return {};
101+
// 从 ESDS Box 中解析出音频配置信息,解决封装层音频信息标识错误,导致解码异常
102+
function parseAudioInfoFromESDSBox(esds: ESDSBoxParser): {
103+
codec?: string;
104+
sampleRate?: number;
105+
numberOfChannels?: number;
106+
} {
107+
let codec = 'mp4a';
108+
const decConfDesc = esds.esd.descs[0];
109+
if (decConfDesc == null) return {};
110+
codec += '.' + decConfDesc.oti.toString(16);
111+
112+
const decSpecInfo = decConfDesc.descs[0];
113+
if (decSpecInfo == null) return { codec };
104114

105-
const [byte1, byte2] = decoderConf.data;
115+
// ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Audio_Specific_Config
116+
const audioObjectType = (decSpecInfo.data[0] & 0xf8) >> 3;
117+
codec += '.' + audioObjectType;
118+
119+
const [byte1, byte2] = decSpecInfo.data;
106120
// sampleRate 是第一字节后 3bit + 第二字节前 1bit
107121
const sampleRateIdx = ((byte1 & 0x07) << 1) + (byte2 >> 7);
108122
// numberOfChannels 是第二字节 [2, 5] 4bit
@@ -111,7 +125,9 @@ function parseAudioInfo4ESDSBox(esds: ESDSBoxParser) {
111125
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025,
112126
8000, 7350,
113127
] as const;
128+
114129
return {
130+
codec,
115131
sampleRate: sampleRateEnum[sampleRateIdx],
116132
numberOfChannels,
117133
};

types/mp4box.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ declare module '@webav/mp4box.js' {
168168

169169
interface DecoderConfigDescriptor {
170170
descs: [DecoderSpecificInfo] | [];
171+
oti: number;
171172
}
172173
interface DecoderSpecificInfo {
173174
data: Uint8ArrayBuffer;

0 commit comments

Comments
 (0)