• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.cts;
18 
19 import android.content.Context;
20 import android.content.res.AssetFileDescriptor;
21 import android.content.res.Resources;
22 import android.media.MediaCodec.BufferInfo;
23 import android.media.MediaExtractor;
24 import android.media.MediaFormat;
25 import android.media.MediaMetadataRetriever;
26 import android.media.MediaMuxer;
27 import android.test.AndroidTestCase;
28 import android.util.Log;
29 
30 import android.media.cts.R;
31 
32 import java.io.File;
33 import java.io.IOException;
34 import java.nio.ByteBuffer;
35 import java.util.HashMap;
36 
37 public class MediaMuxerTest extends AndroidTestCase {
38     private static final String TAG = "MediaMuxerTest";
39     private static final boolean VERBOSE = false;
40     private static final int MAX_SAMPLE_SIZE = 256 * 1024;
41     private static final float LATITUDE = 0.0000f;
42     private static final float LONGITUDE  = -180.0f;
43     private static final float BAD_LATITUDE = 91.0f;
44     private static final float BAD_LONGITUDE = -181.0f;
45     private static final float TOLERANCE = 0.0002f;
46     private Resources mResources;
47 
48     @Override
setContext(Context context)49     public void setContext(Context context) {
50         super.setContext(context);
51         mResources = context.getResources();
52     }
53 
54     /**
55      * Test: make sure the muxer handles both video and audio tracks correctly.
56      */
testVideoAudio()57     public void testVideoAudio() throws Exception {
58         int source = R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz;
59         String outputFile = File.createTempFile("MediaMuxerTest_testAudioVideo", ".mp4")
60                 .getAbsolutePath();
61         cloneAndVerify(source, outputFile, 2, 90);
62     }
63 
64     /**
65      * Test: make sure the muxer handles audio track only file correctly.
66      */
testAudioOnly()67     public void testAudioOnly() throws Exception {
68         int source = R.raw.sinesweepm4a;
69         String outputFile = File.createTempFile("MediaMuxerTest_testAudioOnly", ".mp4")
70                 .getAbsolutePath();
71         cloneAndVerify(source, outputFile, 1, -1);
72     }
73 
74     /**
75      * Test: make sure the muxer handles video track only file correctly.
76      */
testVideoOnly()77     public void testVideoOnly() throws Exception {
78         int source = R.raw.video_only_176x144_3gp_h263_25fps;
79         String outputFile = File.createTempFile("MediaMuxerTest_videoOnly", ".mp4")
80                 .getAbsolutePath();
81         cloneAndVerify(source, outputFile, 1, 180);
82     }
83 
84     /**
85      * Tests: make sure the muxer handles exceptions correctly.
86      * <br> Throws exception b/c start() is not called.
87      * <br> Throws exception b/c 2 video tracks were added.
88      * <br> Throws exception b/c 2 audio tracks were added.
89      * <br> Throws exception b/c 3 tracks were added.
90      * <br> Throws exception b/c no tracks was added.
91      * <br> Throws exception b/c a wrong format.
92      */
testIllegalStateExceptions()93     public void testIllegalStateExceptions() throws IOException {
94         String outputFile = File.createTempFile("MediaMuxerTest_testISEs", ".mp4")
95                 .getAbsolutePath();
96         MediaMuxer muxer;
97 
98         // Throws exception b/c start() is not called.
99         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
100         muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
101 
102         try {
103             muxer.stop();
104             fail("should throw IllegalStateException.");
105         } catch (IllegalStateException e) {
106             // expected
107         } finally {
108             muxer.release();
109         }
110 
111         // Throws exception b/c 2 video tracks were added.
112         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
113         muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
114 
115         try {
116             muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
117             fail("should throw IllegalStateException.");
118         } catch (IllegalStateException e) {
119             // expected
120         } finally {
121             muxer.release();
122         }
123 
124         // Throws exception b/c 2 audio tracks were added.
125         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
126         muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
127         try {
128             muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
129             fail("should throw IllegalStateException.");
130         } catch (IllegalStateException e) {
131             // expected
132         } finally {
133             muxer.release();
134         }
135 
136         // Throws exception b/c 3 tracks were added.
137         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
138         muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
139         muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
140         try {
141 
142             muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
143             fail("should throw IllegalStateException.");
144         } catch (IllegalStateException e) {
145             // expected
146         } finally {
147             muxer.release();
148         }
149 
150         // Throws exception b/c no tracks was added.
151         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
152         try {
153             muxer.start();
154             fail("should throw IllegalStateException.");
155         } catch (IllegalStateException e) {
156             // expected
157         } finally {
158             muxer.release();
159         }
160 
161         // Throws exception b/c a wrong format.
162         muxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
163         try {
164             muxer.addTrack(MediaFormat.createVideoFormat("vidoe/mp4", 480, 320));
165             fail("should throw IllegalStateException.");
166         } catch (IllegalStateException e) {
167             // expected
168         } finally {
169             muxer.release();
170         }
171         new File(outputFile).delete();
172     }
173 
174     /**
175      * Using the MediaMuxer to clone a media file.
176      */
cloneMediaUsingMuxer(int srcMedia, String dstMediaPath, int expectedTrackCount, int degrees)177     private void cloneMediaUsingMuxer(int srcMedia, String dstMediaPath,
178             int expectedTrackCount, int degrees) throws IOException {
179         // Set up MediaExtractor to read from the source.
180         AssetFileDescriptor srcFd = mResources.openRawResourceFd(srcMedia);
181         MediaExtractor extractor = new MediaExtractor();
182         extractor.setDataSource(srcFd.getFileDescriptor(), srcFd.getStartOffset(),
183                 srcFd.getLength());
184 
185         int trackCount = extractor.getTrackCount();
186         assertEquals("wrong number of tracks", expectedTrackCount, trackCount);
187 
188         // Set up MediaMuxer for the destination.
189         MediaMuxer muxer;
190         muxer = new MediaMuxer(dstMediaPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
191 
192         // Set up the tracks.
193         HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount);
194         for (int i = 0; i < trackCount; i++) {
195             extractor.selectTrack(i);
196             MediaFormat format = extractor.getTrackFormat(i);
197             int dstIndex = muxer.addTrack(format);
198             indexMap.put(i, dstIndex);
199         }
200 
201         // Copy the samples from MediaExtractor to MediaMuxer.
202         boolean sawEOS = false;
203         int bufferSize = MAX_SAMPLE_SIZE;
204         int frameCount = 0;
205         int offset = 100;
206 
207         ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
208         BufferInfo bufferInfo = new BufferInfo();
209 
210         if (degrees >= 0) {
211             muxer.setOrientationHint(degrees);
212         }
213 
214         // Test setLocation out of bound cases
215         try {
216             muxer.setLocation(BAD_LATITUDE, LONGITUDE);
217             fail("setLocation succeeded with bad argument: [" + BAD_LATITUDE + "," + LONGITUDE
218                     + "]");
219         } catch (IllegalArgumentException e) {
220             // Expected
221         }
222         try {
223             muxer.setLocation(LATITUDE, BAD_LONGITUDE);
224             fail("setLocation succeeded with bad argument: [" + LATITUDE + "," + BAD_LONGITUDE
225                     + "]");
226         } catch (IllegalArgumentException e) {
227             // Expected
228         }
229 
230         muxer.setLocation(LATITUDE, LONGITUDE);
231 
232         muxer.start();
233         while (!sawEOS) {
234             bufferInfo.offset = offset;
235             bufferInfo.size = extractor.readSampleData(dstBuf, offset);
236 
237             if (bufferInfo.size < 0) {
238                 if (VERBOSE) {
239                     Log.d(TAG, "saw input EOS.");
240                 }
241                 sawEOS = true;
242                 bufferInfo.size = 0;
243             } else {
244                 bufferInfo.presentationTimeUs = extractor.getSampleTime();
245                 bufferInfo.flags = extractor.getSampleFlags();
246                 int trackIndex = extractor.getSampleTrackIndex();
247 
248                 muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
249                         bufferInfo);
250                 extractor.advance();
251 
252                 frameCount++;
253                 if (VERBOSE) {
254                     Log.d(TAG, "Frame (" + frameCount + ") " +
255                             "PresentationTimeUs:" + bufferInfo.presentationTimeUs +
256                             " Flags:" + bufferInfo.flags +
257                             " TrackIndex:" + trackIndex +
258                             " Size(KB) " + bufferInfo.size / 1024);
259                 }
260             }
261         }
262 
263         muxer.stop();
264         muxer.release();
265         srcFd.close();
266         return;
267     }
268 
269     /**
270      * Clones a media file and then compares against the source file to make
271      * sure they match.
272      */
cloneAndVerify(int srcMedia, String outputMediaFile, int expectedTrackCount, int degrees)273     private void cloneAndVerify(int srcMedia, String outputMediaFile,
274             int expectedTrackCount, int degrees) throws IOException {
275         try {
276             cloneMediaUsingMuxer(srcMedia, outputMediaFile, expectedTrackCount, degrees);
277             verifyAttributesMatch(srcMedia, outputMediaFile, degrees);
278             verifyLocationInFile(outputMediaFile);
279             // Check the sample on 1s and 0.5s.
280             verifySamplesMatch(srcMedia, outputMediaFile, 1000000);
281             verifySamplesMatch(srcMedia, outputMediaFile, 500000);
282         } finally {
283             new File(outputMediaFile).delete();
284         }
285     }
286 
287     /**
288      * Compares some attributes using MediaMetadataRetriever to make sure the
289      * cloned media file matches the source file.
290      */
verifyAttributesMatch(int srcMedia, String testMediaPath, int degrees)291     private void verifyAttributesMatch(int srcMedia, String testMediaPath,
292             int degrees) {
293         AssetFileDescriptor testFd = mResources.openRawResourceFd(srcMedia);
294 
295         MediaMetadataRetriever retrieverSrc = new MediaMetadataRetriever();
296         retrieverSrc.setDataSource(testFd.getFileDescriptor(),
297                 testFd.getStartOffset(), testFd.getLength());
298 
299         MediaMetadataRetriever retrieverTest = new MediaMetadataRetriever();
300         retrieverTest.setDataSource(testMediaPath);
301 
302         String testDegrees = retrieverTest.extractMetadata(
303                 MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
304         if (testDegrees != null) {
305             assertEquals("Different degrees", degrees,
306                     Integer.parseInt(testDegrees));
307         }
308 
309         String heightSrc = retrieverSrc.extractMetadata(
310                 MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
311         String heightTest = retrieverTest.extractMetadata(
312                 MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
313         assertEquals("Different height", heightSrc,
314                 heightTest);
315 
316         String widthSrc = retrieverSrc.extractMetadata(
317                 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
318         String widthTest = retrieverTest.extractMetadata(
319                 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
320         assertEquals("Different height", widthSrc,
321                 widthTest);
322 
323         String durationSrc = retrieverSrc.extractMetadata(
324                 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
325         String durationTest = retrieverTest.extractMetadata(
326                 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
327         assertEquals("Different height", durationSrc,
328                 durationTest);
329 
330         retrieverSrc.release();
331         retrieverTest.release();
332     }
333 
334     /**
335      * Uses 2 MediaExtractor, seeking to the same position, reads the sample and
336      * makes sure the samples match.
337      */
verifySamplesMatch(int srcMedia, String testMediaPath, int seekToUs)338     private void verifySamplesMatch(int srcMedia, String testMediaPath,
339             int seekToUs) throws IOException {
340         AssetFileDescriptor testFd = mResources.openRawResourceFd(srcMedia);
341         MediaExtractor extractorSrc = new MediaExtractor();
342         extractorSrc.setDataSource(testFd.getFileDescriptor(),
343                 testFd.getStartOffset(), testFd.getLength());
344         int trackCount = extractorSrc.getTrackCount();
345 
346         MediaExtractor extractorTest = new MediaExtractor();
347         extractorTest.setDataSource(testMediaPath);
348 
349         assertEquals("wrong number of tracks", trackCount,
350                 extractorTest.getTrackCount());
351 
352         // Make sure the format is the same and select them
353         for (int i = 0; i < trackCount; i++) {
354             MediaFormat formatSrc = extractorSrc.getTrackFormat(i);
355             MediaFormat formatTest = extractorTest.getTrackFormat(i);
356 
357             String mimeIn = formatSrc.getString(MediaFormat.KEY_MIME);
358             String mimeOut = formatTest.getString(MediaFormat.KEY_MIME);
359             if (!(mimeIn.equals(mimeOut))) {
360                 fail("format didn't match on track No." + i +
361                         formatSrc.toString() + "\n" + formatTest.toString());
362             }
363             extractorSrc.selectTrack(i);
364             extractorTest.selectTrack(i);
365         }
366         // Pick a time and try to compare the frame.
367         extractorSrc.seekTo(seekToUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
368         extractorTest.seekTo(seekToUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
369 
370         int bufferSize = MAX_SAMPLE_SIZE;
371         ByteBuffer byteBufSrc = ByteBuffer.allocate(bufferSize);
372         ByteBuffer byteBufTest = ByteBuffer.allocate(bufferSize);
373 
374         extractorSrc.readSampleData(byteBufSrc, 0);
375         extractorTest.readSampleData(byteBufTest, 0);
376 
377         if (!(byteBufSrc.equals(byteBufTest))) {
378             fail("byteBuffer didn't match");
379         }
380     }
381 
verifyLocationInFile(String fileName)382     private void verifyLocationInFile(String fileName) {
383         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
384         retriever.setDataSource(fileName);
385         String location = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
386         assertNotNull("No location information found in file " + fileName, location);
387 
388 
389         // parsing String location and recover the location information in floats
390         // Make sure the tolerance is very small - due to rounding errors.
391 
392         // Get the position of the -/+ sign in location String, which indicates
393         // the beginning of the longitude.
394         int minusIndex = location.lastIndexOf('-');
395         int plusIndex = location.lastIndexOf('+');
396 
397         assertTrue("+ or - is not found or found only at the beginning [" + location + "]",
398                 (minusIndex > 0 || plusIndex > 0));
399         int index = Math.max(minusIndex, plusIndex);
400 
401         float latitude = Float.parseFloat(location.substring(0, index - 1));
402         float longitude = Float.parseFloat(location.substring(index));
403         assertTrue("Incorrect latitude: " + latitude + " [" + location + "]",
404                 Math.abs(latitude - LATITUDE) <= TOLERANCE);
405         assertTrue("Incorrect longitude: " + longitude + " [" + location + "]",
406                 Math.abs(longitude - LONGITUDE) <= TOLERANCE);
407         retriever.release();
408     }
409 }
410 
411