Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright 2018 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 package android.media.cts;
     17 
     18 import android.content.Context;
     19 import android.content.pm.PackageManager;
     20 import android.content.res.AssetFileDescriptor;
     21 import android.content.res.Resources;
     22 import android.media.AudioAttributes;
     23 import android.media.AudioManager;
     24 import android.media.DataSourceDesc;
     25 import android.media.MediaPlayer2;
     26 import android.media.MediaTimestamp;
     27 import android.media.TimedMetaData;
     28 import android.media.TimedText;
     29 import android.media.cts.TestUtils.Monitor;
     30 import android.net.Uri;
     31 import android.os.PersistableBundle;
     32 import android.test.ActivityInstrumentationTestCase2;
     33 import android.view.SurfaceHolder;
     34 
     35 import com.android.compatibility.common.util.MediaUtils;
     36 
     37 import java.io.IOException;
     38 import java.net.HttpCookie;
     39 import java.util.ArrayList;
     40 import java.util.List;
     41 import java.util.Map;
     42 import java.util.Set;
     43 import java.util.concurrent.ExecutorService;
     44 import java.util.concurrent.Executors;
     45 import java.util.logging.Logger;
     46 
     47 /**
     48  * Base class for tests which use MediaPlayer2 to play audio or video.
     49  */
     50 public class MediaPlayer2TestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> {
     51     private static final Logger LOG = Logger.getLogger(MediaPlayer2TestBase.class.getName());
     52 
     53     protected static final int SLEEP_TIME = 1000;
     54     protected static final int LONG_SLEEP_TIME = 6000;
     55     protected static final int STREAM_RETRIES = 20;
     56     protected static boolean sUseScaleToFitMode = false;
     57 
     58     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
     59     protected Monitor mOnVideoRenderingStartCalled = new Monitor();
     60     protected Monitor mOnBufferingUpdateCalled = new Monitor();
     61     protected Monitor mOnPrepareCalled = new Monitor();
     62     protected Monitor mOnPlayCalled = new Monitor();
     63     protected Monitor mOnDeselectTrackCalled = new Monitor();
     64     protected Monitor mOnSeekCompleteCalled = new Monitor();
     65     protected Monitor mOnCompletionCalled = new Monitor();
     66     protected Monitor mOnInfoCalled = new Monitor();
     67     protected Monitor mOnErrorCalled = new Monitor();
     68     protected int mCallStatus;
     69 
     70     protected Context mContext;
     71     protected Resources mResources;
     72 
     73     protected ExecutorService mExecutor;
     74 
     75     protected MediaPlayer2 mPlayer = null;
     76     protected MediaPlayer2 mPlayer2 = null;
     77     protected MediaStubActivity mActivity;
     78 
     79     protected final Object mEventCbLock = new Object();
     80     protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks =
     81             new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
     82     protected final Object mEventCbLock2 = new Object();
     83     protected List<MediaPlayer2.MediaPlayer2EventCallback> mEventCallbacks2 =
     84             new ArrayList<MediaPlayer2.MediaPlayer2EventCallback>();
     85 
     86     // convenience functions to create MediaPlayer2
     87     protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri) {
     88         return createMediaPlayer2(context, uri, null);
     89     }
     90 
     91     protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri,
     92             SurfaceHolder holder) {
     93         AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
     94         int s = am.generateAudioSessionId();
     95         return createMediaPlayer2(context, uri, holder, null, s > 0 ? s : 0);
     96     }
     97 
     98     protected static MediaPlayer2 createMediaPlayer2(Context context, Uri uri, SurfaceHolder holder,
     99             AudioAttributes audioAttributes, int audioSessionId) {
    100         try {
    101             MediaPlayer2 mp = MediaPlayer2.create();
    102             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    103                     new AudioAttributes.Builder().build();
    104             mp.setAudioAttributes(aa);
    105             mp.setAudioSessionId(audioSessionId);
    106             mp.setDataSource(new DataSourceDesc.Builder()
    107                     .setDataSource(context, uri)
    108                     .build());
    109             if (holder != null) {
    110                 mp.setDisplay(holder);
    111             }
    112             Monitor onPrepareCalled = new Monitor();
    113             ExecutorService executor = Executors.newFixedThreadPool(1);
    114             MediaPlayer2.MediaPlayer2EventCallback ecb =
    115                 new MediaPlayer2.MediaPlayer2EventCallback() {
    116                     @Override
    117                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    118                         if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
    119                             onPrepareCalled.signal();
    120                         }
    121                     }
    122                 };
    123             mp.setMediaPlayer2EventCallback(executor, ecb);
    124             mp.prepare();
    125             onPrepareCalled.waitForSignal();
    126             mp.clearMediaPlayer2EventCallback();
    127             executor.shutdown();
    128             return mp;
    129         } catch (IllegalArgumentException ex) {
    130             LOG.warning("create failed:" + ex);
    131             // fall through
    132         } catch (SecurityException ex) {
    133             LOG.warning("create failed:" + ex);
    134             // fall through
    135         } catch (InterruptedException ex) {
    136             LOG.warning("create failed:" + ex);
    137             // fall through
    138         }
    139         return null;
    140     }
    141 
    142     protected static MediaPlayer2 createMediaPlayer2(Context context, int resid) {
    143         AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
    144         int s = am.generateAudioSessionId();
    145         return createMediaPlayer2(context, resid, null, s > 0 ? s : 0);
    146     }
    147 
    148     protected static MediaPlayer2 createMediaPlayer2(Context context, int resid,
    149             AudioAttributes audioAttributes, int audioSessionId) {
    150         try {
    151             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
    152             if (afd == null) {
    153                 return null;
    154             }
    155 
    156             MediaPlayer2 mp = MediaPlayer2.create();
    157 
    158             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    159                     new AudioAttributes.Builder().build();
    160             mp.setAudioAttributes(aa);
    161             mp.setAudioSessionId(audioSessionId);
    162 
    163             mp.setDataSource(new DataSourceDesc.Builder()
    164                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
    165                     .build());
    166 
    167             Monitor onPrepareCalled = new Monitor();
    168             ExecutorService executor = Executors.newFixedThreadPool(1);
    169             MediaPlayer2.MediaPlayer2EventCallback ecb =
    170                 new MediaPlayer2.MediaPlayer2EventCallback() {
    171                     @Override
    172                     public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    173                         if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
    174                             onPrepareCalled.signal();
    175                         }
    176                     }
    177                 };
    178             mp.setMediaPlayer2EventCallback(executor, ecb);
    179             mp.prepare();
    180             onPrepareCalled.waitForSignal();
    181             mp.clearMediaPlayer2EventCallback();
    182             afd.close();
    183             executor.shutdown();
    184             return mp;
    185         } catch (IOException ex) {
    186             LOG.warning("create failed:" + ex);
    187             // fall through
    188         } catch (IllegalArgumentException ex) {
    189             LOG.warning("create failed:" + ex);
    190             // fall through
    191         } catch (SecurityException ex) {
    192             LOG.warning("create failed:" + ex);
    193             // fall through
    194         } catch (InterruptedException ex) {
    195             LOG.warning("create failed:" + ex);
    196             // fall through
    197         }
    198         return null;
    199     }
    200 
    201     public MediaPlayer2TestBase() {
    202         super(MediaStubActivity.class);
    203     }
    204 
    205     @Override
    206     protected void setUp() throws Exception {
    207         super.setUp();
    208         mActivity = getActivity();
    209         getInstrumentation().waitForIdleSync();
    210         try {
    211             runTestOnUiThread(new Runnable() {
    212                 public void run() {
    213                     mPlayer = MediaPlayer2.create();
    214                     mPlayer2 = MediaPlayer2.create();
    215                 }
    216             });
    217         } catch (Throwable e) {
    218             e.printStackTrace();
    219             fail();
    220         }
    221         mContext = getInstrumentation().getTargetContext();
    222         mResources = mContext.getResources();
    223         mExecutor = Executors.newFixedThreadPool(1);
    224 
    225         setUpMP2ECb(mPlayer, mEventCbLock, mEventCallbacks);
    226         setUpMP2ECb(mPlayer2, mEventCbLock2, mEventCallbacks2);
    227     }
    228 
    229     @Override
    230     protected void tearDown() throws Exception {
    231         if (mPlayer != null) {
    232             mPlayer.close();
    233             mPlayer = null;
    234         }
    235         if (mPlayer2 != null) {
    236             mPlayer2.close();
    237             mPlayer2 = null;
    238         }
    239         mExecutor.shutdown();
    240         mActivity = null;
    241         super.tearDown();
    242     }
    243 
    244     protected void setUpMP2ECb(MediaPlayer2 mp, Object cbLock,
    245             List<MediaPlayer2.MediaPlayer2EventCallback> ecbs) {
    246         mp.setMediaPlayer2EventCallback(mExecutor, new MediaPlayer2.MediaPlayer2EventCallback() {
    247             @Override
    248             public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
    249                 synchronized (cbLock) {
    250                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    251                         ecb.onVideoSizeChanged(mp, dsd, w, h);
    252                     }
    253                 }
    254             }
    255 
    256             @Override
    257             public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
    258                 synchronized (cbLock) {
    259                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    260                         ecb.onTimedText(mp, dsd, text);
    261                     }
    262                 }
    263             }
    264 
    265             @Override
    266             public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
    267                     TimedMetaData data) {
    268                 synchronized (cbLock) {
    269                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    270                         ecb.onTimedMetaDataAvailable(mp, dsd, data);
    271                     }
    272                 }
    273             }
    274 
    275             @Override
    276             public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    277                 synchronized (cbLock) {
    278                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    279                         ecb.onError(mp, dsd, what, extra);
    280                     }
    281                 }
    282             }
    283 
    284             @Override
    285             public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    286                 synchronized (cbLock) {
    287                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    288                         ecb.onInfo(mp, dsd, what, extra);
    289                     }
    290                 }
    291             }
    292 
    293             @Override
    294             public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
    295                 synchronized (cbLock) {
    296                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    297                         ecb.onCallCompleted(mp, dsd, what, status);
    298                     }
    299                 }
    300             }
    301 
    302             @Override
    303             public void onMediaTimeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
    304                     MediaTimestamp timestamp) {
    305                 synchronized (cbLock) {
    306                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    307                         ecb.onMediaTimeChanged(mp, dsd, timestamp);
    308                     }
    309                 }
    310             }
    311 
    312             @Override
    313             public void onCommandLabelReached(MediaPlayer2 mp, Object label) {
    314                 synchronized (cbLock) {
    315                     for (MediaPlayer2.MediaPlayer2EventCallback ecb : ecbs) {
    316                         ecb.onCommandLabelReached(mp, label);
    317                     }
    318                 }
    319             }
    320         });
    321     }
    322 
    323     // returns true on success
    324     protected boolean loadResource(int resid) throws Exception {
    325         if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
    326             return false;
    327         }
    328 
    329         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
    330         try {
    331             mPlayer.setDataSource(new DataSourceDesc.Builder()
    332                     .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
    333                     .build());
    334 
    335             // Although it is only meant for video playback, it should not
    336             // cause issues for audio-only playback.
    337             int videoScalingMode = sUseScaleToFitMode?
    338                                     MediaPlayer2.VIDEO_SCALING_MODE_SCALE_TO_FIT
    339                                   : MediaPlayer2.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
    340 
    341             mPlayer.setVideoScalingMode(videoScalingMode);
    342         } finally {
    343             // TODO: close afd only after setDataSource is confirmed.
    344             // afd.close();
    345         }
    346         sUseScaleToFitMode = !sUseScaleToFitMode;  // Alternate the scaling mode
    347         return true;
    348     }
    349 
    350     protected DataSourceDesc createDataSourceDesc(int resid) throws Exception {
    351         if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
    352             return null;
    353         }
    354 
    355         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
    356         return new DataSourceDesc.Builder()
    357                 .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
    358                 .build();
    359     }
    360 
    361     protected boolean checkLoadResource(int resid) throws Exception {
    362         return MediaUtils.check(loadResource(resid), "no decoder found");
    363     }
    364 
    365     protected void loadSubtitleSource(int resid) throws Exception {
    366         AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
    367         try {
    368             mPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
    369                       afd.getLength(), MediaPlayer2.MEDIA_MIMETYPE_TEXT_SUBRIP);
    370         } finally {
    371             afd.close();
    372         }
    373     }
    374 
    375     protected void playLiveVideoTest(String path, int playTime) throws Exception {
    376         playVideoWithRetries(path, null, null, playTime);
    377     }
    378 
    379     protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
    380         playVideoWithRetries(path, -1, -1, playTime);
    381     }
    382 
    383     protected void playVideoTest(String path, int width, int height) throws Exception {
    384         playVideoWithRetries(path, width, height, 0);
    385     }
    386 
    387     protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
    388             throws Exception {
    389         boolean playedSuccessfully = false;
    390         final Uri uri = Uri.parse(path);
    391         for (int i = 0; i < STREAM_RETRIES; i++) {
    392           try {
    393             mPlayer.setDataSource(new DataSourceDesc.Builder()
    394                     .setDataSource(mContext, uri)
    395                     .build());
    396             playLoadedVideo(width, height, playTime);
    397             playedSuccessfully = true;
    398             break;
    399           } catch (PrepareFailedException e) {
    400             // prepare() can fail because of network issues, so try again
    401             LOG.warning("prepare() failed on try " + i + ", trying playback again");
    402           }
    403         }
    404         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
    405     }
    406 
    407     protected void playVideoTest(int resid, int width, int height) throws Exception {
    408         if (!checkLoadResource(resid)) {
    409             return; // skip
    410         }
    411 
    412         playLoadedVideo(width, height, 0);
    413     }
    414 
    415     protected void playLiveVideoTest(
    416             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
    417             int playTime) throws Exception {
    418         playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
    419     }
    420 
    421     protected void playVideoWithRetries(
    422             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
    423             Integer width, Integer height, int playTime) throws Exception {
    424         boolean playedSuccessfully = false;
    425         for (int i = 0; i < STREAM_RETRIES; i++) {
    426             try {
    427                 mPlayer.setDataSource(new DataSourceDesc.Builder()
    428                         .setDataSource(getInstrumentation().getTargetContext(),
    429                             uri, headers, cookies)
    430                         .build());
    431                 playLoadedVideo(width, height, playTime);
    432                 playedSuccessfully = true;
    433                 break;
    434             } catch (PrepareFailedException e) {
    435                 // prepare() can fail because of network issues, so try again
    436                 // playLoadedVideo already has reset the player so we can try again safely.
    437                 LOG.warning("prepare() failed on try " + i + ", trying playback again");
    438             }
    439         }
    440         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
    441     }
    442 
    443     /**
    444      * Play a video which has already been loaded with setDataSource().
    445      *
    446      * @param width width of the video to verify, or null to skip verification
    447      * @param height height of the video to verify, or null to skip verification
    448      * @param playTime length of time to play video, or 0 to play entire video.
    449      * with a non-negative value, this method stops the playback after the length of
    450      * time or the duration the video is elapsed. With a value of -1,
    451      * this method simply starts the video and returns immediately without
    452      * stoping the video playback.
    453      */
    454     protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
    455             throws Exception {
    456         final float volume = 0.5f;
    457 
    458         boolean audioOnly = (width != null && width.intValue() == -1) ||
    459                 (height != null && height.intValue() == -1);
    460 
    461         mPlayer.setDisplay(mActivity.getSurfaceHolder());
    462         mPlayer.setScreenOnWhilePlaying(true);
    463 
    464         synchronized (mEventCbLock) {
    465             mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
    466                 @Override
    467                 public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int w, int h) {
    468                     if (w == 0 && h == 0) {
    469                         // A size of 0x0 can be sent initially one time when using NuPlayer.
    470                         assertFalse(mOnVideoSizeChangedCalled.isSignalled());
    471                         return;
    472                     }
    473                     mOnVideoSizeChangedCalled.signal();
    474                     if (width != null) {
    475                         assertEquals(width.intValue(), w);
    476                     }
    477                     if (height != null) {
    478                         assertEquals(height.intValue(), h);
    479                     }
    480                 }
    481 
    482                 @Override
    483                 public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    484                     fail("Media player had error " + what + " playing video");
    485                 }
    486 
    487                 @Override
    488                 public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    489                     if (what == MediaPlayer2.MEDIA_INFO_VIDEO_RENDERING_START) {
    490                         mOnVideoRenderingStartCalled.signal();
    491                     } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
    492                         mOnPrepareCalled.signal();
    493                     }
    494                 }
    495 
    496                 @Override
    497                 public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
    498                         int what, int status) {
    499                     if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
    500                         mOnPlayCalled.signal();
    501                     }
    502                 }
    503             });
    504         }
    505         try {
    506           mOnPrepareCalled.reset();
    507           mPlayer.prepare();
    508           mOnPrepareCalled.waitForSignal();
    509         } catch (Exception e) {
    510           mPlayer.reset();
    511           throw new PrepareFailedException();
    512         }
    513 
    514         mOnPlayCalled.reset();
    515         mPlayer.play();
    516         mOnPlayCalled.waitForSignal();
    517         if (!audioOnly) {
    518             mOnVideoSizeChangedCalled.waitForSignal();
    519             mOnVideoRenderingStartCalled.waitForSignal();
    520         }
    521         mPlayer.setPlayerVolume(volume);
    522 
    523         // waiting to complete
    524         if (playTime == -1) {
    525             return;
    526         } else if (playTime == 0) {
    527             while (mPlayer.isPlaying()) {
    528                 Thread.sleep(SLEEP_TIME);
    529             }
    530         } else {
    531             Thread.sleep(playTime);
    532         }
    533 
    534         // validate a few MediaMetrics.
    535         PersistableBundle metrics = mPlayer.getMetrics();
    536         if (metrics == null) {
    537             fail("MediaPlayer2.getMetrics() returned null metrics");
    538         } else if (metrics.isEmpty()) {
    539             fail("MediaPlayer2.getMetrics() returned empty metrics");
    540         } else {
    541 
    542             int size = metrics.size();
    543             Set<String> keys = metrics.keySet();
    544 
    545             if (keys == null) {
    546                 fail("MediaMetricsSet returned no keys");
    547             } else if (keys.size() != size) {
    548                 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
    549             }
    550 
    551             // we played something; so one of these should be non-null
    552             String vmime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_VIDEO, null);
    553             String amime = metrics.getString(MediaPlayer2.MetricsConstants.MIME_TYPE_AUDIO, null);
    554             if (vmime == null && amime == null) {
    555                 fail("getMetrics() returned neither video nor audio mime value");
    556             }
    557 
    558             long duration = metrics.getLong(MediaPlayer2.MetricsConstants.DURATION, -2);
    559             if (duration == -2) {
    560                 fail("getMetrics() didn't return a duration");
    561             }
    562             long playing = metrics.getLong(MediaPlayer2.MetricsConstants.PLAYING, -2);
    563             if (playing == -2) {
    564                 fail("getMetrics() didn't return a playing time");
    565             }
    566             if (!keys.contains(MediaPlayer2.MetricsConstants.PLAYING)) {
    567                 fail("MediaMetricsSet.keys() missing: " + MediaPlayer2.MetricsConstants.PLAYING);
    568             }
    569         }
    570 
    571         mPlayer.stop();
    572     }
    573 
    574     private static class PrepareFailedException extends Exception {}
    575 
    576     public boolean isTv() {
    577         PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
    578         return pm.hasSystemFeature(pm.FEATURE_TELEVISION)
    579                 && pm.hasSystemFeature(pm.FEATURE_LEANBACK);
    580     }
    581 
    582     public boolean checkTv() {
    583         return MediaUtils.check(isTv(), "not a TV");
    584     }
    585 
    586     protected void setOnErrorListener() {
    587         synchronized (mEventCbLock) {
    588             mEventCallbacks.add(new MediaPlayer2.MediaPlayer2EventCallback() {
    589                 @Override
    590                 public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
    591                     mOnErrorCalled.signal();
    592                 }
    593             });
    594         }
    595     }
    596 }
    597