blob: 58d803e0cf22131c797ed7bd304fa193638a53de [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.media.cts.R;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.MediaDataSource;
import android.media.MediaMetadataRetriever;
import android.graphics.Bitmap;
import android.support.test.filters.SmallTest;
import android.platform.test.annotations.RequiresDevice;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.compatibility.common.util.MediaUtils;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.media.MediaMetadataRetriever.OPTION_CLOSEST;
import static android.media.MediaMetadataRetriever.OPTION_CLOSEST_SYNC;
import static android.media.MediaMetadataRetriever.OPTION_NEXT_SYNC;
import static android.media.MediaMetadataRetriever.OPTION_PREVIOUS_SYNC;
import java.io.IOException;
@SmallTest
@RequiresDevice
public class MediaMetadataRetrieverTest extends AndroidTestCase {
private static final String TAG = "MediaMetadataRetrieverTest";
private static final boolean SAVE_BITMAP_OUTPUT = false;
protected Resources mResources;
protected MediaMetadataRetriever mRetriever;
private PackageManager mPackageManager;
@Override
protected void setUp() throws Exception {
super.setUp();
mResources = getContext().getResources();
mRetriever = new MediaMetadataRetriever();
mPackageManager = getContext().getPackageManager();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
mRetriever.release();
}
protected void setDataSourceFd(int resid) {
try {
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
} catch (Exception e) {
fail("Unable to open file");
}
}
protected TestMediaDataSource setDataSourceCallback(int resid) {
TestMediaDataSource ds = null;
try {
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
ds = TestMediaDataSource.fromAssetFd(afd);
mRetriever.setDataSource(ds);
} catch (Exception e) {
fail("Unable to open file");
}
return ds;
}
protected TestMediaDataSource getFaultyDataSource(int resid, boolean throwing) {
TestMediaDataSource ds = null;
try {
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
ds = TestMediaDataSource.fromAssetFd(afd);
if (throwing) {
ds.throwFromReadAt();
} else {
ds.returnFromReadAt(-2);
}
} catch (Exception e) {
fail("Unable to open file");
}
return ds;
}
public void test3gppMetadata() {
setDataSourceCallback(R.raw.testvideo);
assertEquals("Title was other than expected",
"Title", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE));
assertEquals("Artist was other than expected",
"UTF16LE エンディアン ",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST));
assertEquals("Album was other than expected",
"Test album",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
assertEquals("Track number was other than expected",
"10",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
assertEquals("Year was other than expected",
"2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
assertEquals("Date was other than expected",
"19040101T000000.000Z",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
assertNull("Writer was unexpected present",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
}
public void testID3v2Metadata() {
setDataSourceFd(R.raw.video_480x360_mp4_h264_500kbps_25fps_aac_stereo_128kbps_44100hz_id3v2);
assertEquals("Title was other than expected",
"Title", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE));
assertEquals("Artist was other than expected",
"UTF16LE エンディアン ",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST));
assertEquals("Album was other than expected",
"Test album",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
assertEquals("Track number was other than expected",
"10",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
assertEquals("Year was other than expected",
"2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
assertEquals("Date was other than expected",
"19700101T000000.000Z",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
assertNull("Writer was unexpectedly present",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
}
public void testLargeAlbumArt() {
setDataSourceFd(R.raw.largealbumart);
assertNotNull("couldn't retrieve album art", mRetriever.getEmbeddedPicture());
}
public void testSetDataSourceNullPath() {
try {
mRetriever.setDataSource((String)null);
fail("Expected IllegalArgumentException.");
} catch (IllegalArgumentException ex) {
// Expected, test passed.
}
}
public void testNullMediaDataSourceIsRejected() {
try {
mRetriever.setDataSource((MediaDataSource)null);
fail("Expected IllegalArgumentException.");
} catch (IllegalArgumentException ex) {
// Expected, test passed.
}
}
public void testMediaDataSourceIsClosedOnRelease() throws Exception {
TestMediaDataSource dataSource = setDataSourceCallback(R.raw.testvideo);
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
mRetriever.release();
assertTrue(dataSource.isClosed());
}
public void testRetrieveFailsIfMediaDataSourceThrows() throws Exception {
TestMediaDataSource ds = getFaultyDataSource(R.raw.testvideo, true /* throwing */);
try {
mRetriever.setDataSource(ds);
fail("Failed to throw exceptions");
} catch (RuntimeException e) {
assertTrue(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) == null);
}
}
public void testRetrieveFailsIfMediaDataSourceReturnsAnError() throws Exception {
TestMediaDataSource ds = getFaultyDataSource(R.raw.testvideo, false /* throwing */);
try {
mRetriever.setDataSource(ds);
fail("Failed to throw exceptions");
} catch (RuntimeException e) {
assertTrue(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) == null);
}
}
private void testThumbnail(int resId) {
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
MediaUtils.skipTest("no video codecs for resource");
return;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Resources resources = getContext().getResources();
AssetFileDescriptor afd = resources.openRawResourceFd(resId);
retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
try {
afd.close();
} catch (IOException e) {
fail("Unable to open file");
}
assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
}
public void testThumbnailH264() {
testThumbnail(R.raw.bbb_s4_1280x720_mp4_h264_mp31_8mbps_30fps_aac_he_mono_40kbps_44100hz);
}
public void testThumbnailH263() {
testThumbnail(R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz);
}
public void testThumbnailMPEG4() {
testThumbnail(R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz);
}
public void testThumbnailVP8() {
testThumbnail(R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz);
}
public void testThumbnailVP9() {
testThumbnail(R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz);
}
public void testThumbnailHEVC() {
testThumbnail(R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz);
}
/**
* The following tests verifies MediaMetadataRetriever.getFrameAtTime behavior.
*
* We use a simple stream with binary counter at the top to check which frame
* is actually captured. The stream is 30fps with 600 frames in total. It has
* I/P/B frames, with I interval of 30. Due to the encoding structure, pts starts
* at 66666 (instead of 0), so we have I frames at 66666, 1066666, ..., etc..
*
* For each seek option, we check the following five cases:
* 1) frame time falls right on a sync frame
* 2) frame time is near the middle of two sync frames but closer to the previous one
* 3) frame time is near the middle of two sync frames but closer to the next one
* 4) frame time is shortly before a sync frame
* 5) frame time is shortly after a sync frame
*/
public void testGetFrameAtTimePreviousSync() {
int[][] testCases = {
{ 2066666, 60 }, { 2500000, 60 }, { 2600000, 60 }, { 3000000, 60 }, { 3200000, 90}};
testGetFrameAtTime(OPTION_PREVIOUS_SYNC, testCases);
}
public void testGetFrameAtTimeNextSync() {
int[][] testCases = {
{ 2066666, 60 }, { 2500000, 90 }, { 2600000, 90 }, { 3000000, 90 }, { 3200000, 120}};
testGetFrameAtTime(OPTION_NEXT_SYNC, testCases);
}
public void testGetFrameAtTimeClosestSync() {
int[][] testCases = {
{ 2066666, 60 }, { 2500000, 60 }, { 2600000, 90 }, { 3000000, 90 }, { 3200000, 90}};
testGetFrameAtTime(OPTION_CLOSEST_SYNC, testCases);
}
public void testGetFrameAtTimeClosest() {
int[][] testCases = {
{ 2066666, 60 }, { 2500001, 73 }, { 2599999, 76 }, { 3016000, 88 }, { 3184000, 94}};
testGetFrameAtTime(OPTION_CLOSEST, testCases);
}
private void testGetFrameAtTime(int option, int[][] testCases) {
int resId = R.raw.binary_counter_320x240_30fps_600frames;
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
&& mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
MediaUtils.skipTest("no video codecs for resource on watch");
return;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Resources resources = getContext().getResources();
AssetFileDescriptor afd = resources.openRawResourceFd(resId);
retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
try {
afd.close();
} catch (IOException e) {
fail("Unable to close file");
}
for (int[] testCase : testCases) {
getVideoFrameAndVerify(retriever, testCase[0], testCase[1], option);
}
retriever.release();
}
private void getVideoFrameAndVerify(
MediaMetadataRetriever retriever, long timeUs, long expectedCounter, int option) {
try {
Bitmap bitmap = retriever.getFrameAtTime(timeUs, option);
if (bitmap == null) {
fail("Failed to get bitmap at time " + timeUs + " with option " + option);
}
assertEquals("Counter value incorrect at time " + timeUs + " with option " + option,
expectedCounter, CodecUtils.readBinaryCounterFromBitmap(bitmap));
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test" + timeUs + ".jpg");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
}
/**
* The following tests verifies MediaMetadataRetriever.getScaledFrameAtTime behavior.
*/
public void testGetScaledFrameAtTime() {
int resId = R.raw.binary_counter_320x240_30fps_600frames;
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
&& mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
MediaUtils.skipTest("no video codecs for resource on watch");
return;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Resources resources = getContext().getResources();
AssetFileDescriptor afd = resources.openRawResourceFd(resId);
retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
try {
afd.close();
} catch (IOException e) {
fail("Unable to close file");
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs*/ , OPTION_CLOSEST, 0 /*width*/, 120 /*height*/);
fail("Failed to receive exception");
} catch (IllegalArgumentException e) {
// Expect exception
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 0 /*height*/);
fail("Failed to receive exception");
} catch (IllegalArgumentException e) {
// Expect exception
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 120 /*height*/);
fail("Failed to receive exception");
} catch (IllegalArgumentException e) {
// Expect exception
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 140 /*width*/, -1 /*height*/);
fail("Failed to receive exception");
} catch (IllegalArgumentException e) {
// Expect exception
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, -1 /*width*/, -1 /*height*/);
fail("Failed to receive exception");
} catch (IllegalArgumentException e) {
// Expect exception
}
// Test desided size of 160 x 120. Return should be 160 x 120
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 120 /*height*/);
if (bitmap == null) {
fail("Failed to get scaled bitmap");
}
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test_160x120" + ".jpg");
}
if (bitmap.getWidth() != 160 /* width */) {
fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
}
if (bitmap.getHeight() != 120 /* height */) {
fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
// Test scaled up bitmap to 640 x 480. Return should be 640 x 480
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 640 /*width*/, 480 /*height*/);
if (bitmap == null) {
fail("Failed to get scaled bitmap");
}
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test_640x480" + ".jpg");
}
if (bitmap.getWidth() != 640 /* width */) {
fail("Bitmap width is " + bitmap.getWidth() + "Expect: 640");
}
if (bitmap.getHeight() != 480 /* height */) {
fail("Bitmap height is " + bitmap.getHeight() + "Expect: 480");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
// Test scaled up bitmap to 320 x 120. Return should be 160 x 120
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 320 /*width*/, 120 /*height*/);
if (bitmap == null) {
fail("Failed to get scaled bitmap");
}
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test_320x120" + ".jpg");
}
if (bitmap.getWidth() != 160 /* width */) {
fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
}
if (bitmap.getHeight() != 120 /* height */) {
fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
// Test scaled up bitmap to 160 x 240. Return should be 160 x 120
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 240 /*height*/);
if (bitmap == null) {
fail("Failed to get scaled bitmap");
}
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test_160x240" + ".jpg");
}
if (bitmap.getWidth() != 160 /* width */) {
fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
}
if (bitmap.getHeight() != 120 /* height */) {
fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
// Test scaled the video with aspect ratio
resId = R.raw.binary_counter_320x240_720x240_30fps_600frames;
afd = resources.openRawResourceFd(resId);
retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
try {
afd.close();
} catch (IOException e) {
fail("Unable to close file");
}
try {
Bitmap bitmap = retriever.getScaledFrameAtTime(
2066666 /*timeUs */, OPTION_CLOSEST, 330 /*width*/, 240 /*height*/);
if (bitmap == null) {
fail("Failed to get scaled bitmap");
}
if (SAVE_BITMAP_OUTPUT) {
CodecUtils.saveBitmapToFile(bitmap, "test_330x240" + ".jpg");
}
if (bitmap.getWidth() != 330 /* width */) {
fail("Bitmap width is " + bitmap.getWidth() + "Expect: 330");
}
if (bitmap.getHeight() != 110 /* height */) {
fail("Bitmap height is " + bitmap.getHeight() + "Expect: 110");
}
} catch (Exception e) {
fail("Exception getting bitmap: " + e);
}
}
}