Use the true bitrate for CBR MP3 seeking

PiperOrigin-RevId: 225989898
This commit is contained in:
andrewlewis 2018-12-18 14:58:01 +00:00 committed by Oliver Woodman
parent ee2e89e0cd
commit 0e8e9621c0
6 changed files with 44 additions and 24 deletions

View File

@ -7,6 +7,7 @@
* Support for playing spherical videos on Daydream. * Support for playing spherical videos on Daydream.
* Improve decoder re-use between playbacks. TODO: Write and link a blog post * Improve decoder re-use between playbacks. TODO: Write and link a blog post
here ([#2826](https://github.com/google/ExoPlayer/issues/2826)). here ([#2826](https://github.com/google/ExoPlayer/issues/2826)).
* Use the true bitrate for constant-bitrate MP3 seeking.
* Track selection: * Track selection:
* Add options for controlling audio track selections to `DefaultTrackSelector` * Add options for controlling audio track selections to `DefaultTrackSelector`
([#3314](https://github.com/google/ExoPlayer/issues/3314)). ([#3314](https://github.com/google/ExoPlayer/issues/3314)).

View File

@ -34,16 +34,26 @@ public final class MpegAudioHeader {
private static final String[] MIME_TYPE_BY_LAYER = private static final String[] MIME_TYPE_BY_LAYER =
new String[] {MimeTypes.AUDIO_MPEG_L1, MimeTypes.AUDIO_MPEG_L2, MimeTypes.AUDIO_MPEG}; new String[] {MimeTypes.AUDIO_MPEG_L1, MimeTypes.AUDIO_MPEG_L2, MimeTypes.AUDIO_MPEG};
private static final int[] SAMPLING_RATE_V1 = {44100, 48000, 32000}; private static final int[] SAMPLING_RATE_V1 = {44100, 48000, 32000};
private static final int[] BITRATE_V1_L1 = private static final int[] BITRATE_V1_L1 = {
{32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}; 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000,
private static final int[] BITRATE_V2_L1 = 416000, 448000
{32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}; };
private static final int[] BITRATE_V1_L2 = private static final int[] BITRATE_V2_L1 = {
{32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}; 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000,
private static final int[] BITRATE_V1_L3 = 224000, 256000
{32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}; };
private static final int[] BITRATE_V2 = private static final int[] BITRATE_V1_L2 = {
{8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}; 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000,
320000, 384000
};
private static final int[] BITRATE_V1_L3 = {
32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000,
320000
};
private static final int[] BITRATE_V2 = {
8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000,
160000
};
/** /**
* Returns the size of the frame associated with {@code header}, or {@link C#LENGTH_UNSET} if it * Returns the size of the frame associated with {@code header}, or {@link C#LENGTH_UNSET} if it
@ -89,7 +99,7 @@ public final class MpegAudioHeader {
if (layer == 3) { if (layer == 3) {
// Layer I (layer == 3) // Layer I (layer == 3)
bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1]; bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
return (12000 * bitrate / samplingRate + padding) * 4; return (12 * bitrate / samplingRate + padding) * 4;
} else { } else {
// Layer II (layer == 2) or III (layer == 1) // Layer II (layer == 2) or III (layer == 1)
if (version == 3) { if (version == 3) {
@ -102,10 +112,10 @@ public final class MpegAudioHeader {
if (version == 3) { if (version == 3) {
// Version 1 // Version 1
return 144000 * bitrate / samplingRate + padding; return 144 * bitrate / samplingRate + padding;
} else { } else {
// Version 2 or 2.5 // Version 2 or 2.5
return (layer == 1 ? 72000 : 144000) * bitrate / samplingRate + padding; return (layer == 1 ? 72 : 144) * bitrate / samplingRate + padding;
} }
} }
@ -159,7 +169,7 @@ public final class MpegAudioHeader {
if (layer == 3) { if (layer == 3) {
// Layer I (layer == 3) // Layer I (layer == 3)
bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1]; bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
frameSize = (12000 * bitrate / sampleRate + padding) * 4; frameSize = (12 * bitrate / sampleRate + padding) * 4;
samplesPerFrame = 384; samplesPerFrame = 384;
} else { } else {
// Layer II (layer == 2) or III (layer == 1) // Layer II (layer == 2) or III (layer == 1)
@ -167,19 +177,22 @@ public final class MpegAudioHeader {
// Version 1 // Version 1
bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1]; bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1];
samplesPerFrame = 1152; samplesPerFrame = 1152;
frameSize = 144000 * bitrate / sampleRate + padding; frameSize = 144 * bitrate / sampleRate + padding;
} else { } else {
// Version 2 or 2.5. // Version 2 or 2.5.
bitrate = BITRATE_V2[bitrateIndex - 1]; bitrate = BITRATE_V2[bitrateIndex - 1];
samplesPerFrame = layer == 1 ? 576 : 1152; samplesPerFrame = layer == 1 ? 576 : 1152;
frameSize = (layer == 1 ? 72000 : 144000) * bitrate / sampleRate + padding; frameSize = (layer == 1 ? 72 : 144) * bitrate / sampleRate + padding;
} }
} }
// Calculate the bitrate in the same way Mp3Extractor calculates sample timestamps so that
// seeking to a given timestamp and playing from the start up to that timestamp give the same
// results for CBR streams. See also [internal: b/120390268].
bitrate = 8 * frameSize * sampleRate / samplesPerFrame;
String mimeType = MIME_TYPE_BY_LAYER[3 - layer]; String mimeType = MIME_TYPE_BY_LAYER[3 - layer];
int channels = ((headerData >> 6) & 3) == 3 ? 1 : 2; int channels = ((headerData >> 6) & 3) == 3 ? 1 : 2;
header.setValues(version, mimeType, frameSize, sampleRate, channels, bitrate * 1000, header.setValues(version, mimeType, frameSize, sampleRate, channels, bitrate, samplesPerFrame);
samplesPerFrame);
return true; return true;
} }
@ -198,8 +211,14 @@ public final class MpegAudioHeader {
/** Number of samples stored in the frame. */ /** Number of samples stored in the frame. */
public int samplesPerFrame; public int samplesPerFrame;
private void setValues(int version, String mimeType, int frameSize, int sampleRate, int channels, private void setValues(
int bitrate, int samplesPerFrame) { int version,
String mimeType,
int frameSize,
int sampleRate,
int channels,
int bitrate,
int samplesPerFrame) {
this.version = version; this.version = version;
this.mimeType = mimeType; this.mimeType = mimeType;
this.frameSize = frameSize; this.frameSize = frameSize;

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 26125 duration = 26122
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 26125 duration = 26122
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 26125 duration = 26122
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:

View File

@ -1,6 +1,6 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 26125 duration = 26122
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0: