Fix SmoothStreaming where audio FourCC is missing.

This commit is contained in:
Oliver Woodman 2014-09-19 18:29:34 +01:00
parent b2fc944af1
commit 8378019839
3 changed files with 38 additions and 123 deletions

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer.smoothstreaming; package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri; import android.net.Uri;
@ -103,12 +102,9 @@ public class SmoothStreamingManifest {
public final int bitrate; public final int bitrate;
// Audio-video // Audio-video
public final String fourCC;
public final byte[][] csd; public final byte[][] csd;
public final int profile; public final int profile;
public final int level; public final int level;
// Audio-video (derived)
public final String mimeType; public final String mimeType;
// Video-only // Video-only
@ -125,12 +121,12 @@ public class SmoothStreamingManifest {
public final int nalUnitLengthField; public final int nalUnitLengthField;
public final String content; public final String content;
public TrackElement(int index, int bitrate, String fourCC, byte[][] csd, int profile, int level, public TrackElement(int index, int bitrate, String mimeType, byte[][] csd, int profile,
int maxWidth, int maxHeight, int sampleRate, int channels, int packetSize, int audioTag, int level, int maxWidth, int maxHeight, int sampleRate, int channels, int packetSize,
int bitPerSample, int nalUnitLengthField, String content) { int audioTag, int bitPerSample, int nalUnitLengthField, String content) {
this.index = index; this.index = index;
this.bitrate = bitrate; this.bitrate = bitrate;
this.fourCC = fourCC; this.mimeType = mimeType;
this.csd = csd; this.csd = csd;
this.profile = profile; this.profile = profile;
this.level = level; this.level = level;
@ -143,19 +139,6 @@ public class SmoothStreamingManifest {
this.bitPerSample = bitPerSample; this.bitPerSample = bitPerSample;
this.nalUnitLengthField = nalUnitLengthField; this.nalUnitLengthField = nalUnitLengthField;
this.content = content; this.content = content;
this.mimeType = fourCCToMimeType(fourCC);
}
private static String fourCCToMimeType(String fourCC) {
if (fourCC.equalsIgnoreCase("H264") || fourCC.equalsIgnoreCase("AVC1")
|| fourCC.equalsIgnoreCase("DAVC")) {
return MimeTypes.VIDEO_H264;
} else if (fourCC.equalsIgnoreCase("AACL") || fourCC.equalsIgnoreCase("AACH")) {
return MimeTypes.AUDIO_AAC;
} else if (fourCC.equalsIgnoreCase("TTML")) {
return MimeTypes.APPLICATION_TTML;
}
return null;
} }
} }

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.CodecSpecificDataUtil; import com.google.android.exoplayer.util.CodecSpecificDataUtil;
import com.google.android.exoplayer.util.ManifestParser; import com.google.android.exoplayer.util.ManifestParser;
import com.google.android.exoplayer.util.MimeTypes;
import android.net.Uri; import android.net.Uri;
import android.util.Base64; import android.util.Base64;
@ -586,7 +587,7 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
private int index; private int index;
private int bitrate; private int bitrate;
private String fourCC; private String mimeType;
private int profile; private int profile;
private int level; private int level;
private int maxWidth; private int maxWidth;
@ -618,11 +619,14 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
if (type == StreamElement.TYPE_VIDEO) { if (type == StreamElement.TYPE_VIDEO) {
maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT); maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT);
maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH); maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH);
fourCC = parseRequiredString(parser, KEY_FOUR_CC); mimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
} else { } else {
maxHeight = -1; maxHeight = -1;
maxWidth = -1; maxWidth = -1;
fourCC = parser.getAttributeValue(null, KEY_FOUR_CC); String fourCC = parser.getAttributeValue(null, KEY_FOUR_CC);
// If fourCC is missing and the stream type is audio, we assume AAC.
mimeType = fourCC != null ? fourCCToMimeType(fourCC)
: type == StreamElement.TYPE_AUDIO ? MimeTypes.AUDIO_AAC : null;
} }
if (type == StreamElement.TYPE_AUDIO) { if (type == StreamElement.TYPE_AUDIO) {
@ -658,17 +662,6 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
} }
} }
private byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] data = new byte[length / 2];
for (int i = 0; i < data.length; i++) {
int stringOffset = i * 2;
data[i] = (byte) ((Character.digit(hexString.charAt(stringOffset), 16) << 4)
+ Character.digit(hexString.charAt(stringOffset + 1), 16));
}
return data;
}
@Override @Override
public void parseText(XmlPullParser parser) { public void parseText(XmlPullParser parser) {
content = parser.getText(); content = parser.getText();
@ -681,8 +674,33 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
csdArray = new byte[csd.size()][]; csdArray = new byte[csd.size()][];
csd.toArray(csdArray); csd.toArray(csdArray);
} }
return new TrackElement(index, bitrate, fourCC, csdArray, profile, level, maxWidth, maxHeight, return new TrackElement(index, bitrate, mimeType, csdArray, profile, level, maxWidth,
samplingRate, channels, packetSize, audioTag, bitPerSample, nalUnitLengthField, content); maxHeight, samplingRate, channels, packetSize, audioTag, bitPerSample, nalUnitLengthField,
content);
}
private static String fourCCToMimeType(String fourCC) {
if (fourCC.equalsIgnoreCase("H264") || fourCC.equalsIgnoreCase("X264")
|| fourCC.equalsIgnoreCase("AVC1") || fourCC.equalsIgnoreCase("DAVC")) {
return MimeTypes.VIDEO_H264;
} else if (fourCC.equalsIgnoreCase("AAC") || fourCC.equalsIgnoreCase("AACL")
|| fourCC.equalsIgnoreCase("AACH") || fourCC.equalsIgnoreCase("AACP")) {
return MimeTypes.AUDIO_AAC;
} else if (fourCC.equalsIgnoreCase("TTML")) {
return MimeTypes.APPLICATION_TTML;
}
return null;
}
private static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] data = new byte[length / 2];
for (int i = 0; i < data.length; i++) {
int stringOffset = i * 2;
data[i] = (byte) ((Character.digit(hexString.charAt(stringOffset), 16) << 4)
+ Character.digit(hexString.charAt(stringOffset + 1), 16));
}
return data;
} }
} }

View File

@ -1,86 +0,0 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement;
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
import android.util.Base64;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SmoothStreamingUtil {
private SmoothStreamingUtil() {}
/**
* Builds a {@link MediaFormat} for the specified track of the specified {@link StreamElement}.
*
* @param element The stream element.
* @param track The index of the track for which to build the format.
* @return The format.
*/
public static MediaFormat getMediaFormat(StreamElement element, int track) {
TrackElement trackElement = element.tracks[track];
String mimeType = trackElement.mimeType;
if (element.type == StreamElement.TYPE_VIDEO) {
MediaFormat format = MediaFormat.createVideoFormat(mimeType, -1, trackElement.maxWidth,
trackElement.maxHeight, Arrays.asList(trackElement.csd));
format.setMaxVideoDimensions(element.maxWidth, element.maxHeight);
return format;
} else if (element.type == StreamElement.TYPE_AUDIO) {
List<byte[]> csd;
if (trackElement.csd != null) {
csd = Arrays.asList(trackElement.csd);
} else {
csd = Collections.singletonList(CodecSpecificDataUtil.buildAudioSpecificConfig(
trackElement.sampleRate, trackElement.numChannels));
}
MediaFormat format = MediaFormat.createAudioFormat(mimeType, -1, trackElement.numChannels,
trackElement.sampleRate, csd);
return format;
}
// TODO: Do subtitles need a format? MediaFormat supports KEY_LANGUAGE.
return null;
}
public static byte[] getKeyId(byte[] initData) {
StringBuilder initDataStringBuilder = new StringBuilder();
for (int i = 0; i < initData.length; i += 2) {
initDataStringBuilder.append((char) initData[i]);
}
String initDataString = initDataStringBuilder.toString();
String keyIdString = initDataString.substring(
initDataString.indexOf("<KID>") + 5, initDataString.indexOf("</KID>"));
byte[] keyId = Base64.decode(keyIdString, Base64.DEFAULT);
swap(keyId, 0, 3);
swap(keyId, 1, 2);
swap(keyId, 4, 5);
swap(keyId, 6, 7);
return keyId;
}
private static void swap(byte[] data, int firstPosition, int secondPosition) {
byte temp = data[firstPosition];
data[firstPosition] = data[secondPosition];
data[secondPosition] = temp;
}
}