|
| 1 | +const safariPreferredCodecs = [ |
| 2 | + // Safari works best with MP4/AAC but fails when strict codecs are defined on iOS. |
| 3 | + // Prioritize the plain container to avoid NotSupportedError during MediaRecorder initialization. |
| 4 | + 'audio/mp4', |
| 5 | + 'audio/mp4;codecs=mp4a.40.2', |
| 6 | + 'audio/mp4;codecs=mp4a.40.5', |
| 7 | + 'audio/mp4;codecs=aac', |
| 8 | + 'audio/aac', |
| 9 | + // Fallbacks |
| 10 | + 'audio/wav;codecs=1', |
| 11 | + 'audio/wav', |
| 12 | + 'audio/mpeg', |
| 13 | +]; |
| 14 | + |
| 15 | +const defaultPreferredCodecs = [ |
| 16 | + // Chromium / Firefox stable path. |
| 17 | + 'audio/webm;codecs=opus', |
| 18 | + 'audio/webm', |
| 19 | + // Firefox |
| 20 | + 'audio/ogg;codecs=opus', |
| 21 | + 'audio/ogg;codecs=vorbis', |
| 22 | + 'audio/ogg', |
| 23 | + // Fallbacks |
| 24 | + 'audio/wav;codecs=1', |
| 25 | + 'audio/wav', |
| 26 | + 'audio/mpeg', |
| 27 | + // Keep MP4/AAC as late fallback for non-Safari browsers. |
| 28 | + 'audio/mp4;codecs=mp4a.40.2', |
| 29 | + 'audio/mp4;codecs=mp4a.40.5', |
| 30 | + 'audio/mp4;codecs=aac', |
| 31 | + 'audio/mp4', |
| 32 | + 'audio/aac', |
| 33 | + 'audio/ogg;codecs=speex', |
| 34 | + 'audio/webm;codecs=vorbis', |
| 35 | +]; |
| 36 | + |
| 37 | +/** |
| 38 | + * Checks for supported audio codecs in the current browser and returns the first supported codec. |
| 39 | + * If no supported codec is found, it returns null. |
| 40 | + */ |
| 41 | +export function getSupportedAudioCodec(): string | null { |
| 42 | + if (!('MediaRecorder' in globalThis) || !globalThis.MediaRecorder) { |
| 43 | + return null; |
| 44 | + } |
| 45 | + |
| 46 | + const userAgent = globalThis.navigator?.userAgent ?? ''; |
| 47 | + const isIOS = |
| 48 | + /iPad|iPhone|iPod/.test(userAgent) || |
| 49 | + // eslint-disable-next-line @typescript-eslint/no-deprecated |
| 50 | + (globalThis.navigator?.platform === 'MacIntel' && globalThis.navigator?.maxTouchPoints > 1); |
| 51 | + const isSafari = /^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(userAgent) || isIOS; |
| 52 | + |
| 53 | + const preferredCodecs = isSafari ? safariPreferredCodecs : defaultPreferredCodecs; |
| 54 | + const supportedCodec = preferredCodecs.find((codec) => MediaRecorder.isTypeSupported(codec)); |
| 55 | + return supportedCodec || null; |
| 56 | +} |
| 57 | + |
| 58 | +/** |
| 59 | + * Returns the appropriate file extension for a given audio codec. |
| 60 | + * This is used to ensure that the recorded audio file has the correct extension based on the codec used for recording. |
| 61 | + */ |
| 62 | +export function getSupportedAudioExtension(codec: string): string { |
| 63 | + const baseType = codec.split(';')[0].trim(); |
| 64 | + switch (baseType) { |
| 65 | + case 'audio/ogg': |
| 66 | + return 'ogg'; |
| 67 | + case 'audio/webm': |
| 68 | + return 'webm'; |
| 69 | + case 'audio/mp4': |
| 70 | + return 'm4a'; |
| 71 | + case 'audio/mpeg': |
| 72 | + return 'mp3'; |
| 73 | + case 'audio/wav': |
| 74 | + return 'wav'; |
| 75 | + case 'audio/aac': |
| 76 | + return 'aac'; |
| 77 | + default: |
| 78 | + return 'dat'; // default extension for unknown codecs |
| 79 | + } |
| 80 | +} |
0 commit comments