Add support for Opus audio codec.

Implement dOpsBox to provide support for Opus audio codec

PiperOrigin-RevId: 653288049
This commit is contained in:
Googler 2024-07-17 10:48:55 -07:00 committed by Copybara-Service
parent 68e8d9cb68
commit 01dda6d3e5
8 changed files with 682 additions and 3 deletions

View File

@ -55,6 +55,7 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest {
private static final String AMR_NB = "bbb_mono_8kHz_12.2kbps_amrnb.3gp"; private static final String AMR_NB = "bbb_mono_8kHz_12.2kbps_amrnb.3gp";
private static final String AMR_WB = "bbb_mono_16kHz_23.05kbps_amrwb.3gp"; private static final String AMR_WB = "bbb_mono_16kHz_23.05kbps_amrwb.3gp";
private static final String MPEG4_MP4 = "bbb_176x144_192kbps_15fps_mpeg4.mp4"; private static final String MPEG4_MP4 = "bbb_176x144_192kbps_15fps_mpeg4.mp4";
private static final String OPUS_OGG = "bbb_6ch_8kHz_opus.ogg";
@Parameters(name = "{0}") @Parameters(name = "{0}")
public static ImmutableList<String> mediaSamples() { public static ImmutableList<String> mediaSamples() {
@ -68,7 +69,8 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest {
AV1_MP4, AV1_MP4,
AMR_NB, AMR_NB,
AMR_WB, AMR_WB,
MPEG4_MP4); MPEG4_MP4,
OPUS_OGG);
} }
@Parameter public @MonotonicNonNull String inputFile; @Parameter public @MonotonicNonNull String inputFile;

View File

@ -550,6 +550,8 @@ import java.util.List;
return damrBox(/* mode= */ (short) 0x83FF); // mode set: all enabled for AMR-WB return damrBox(/* mode= */ (short) 0x83FF); // mode set: all enabled for AMR-WB
case MimeTypes.VIDEO_H263: case MimeTypes.VIDEO_H263:
return d263Box(); return d263Box();
case MimeTypes.AUDIO_OPUS:
return dOpsBox(format);
case MimeTypes.VIDEO_H264: case MimeTypes.VIDEO_H264:
return avcCBox(format); return avcCBox(format);
case MimeTypes.VIDEO_H265: case MimeTypes.VIDEO_H265:
@ -1345,6 +1347,8 @@ import java.util.List;
return "sawb"; return "sawb";
case MimeTypes.VIDEO_H263: case MimeTypes.VIDEO_H263:
return "s263"; return "s263";
case MimeTypes.AUDIO_OPUS:
return "Opus";
case MimeTypes.VIDEO_H264: case MimeTypes.VIDEO_H264:
return "avc1"; return "avc1";
case MimeTypes.VIDEO_H265: case MimeTypes.VIDEO_H265:
@ -1444,6 +1448,24 @@ import java.util.List;
return BoxUtils.wrapIntoBox("damr", contents); return BoxUtils.wrapIntoBox("damr", contents);
} }
/** Returns the audio dOps box for Opus codec as per RFC-7845: 5.1. */
private static ByteBuffer dOpsBox(Format format) {
checkArgument(!format.initializationData.isEmpty());
int opusHeaderLength = 8;
byte[] csd0 = format.initializationData.get(0);
checkArgument(
csd0.length >= opusHeaderLength,
"As csd0 contains 'OpusHead' in first 8 bytes, csd0 length should be greater than 8");
ByteBuffer contents = ByteBuffer.allocate(csd0.length);
// Skip 8 bytes containing "OpusHead".
contents.put(
/* src */ csd0, /* offset */ opusHeaderLength, /* length */ csd0.length - opusHeaderLength);
contents.flip();
return BoxUtils.wrapIntoBox("dOps", contents);
}
/** Packs a three-letter language code into a short, packing 3x5 bits. */ /** Packs a three-letter language code into a short, packing 3x5 bits. */
private static short languageCodeFromString(@Nullable String code) { private static short languageCodeFromString(@Nullable String code) {
if (code == null) { if (code == null) {

View File

@ -35,7 +35,7 @@ import java.nio.ByteBuffer;
/** /**
* A muxer for creating a fragmented MP4 file. * A muxer for creating a fragmented MP4 file.
* *
* <p>The muxer supports writing H263, H264, H265 and AV1 video, AAC audio and metadata. * <p>The muxer supports writing H263, H264, H265 and AV1 video, AAC and Opus audio, and metadata.
* *
* <p>All the operations are performed on the caller thread. * <p>All the operations are performed on the caller thread.
* *

View File

@ -54,7 +54,7 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
/** /**
* A muxer for creating an MP4 container file. * A muxer for creating an MP4 container file.
* *
* <p>The muxer supports writing H263, H264, H265 and AV1 video, AAC audio and metadata. * <p>The muxer supports writing H263, H264, H265 and AV1 video, AAC and Opus audio, and metadata.
* *
* <p>All the operations are performed on the caller thread. * <p>All the operations are performed on the caller thread.
* *

View File

@ -296,6 +296,31 @@ public class BoxesTest {
context, dumpableBox, getExpectedDumpFilePath("audio_sample_entry_box_sawb")); context, dumpableBox, getExpectedDumpFilePath("audio_sample_entry_box_sawb"));
} }
@Test
public void createAudioSampleEntryBox_forOpus_matchesExpected() throws Exception {
Format format =
new Format.Builder()
.setPeakBitrate(128000)
.setSampleRate(48000)
.setId(3)
.setSampleMimeType(MimeTypes.AUDIO_OPUS)
.setChannelCount(6)
.setAverageBitrate(128000)
.setLanguage("```")
.setMaxInputSize(502)
.setInitializationData(
ImmutableList.of(
BaseEncoding.base16()
.decode("4F7075734865616401063801401F00000000010402000401020305")))
.build();
ByteBuffer audioSampleEntryBox = Boxes.audioSampleEntry(format);
DumpableMp4Box dumpableBox = new DumpableMp4Box(audioSampleEntryBox);
DumpFileAsserts.assertOutput(
context, dumpableBox, getExpectedDumpFilePath("audio_sample_entry_box_opus"));
}
@Test @Test
public void createAudioSampleEntryBox_withUnknownAudioFormat_throws() { public void createAudioSampleEntryBox_withUnknownAudioFormat_throws() {
// The audio format contains an unknown MIME type. // The audio format contains an unknown MIME type.

View File

@ -0,0 +1,2 @@
Opus (63 bytes):
Data = length 55, hash CF9222D3

View File

@ -0,0 +1,628 @@
seekMap:
isSeekable = true
duration = 2993500
getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=1, position=400052]]
getPosition(1496750) = [[timeUs=1496750, position=449844]]
getPosition(2993500) = [[timeUs=2993500, position=499769]]
numberOfTracks = 1
track 0:
total output bytes = 100872
sample count = 151
format 0:
id = 1
sampleMimeType = audio/opus
maxInputSize = 1185
channelCount = 6
sampleRate = 48000
language = ```
metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData:
data = length 27, hash 9EE6F879
data = length 8, hash CA22068C
data = length 8, hash 79C07075
sample 0:
time = 0
flags = 1
data = length 949, hash E49C1A9D
sample 1:
time = 13500
flags = 1
data = length 608, hash F65C6881
sample 2:
time = 33500
flags = 1
data = length 650, hash 914AF7E1
sample 3:
time = 53500
flags = 1
data = length 603, hash B3EC7A47
sample 4:
time = 73500
flags = 1
data = length 650, hash 162CA2A3
sample 5:
time = 93500
flags = 1
data = length 678, hash 192785A9
sample 6:
time = 113500
flags = 1
data = length 685, hash 96C2F2BB
sample 7:
time = 133500
flags = 1
data = length 665, hash 5922AAEB
sample 8:
time = 153500
flags = 1
data = length 646, hash 8619B832
sample 9:
time = 173500
flags = 1
data = length 644, hash AFAFE536
sample 10:
time = 193500
flags = 1
data = length 629, hash 9E73B0B7
sample 11:
time = 213500
flags = 1
data = length 610, hash 669587E0
sample 12:
time = 233500
flags = 1
data = length 663, hash D7642DB7
sample 13:
time = 253500
flags = 1
data = length 648, hash 6D0CD0D2
sample 14:
time = 273500
flags = 1
data = length 642, hash 412DC4E2
sample 15:
time = 293500
flags = 1
data = length 607, hash F9582E56
sample 16:
time = 313500
flags = 1
data = length 645, hash 657B023B
sample 17:
time = 333500
flags = 1
data = length 672, hash AA362E4A
sample 18:
time = 353500
flags = 1
data = length 629, hash 9E527BB
sample 19:
time = 373500
flags = 1
data = length 646, hash F3B96232
sample 20:
time = 393500
flags = 1
data = length 611, hash 9B3061C
sample 21:
time = 413500
flags = 1
data = length 664, hash 50774FBE
sample 22:
time = 433500
flags = 1
data = length 650, hash 52AA7B95
sample 23:
time = 453500
flags = 1
data = length 636, hash F5CB417D
sample 24:
time = 473500
flags = 1
data = length 665, hash 34C938BB
sample 25:
time = 493500
flags = 1
data = length 626, hash 5CD7E3F8
sample 26:
time = 513500
flags = 1
data = length 651, hash AF7BDD5F
sample 27:
time = 533500
flags = 1
data = length 635, hash FF8E92FE
sample 28:
time = 553500
flags = 1
data = length 655, hash CC0821A7
sample 29:
time = 573500
flags = 1
data = length 636, hash 1BDC263F
sample 30:
time = 593500
flags = 1
data = length 641, hash 5ECE0258
sample 31:
time = 613500
flags = 1
data = length 632, hash FA762551
sample 32:
time = 633500
flags = 1
data = length 656, hash 97F5501A
sample 33:
time = 653500
flags = 1
data = length 657, hash 462517A5
sample 34:
time = 673500
flags = 1
data = length 651, hash 8863CCAD
sample 35:
time = 693500
flags = 1
data = length 648, hash B0330C37
sample 36:
time = 713500
flags = 1
data = length 674, hash 60E12F33
sample 37:
time = 733500
flags = 1
data = length 674, hash DEFEB445
sample 38:
time = 753500
flags = 1
data = length 583, hash C8326130
sample 39:
time = 773500
flags = 1
data = length 677, hash 5FADD0
sample 40:
time = 793500
flags = 1
data = length 637, hash CB97E2B8
sample 41:
time = 813500
flags = 1
data = length 637, hash 7D230BE4
sample 42:
time = 833500
flags = 1
data = length 651, hash 230EE90B
sample 43:
time = 853500
flags = 1
data = length 637, hash F0CCCAE
sample 44:
time = 873500
flags = 1
data = length 634, hash D0F5AEEB
sample 45:
time = 893500
flags = 1
data = length 620, hash 680A7962
sample 46:
time = 913500
flags = 1
data = length 701, hash 18050131
sample 47:
time = 933500
flags = 1
data = length 700, hash B948FC3B
sample 48:
time = 953500
flags = 1
data = length 732, hash 5F8C11AD
sample 49:
time = 973500
flags = 1
data = length 632, hash 66D306D1
sample 50:
time = 993500
flags = 1
data = length 666, hash C7130EE0
sample 51:
time = 1013500
flags = 1
data = length 706, hash A3199353
sample 52:
time = 1033500
flags = 1
data = length 717, hash E98C450C
sample 53:
time = 1053500
flags = 1
data = length 699, hash E55D6BF1
sample 54:
time = 1073500
flags = 1
data = length 654, hash 6788EEF5
sample 55:
time = 1093500
flags = 1
data = length 668, hash 104C1F00
sample 56:
time = 1113500
flags = 1
data = length 632, hash 57EEBA22
sample 57:
time = 1133500
flags = 1
data = length 633, hash 8ECAA570
sample 58:
time = 1153500
flags = 1
data = length 631, hash 2583F94B
sample 59:
time = 1173500
flags = 1
data = length 657, hash 47C82A61
sample 60:
time = 1193500
flags = 1
data = length 734, hash CB4105D8
sample 61:
time = 1213500
flags = 1
data = length 626, hash FFFFF6A0
sample 62:
time = 1233500
flags = 1
data = length 647, hash 12B4EE0
sample 63:
time = 1253500
flags = 1
data = length 625, hash 33781766
sample 64:
time = 1273500
flags = 1
data = length 642, hash 4EFBA7BD
sample 65:
time = 1293500
flags = 1
data = length 802, hash 1E1ECCCC
sample 66:
time = 1313500
flags = 1
data = length 831, hash 3B6EB119
sample 67:
time = 1333500
flags = 1
data = length 803, hash 1A6D1B26
sample 68:
time = 1353500
flags = 1
data = length 724, hash 373CBA2
sample 69:
time = 1373500
flags = 1
data = length 697, hash B88E6225
sample 70:
time = 1393500
flags = 1
data = length 684, hash 582095ED
sample 71:
time = 1413500
flags = 1
data = length 642, hash E3396634
sample 72:
time = 1433500
flags = 1
data = length 660, hash 6B7A6B93
sample 73:
time = 1453500
flags = 1
data = length 658, hash BD7B7172
sample 74:
time = 1473500
flags = 1
data = length 682, hash 1B77F311
sample 75:
time = 1493500
flags = 1
data = length 635, hash 4993E2E8
sample 76:
time = 1513500
flags = 1
data = length 670, hash 1F1F426A
sample 77:
time = 1533500
flags = 1
data = length 633, hash 1A6AE4
sample 78:
time = 1553500
flags = 1
data = length 653, hash 55540278
sample 79:
time = 1573500
flags = 1
data = length 618, hash A50A27BC
sample 80:
time = 1593500
flags = 1
data = length 657, hash C61CC3D5
sample 81:
time = 1613500
flags = 1
data = length 608, hash 2691ABBB
sample 82:
time = 1633500
flags = 1
data = length 673, hash 6CFC36BC
sample 83:
time = 1653500
flags = 1
data = length 646, hash C12DD9A3
sample 84:
time = 1673500
flags = 1
data = length 665, hash FF2B77B0
sample 85:
time = 1693500
flags = 1
data = length 644, hash 2BBF36A5
sample 86:
time = 1713500
flags = 1
data = length 750, hash 4510EA7E
sample 87:
time = 1733500
flags = 1
data = length 763, hash 22955796
sample 88:
time = 1753500
flags = 1
data = length 638, hash 8AC8492E
sample 89:
time = 1773500
flags = 1
data = length 659, hash 6CCFCDC4
sample 90:
time = 1793500
flags = 1
data = length 653, hash ED5CB356
sample 91:
time = 1813500
flags = 1
data = length 625, hash A49FB079
sample 92:
time = 1833500
flags = 1
data = length 681, hash 29FCE7E6
sample 93:
time = 1853500
flags = 1
data = length 719, hash B6E2BE04
sample 94:
time = 1873500
flags = 1
data = length 659, hash 455A8590
sample 95:
time = 1893500
flags = 1
data = length 711, hash D970780F
sample 96:
time = 1913500
flags = 1
data = length 649, hash 7B05E203
sample 97:
time = 1933500
flags = 1
data = length 621, hash A06A54BD
sample 98:
time = 1953500
flags = 1
data = length 740, hash F5B727FA
sample 99:
time = 1973500
flags = 1
data = length 829, hash 24C8AD34
sample 100:
time = 1993500
flags = 1
data = length 646, hash E8292CD3
sample 101:
time = 2013500
flags = 1
data = length 628, hash D2CF6E04
sample 102:
time = 2033500
flags = 1
data = length 621, hash B1DEB04E
sample 103:
time = 2053500
flags = 1
data = length 783, hash 35A1171
sample 104:
time = 2073500
flags = 1
data = length 661, hash 7BFCA7CB
sample 105:
time = 2093500
flags = 1
data = length 616, hash 27080B66
sample 106:
time = 2113500
flags = 1
data = length 700, hash E3450B94
sample 107:
time = 2133500
flags = 1
data = length 631, hash 4223054B
sample 108:
time = 2153500
flags = 1
data = length 650, hash DB573283
sample 109:
time = 2173500
flags = 1
data = length 649, hash 619E7468
sample 110:
time = 2193500
flags = 1
data = length 628, hash 9BD367C7
sample 111:
time = 2213500
flags = 1
data = length 693, hash 85E9290
sample 112:
time = 2233500
flags = 1
data = length 645, hash CAC17B47
sample 113:
time = 2253500
flags = 1
data = length 639, hash 88A52980
sample 114:
time = 2273500
flags = 1
data = length 657, hash DAF95EA7
sample 115:
time = 2293500
flags = 1
data = length 635, hash 2F950D30
sample 116:
time = 2313500
flags = 1
data = length 649, hash 48E900E
sample 117:
time = 2333500
flags = 1
data = length 657, hash 4505A6CE
sample 118:
time = 2353500
flags = 1
data = length 682, hash DC4C8CD8
sample 119:
time = 2373500
flags = 1
data = length 711, hash 21B58FD3
sample 120:
time = 2393500
flags = 1
data = length 692, hash 4CDFB1C7
sample 121:
time = 2413500
flags = 1
data = length 693, hash B531D8C8
sample 122:
time = 2433500
flags = 1
data = length 650, hash AF15E260
sample 123:
time = 2453500
flags = 1
data = length 643, hash EDF00663
sample 124:
time = 2473500
flags = 1
data = length 623, hash 1B0243A
sample 125:
time = 2493500
flags = 1
data = length 639, hash 21B0BA43
sample 126:
time = 2513500
flags = 1
data = length 694, hash 46FA242C
sample 127:
time = 2533500
flags = 1
data = length 637, hash 1CEAEA46
sample 128:
time = 2553500
flags = 1
data = length 672, hash A8FE01B1
sample 129:
time = 2573500
flags = 1
data = length 655, hash F4ACFC61
sample 130:
time = 2593500
flags = 1
data = length 674, hash 3B3D15B6
sample 131:
time = 2613500
flags = 1
data = length 695, hash DF376589
sample 132:
time = 2633500
flags = 1
data = length 688, hash A2425D57
sample 133:
time = 2653500
flags = 1
data = length 690, hash BBD3DA9B
sample 134:
time = 2673500
flags = 1
data = length 732, hash F7B00D40
sample 135:
time = 2693500
flags = 1
data = length 684, hash 9158FEAB
sample 136:
time = 2713500
flags = 1
data = length 674, hash 40C7501A
sample 137:
time = 2733500
flags = 1
data = length 680, hash BB132A40
sample 138:
time = 2753500
flags = 1
data = length 648, hash 1114376E
sample 139:
time = 2773500
flags = 1
data = length 661, hash DE71BACA
sample 140:
time = 2793500
flags = 1
data = length 642, hash A2316E7
sample 141:
time = 2813500
flags = 1
data = length 662, hash C7117D4
sample 142:
time = 2833500
flags = 1
data = length 638, hash 2CF77EF4
sample 143:
time = 2853500
flags = 1
data = length 655, hash E5A75C8B
sample 144:
time = 2873500
flags = 1
data = length 635, hash C9E2B5B1
sample 145:
time = 2893500
flags = 1
data = length 655, hash 6F99C63E
sample 146:
time = 2913500
flags = 1
data = length 638, hash 64436E98
sample 147:
time = 2933500
flags = 1
data = length 671, hash 5DFDE09A
sample 148:
time = 2953500
flags = 1
data = length 661, hash 5203D894
sample 149:
time = 2973500
flags = 1
data = length 664, hash CF30C6D7
sample 150:
time = 2993500
flags = 536870913
data = length 1155, hash F5AFA236
tracksEnded = true