/*
* 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;
import com.google.android.exoplayer.util.Util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Defines the format of an elementary media stream.
*/
public class MediaFormat {
public static final int NO_VALUE = -1;
public final String mimeType;
public final int maxInputSize;
public final int width;
public final int height;
public final int channelCount;
public final int sampleRate;
private int maxWidth;
private int maxHeight;
public final List<byte[]> initializationData;
// Lazy-initialized hashcode.
private int hashCode;
// Possibly-lazy-initialized framework media format.
private android.media.MediaFormat frameworkMediaFormat;
@TargetApi(16)
public static MediaFormat createFromFrameworkMediaFormatV16(android.media.MediaFormat format) {
return new MediaFormat(format);
}
public static MediaFormat createVideoFormat(String mimeType, int maxInputSize, int width,
int height, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, width, height, NO_VALUE, NO_VALUE,
initializationData);
}
public static MediaFormat createAudioFormat(String mimeType, int maxInputSize, int channelCount,
int sampleRate, List<byte[]> initializationData) {
return new MediaFormat(mimeType, maxInputSize, NO_VALUE, NO_VALUE, channelCount, sampleRate,
initializationData);
}
@TargetApi(16)
private MediaFormat(android.media.MediaFormat format) {
this.frameworkMediaFormat = format;
mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
maxInputSize = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE);
width = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_WIDTH);
height = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT);
channelCount = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT);
sampleRate = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE);
initializationData = new ArrayList<byte[]>();
for (int i = 0; format.containsKey("csd-" + i); i++) {
ByteBuffer buffer = format.getByteBuffer("csd-" + i);
byte[] data = new byte[buffer.limit()];
buffer.get(data);
initializationData.add(data);
buffer.flip();
}
maxWidth = NO_VALUE;
maxHeight = NO_VALUE;
}
private MediaFormat(String mimeType, int maxInputSize, int width, int height, int channelCount,
int sampleRate, List<byte[]> initializationData) {
this.mimeType = mimeType;
this.maxInputSize = maxInputSize;
this.width = width;
this.height = height;
this.channelCount = channelCount;
this.sampleRate = sampleRate;
this.initializationData = initializationData == null ? Collections.<byte[]>emptyList()
: initializationData;
maxWidth = NO_VALUE;
maxHeight = NO_VALUE;
}
public void setMaxVideoDimensions(int maxWidth, int maxHeight) {
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
if (frameworkMediaFormat != null) {
maybeSetMaxDimensionsV16(frameworkMediaFormat);
}
}
public int getMaxVideoWidth() {
return maxWidth;
}
public int getMaxVideoHeight() {
return maxHeight;
}
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + mimeType == null ? 0 : mimeType.hashCode();
result = 31 * result + maxInputSize;
result = 31 * result + width;
result = 31 * result + height;
result = 31 * result + maxWidth;
result = 31 * result + maxHeight;
result = 31 * result + channelCount;
result = 31 * result + sampleRate;
for (int i = 0; i < initializationData.size(); i++) {
result = 31 * result + Arrays.hashCode(initializationData.get(i));
}
hashCode = result;
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return equalsInternal((MediaFormat) obj, false);
}
public boolean equals(MediaFormat other, boolean ignoreMaxDimensions) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
return equalsInternal(other, ignoreMaxDimensions);
}
private boolean equalsInternal(MediaFormat other, boolean ignoreMaxDimensions) {
if (maxInputSize != other.maxInputSize || width != other.width || height != other.height
|| (!ignoreMaxDimensions && (maxWidth != other.maxWidth || maxHeight != other.maxHeight))
|| channelCount != other.channelCount || sampleRate != other.sampleRate
|| !Util.areEqual(mimeType, other.mimeType)
|| initializationData.size() != other.initializationData.size()) {
return false;
}
for (int i = 0; i < initializationData.size(); i++) {
if (!Arrays.equals(initializationData.get(i), other.initializationData.get(i))) {
return false;
}
}
return true;
}
@Override
public String toString() {
return "MediaFormat(" + mimeType + ", " + maxInputSize + ", " + width + ", " + height + ", " +
channelCount + ", " + sampleRate + ", " + maxWidth + ", " + maxHeight + ")";
}
/**
* @return A {@link MediaFormat} representation of this format.
*/
@TargetApi(16)
public final android.media.MediaFormat getFrameworkMediaFormatV16() {
if (frameworkMediaFormat == null) {
android.media.MediaFormat format = new android.media.MediaFormat();
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_WIDTH, width);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT, height);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT, channelCount);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE, sampleRate);
for (int i = 0; i < initializationData.size(); i++) {
format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i)));
}
maybeSetMaxDimensionsV16(format);
frameworkMediaFormat = format;
}
return frameworkMediaFormat;
}
@SuppressLint("InlinedApi")
@TargetApi(16)
private final void maybeSetMaxDimensionsV16(android.media.MediaFormat format) {
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_WIDTH, maxWidth);
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_HEIGHT, maxHeight);
}
@TargetApi(16)
private static final void maybeSetIntegerV16(android.media.MediaFormat format, String key,
int value) {
if (value != NO_VALUE) {
format.setInteger(key, value);
}
}
@TargetApi(16)
private static final int getOptionalIntegerV16(android.media.MediaFormat format,
String key) {
return format.containsKey(key) ? format.getInteger(key) : NO_VALUE;
}
}