blob: 4e928a51ad94c67b0e658fa1c42ff94e54ce8c68 [file] [log] [blame]
/*
* Copyright (C) 2013 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 android.media.cts;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaPlayer;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.cts.media.R;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
public class MediaMuxerTest extends AndroidTestCase {
private static final String TAG = "MediaMuxerTest";
private static final boolean VERBOSE = false;
private static final int MAX_SAMPLE_SIZE = 256 * 1024;
private Resources mResources;
@Override
public void setContext(Context context) {
super.setContext(context);
mResources = context.getResources();
}
/**
* Test: make sure the muxer handles both video and audio tracks correctly.
*/
public void testVideoAudio() throws Exception {
int source = R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz;
String outputFile = "/sdcard/videoAudio.mp4";
cloneAndVerify(source, outputFile, 2);
}
/**
* Test: make sure the muxer handles audio track only file correctly.
*/
public void testAudioOnly() throws Exception {
int source = R.raw.sinesweepm4a;
String outputFile = "/sdcard/audioOnly.mp4";
cloneAndVerify(source, outputFile, 1);
}
/**
* Test: make sure the muxer handles video track only file correctly.
*/
public void testVideoOnly() throws Exception {
int source = R.raw.video_only_176x144_3gp_h263_25fps;
String outputFile = "/sdcard/videoOnly.mp4";
cloneAndVerify(source, outputFile, 1);
}
/**
* Tests: make sure the muxer handles exceptions correctly.
* <br> Throws exception b/c start() is not called.
* <br> Throws exception b/c 2 video tracks were added.
* <br> Throws exception b/c 2 audio tracks were added.
* <br> Throws exception b/c 3 tracks were added.
* <br> Throws exception b/c no tracks was added.
* <br> Throws exception b/c a wrong format.
*/
public void testIllegalStateExceptions() throws IOException {
String outputFile = "/sdcard/muxerExceptions.mp4";
MediaMuxer muxer;
// Throws exception b/c start() is not called.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
muxer.addTrack(MediaFormat.createVideoFormat("video/mp4", 480, 320));
try {
muxer.stop();
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
// Throws exception b/c 2 video tracks were added.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
muxer.addTrack(MediaFormat.createVideoFormat("video/mp4", 480, 320));
try {
muxer.addTrack(MediaFormat.createVideoFormat("video/mp4", 480, 320));
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
// Throws exception b/c 2 audio tracks were added.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
muxer.addTrack(MediaFormat.createAudioFormat("audio/mp4", 48000, 1));
try {
muxer.addTrack(MediaFormat.createAudioFormat("audio/mp4", 48000, 1));
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
// Throws exception b/c 3 tracks were added.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
muxer.addTrack(MediaFormat.createVideoFormat("video/mp4", 480, 320));
muxer.addTrack(MediaFormat.createAudioFormat("audio/mp4", 48000, 1));
try {
muxer.addTrack(MediaFormat.createVideoFormat("video/mp4", 480, 320));
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
// Throws exception b/c no tracks was added.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
try {
muxer.start();
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
// Throws exception b/c a wrong format.
muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
try {
muxer.addTrack(MediaFormat.createVideoFormat("vidoe/mp4", 480, 320));
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
}
new File(outputFile).delete();
}
/**
* Using the MediaMuxer to clone a media file.
*/
private void cloneMediaUsingMuxer(int srcMedia, String dstMediaPath,
int expectedTrackCount) throws IOException {
// Set up MediaExtractor to read from the source.
AssetFileDescriptor srcFd = mResources.openRawResourceFd(srcMedia);
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(srcFd.getFileDescriptor(), srcFd.getStartOffset(),
srcFd.getLength());
int trackCount = extractor.getTrackCount();
assertEquals("wrong number of tracks", expectedTrackCount, trackCount);
// Set up MediaMuxer for the destination.
MediaMuxer muxer;
muxer = new MediaMuxer(dstMediaPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// Set up the tracks.
HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount);
for (int i = 0; i < trackCount; i++) {
extractor.selectTrack(i);
MediaFormat format = extractor.getTrackFormat(i);
int dstIndex = muxer.addTrack(format);
indexMap.put(i, dstIndex);
}
// Copy the samples from MediaExtractor to MediaMuxer.
boolean sawEOS = false;
int bufferSize = MAX_SAMPLE_SIZE;
int frameCount = 0;
int offset = 100;
ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
BufferInfo bufferInfo = new BufferInfo();
muxer.start();
while (!sawEOS) {
bufferInfo.offset = offset;
bufferInfo.size = extractor.readSampleData(dstBuf, offset);
if (bufferInfo.size < 0) {
if (VERBOSE) {
Log.d(TAG, "saw input EOS.");
}
sawEOS = true;
bufferInfo.size = 0;
} else {
bufferInfo.presentationTimeUs = extractor.getSampleTime();
bufferInfo.flags = extractor.getSampleFlags();
int trackIndex = extractor.getSampleTrackIndex();
muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
bufferInfo);
extractor.advance();
frameCount++;
if (VERBOSE) {
Log.d(TAG, "Frame (" + frameCount + ") " +
"PresentationTimeUs:" + bufferInfo.presentationTimeUs +
" Flags:" + bufferInfo.flags +
" TrackIndex:" + trackIndex +
" Size(KB) " + bufferInfo.size / 1024);
}
}
}
muxer.stop();
muxer.release();
srcFd.close();
return;
}
/**
* Clones a media file and then compares against the source file to make
* sure they match.
*/
private void cloneAndVerify(int srcMedia, String outputMediaFile,
int expectedTrackCount) throws IOException {
try {
cloneMediaUsingMuxer(srcMedia, outputMediaFile, expectedTrackCount);
verifyAttributesMatch(srcMedia, outputMediaFile);
// Check the sample on 1s and 0.5s.
verifySamplesMatch(srcMedia, outputMediaFile, 1000000);
verifySamplesMatch(srcMedia, outputMediaFile, 500000);
} finally {
new File(outputMediaFile).delete();
}
}
/**
* Compares some attributes using MediaPlayer to make sure the cloned
* media file matches the source file.
*/
private void verifyAttributesMatch(int srcMedia, String testMediaPath) {
AssetFileDescriptor testFd = mResources.openRawResourceFd(srcMedia);
MediaPlayer playerSrc = new MediaPlayer();
MediaPlayer playerTest = new MediaPlayer();
try {
playerSrc.setDataSource(testFd.getFileDescriptor(),
testFd.getStartOffset(), testFd.getLength());
playerTest.setDataSource(testMediaPath);
playerSrc.prepare();
playerTest.prepare();
int durationSrc = playerSrc.getDuration();
int durationTest = playerTest.getDuration();
int heightSrc = playerSrc.getVideoHeight();
int heightTest = playerTest.getVideoHeight();
int widthSrc = playerSrc.getVideoWidth();
int widthTest = playerTest.getVideoWidth();
if (VERBOSE) {
Log.d(TAG, "Source video info : width " + widthSrc + " height "
+ heightSrc + " duration " + durationSrc);
Log.d(TAG, "Test video info : width " + widthTest + " height "
+ heightTest + " duration " + durationTest);
}
assertEquals("Different duration", durationSrc, durationTest);
assertEquals("Different height", heightSrc, heightTest);
assertEquals("Different width", widthSrc, widthTest);
playerSrc.stop();
playerTest.stop();
playerSrc.release();
playerTest.release();
} catch (Exception e) {
// Don't expect seeing any exception here.
assertTrue("verifyTwoVideos failed!", false);
}
}
/**
* Uses 2 MediaExtractor, seeking to the same position, reads the sample and
* makes sure the samples match.
*/
private void verifySamplesMatch(int srcMedia, String testMediaPath,
int seekToUs) {
AssetFileDescriptor testFd = mResources.openRawResourceFd(srcMedia);
MediaExtractor extractorSrc = new MediaExtractor();
extractorSrc.setDataSource(testFd.getFileDescriptor(),
testFd.getStartOffset(), testFd.getLength());
int trackCount = extractorSrc.getTrackCount();
MediaExtractor extractorTest = new MediaExtractor();
extractorTest.setDataSource(testMediaPath);
assertEquals("wrong number of tracks", trackCount,
extractorTest.getTrackCount());
// Make sure the format is the same and select them
for (int i = 0; i < trackCount; i++) {
MediaFormat formatSrc = extractorSrc.getTrackFormat(i);
MediaFormat formatTest = extractorTest.getTrackFormat(i);
String mimeIn = formatSrc.getString(MediaFormat.KEY_MIME);
String mimeOut = formatTest.getString(MediaFormat.KEY_MIME);
if (!(mimeIn.equals(mimeOut))) {
fail("format didn't match on track No." + i +
formatSrc.toString() + "\n" + formatTest.toString());
}
extractorSrc.selectTrack(i);
extractorTest.selectTrack(i);
}
// Pick a time and try to compare the frame.
extractorSrc.seekTo(seekToUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
extractorTest.seekTo(seekToUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
int bufferSize = MAX_SAMPLE_SIZE;
ByteBuffer byteBufSrc = ByteBuffer.allocate(bufferSize);
ByteBuffer byteBufTest = ByteBuffer.allocate(bufferSize);
extractorSrc.readSampleData(byteBufSrc, 0);
extractorTest.readSampleData(byteBufTest, 0);
if (!(byteBufSrc.equals(byteBufTest))) {
fail("byteBuffer didn't match");
}
}
}