Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2017 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 com.googlecode.android_scripting.facade.media;
     18 
     19 import android.app.Service;
     20 import android.media.MediaPlayer;
     21 import android.net.Uri;
     22 
     23 import com.googlecode.android_scripting.facade.EventFacade;
     24 import com.googlecode.android_scripting.facade.FacadeManager;
     25 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     26 import com.googlecode.android_scripting.rpc.Rpc;
     27 import com.googlecode.android_scripting.rpc.RpcDefault;
     28 import com.googlecode.android_scripting.rpc.RpcParameter;
     29 
     30 import java.util.HashMap;
     31 import java.util.Hashtable;
     32 import java.util.Map;
     33 import java.util.Set;
     34 import java.util.Map.Entry;
     35 
     36 /**
     37  * This facade exposes basic mediaPlayer functionality. <br>
     38  * <br>
     39  * <b>Usage Notes:</b><br>
     40  * mediaPlayerFacade maintains a list of media streams, identified by a user supplied tag. If the
     41  * tag is null or blank, this tag defaults to "default"<br>
     42  * Basic operation is: mediaPlayOpen("file:///sdcard/MP3/sample.mp3","mytag",true)<br>
     43  * This will look for a media file at /sdcard/MP3/sample.mp3. Other urls should work. If the file
     44  * exists and is playable, this will return a true otherwise it will return a false. <br>
     45  * If play=true, then the media file will play immediately, otherwise it will wait for a
     46  * {@link #mediaPlayStart mediaPlayerStart} command. <br>
     47  * When done with the resource, use {@link #mediaPlayClose mediaPlayClose} <br>
     48  * You can get information about the loaded media with {@link #mediaPlayInfo mediaPlayInfo} This
     49  * returns a map with the following elements:
     50  * <ul>
     51  * <li>"tag" - user supplied tag identifying this mediaPlayer.
     52  * <li>"loaded" - true if loaded, false if not. If false, no other elements are returned.
     53  * <li>"duration" - length of the media in milliseconds.
     54  * <li>"position" - current position of playback in milliseconds. Controlled by
     55  * {@link #mediaPlaySeek mediaPlaySeek}
     56  * <li>"isplaying" - shows whether media is playing. Controlled by {@link #mediaPlayPause
     57  * mediaPlayPause} and {@link #mediaPlayStart mediaPlayStart}
     58  * <li>"url" - the url used to open this media.
     59  * <li>"looping" - whether media will loop. Controlled by {@link #mediaPlaySetLooping
     60  * mediaPlaySetLooping}
     61  * </ul>
     62  * <br>
     63  * You can use {@link #mediaPlayList mediaPlayList} to get a list of the loaded tags. <br>
     64  * {@link #mediaIsPlaying mediaIsPlaying} will return true if the media is playing.<br>
     65  * <b>Events:</b><br>
     66  * A playing media will throw a <b>"media"</b> event on completion. NB: In remote mode, a media file
     67  * will continue playing after the script has finished unless an explicit {@link #mediaPlayClose
     68  * mediaPlayClose} event is called.
     69  *
     70  */
     71 
     72 public class MediaPlayerFacade extends RpcReceiver implements MediaPlayer.OnCompletionListener {
     73 
     74     private final Service mService;
     75     static private final Map<String, MediaPlayer> mPlayers = new Hashtable<String, MediaPlayer>();
     76     static private final Map<String, String> mUrls = new Hashtable<String, String>();
     77 
     78     private final EventFacade mEventFacade;
     79 
     80     public MediaPlayerFacade(FacadeManager manager) {
     81         super(manager);
     82         mService = manager.getService();
     83         mEventFacade = manager.getReceiver(EventFacade.class);
     84     }
     85 
     86     private String getDefault(String tag) {
     87         return (tag == null || tag.equals("")) ? "default" : tag;
     88     }
     89 
     90     private MediaPlayer getPlayer(String tag) {
     91         tag = getDefault(tag);
     92         return mPlayers.get(tag);
     93     }
     94 
     95     private String getUrl(String tag) {
     96         tag = getDefault(tag);
     97         return mUrls.get(tag);
     98     }
     99 
    100     private void putMp(String tag, MediaPlayer player, String url) {
    101         tag = getDefault(tag);
    102         mPlayers.put(tag, player);
    103         mUrls.put(tag, url);
    104     }
    105 
    106     private void removeMp(String tag) {
    107         tag = getDefault(tag);
    108         MediaPlayer player = mPlayers.get(tag);
    109         if (player != null) {
    110             player.stop();
    111             player.release();
    112         }
    113         mPlayers.remove(tag);
    114         mUrls.remove(tag);
    115     }
    116 
    117     @Rpc(description = "Open a media file", returns = "true if play successful")
    118     public synchronized boolean mediaPlayOpen(@RpcParameter(name = "url",
    119                                                             description = "url of media resource")
    120     String url, @RpcParameter(name = "tag", description = "string identifying resource")
    121     @RpcDefault(value = "default")
    122     String tag, @RpcParameter(name = "play", description = "start playing immediately")
    123     @RpcDefault(value = "true")
    124     Boolean play) {
    125         removeMp(tag);
    126         MediaPlayer player = getPlayer(tag);
    127         player = MediaPlayer.create(mService, Uri.parse(url));
    128         if (player != null) {
    129             putMp(tag, player, url);
    130             player.setOnCompletionListener(this);
    131             if (play) {
    132                 player.start();
    133             }
    134         }
    135         return player != null;
    136     }
    137 
    138     @Rpc(description = "pause playing media file", returns = "true if successful")
    139     public synchronized boolean mediaPlayPause(
    140             @RpcParameter(name = "tag", description = "string identifying resource")
    141             @RpcDefault(value = "default")
    142             String tag) {
    143         MediaPlayer player = getPlayer(tag);
    144         if (player == null) {
    145             return false;
    146         }
    147         player.pause();
    148         return true;
    149     }
    150 
    151     @Rpc(description = "Start playing media file.", returns = "true if successful")
    152     public synchronized boolean mediaPlayStart(
    153             @RpcParameter(name = "tag", description = "string identifying resource")
    154             @RpcDefault(value = "default")
    155             String tag) {
    156         MediaPlayer player = getPlayer(tag);
    157         if (player == null) {
    158             return false;
    159         }
    160         player.start();
    161         return mediaIsPlaying(tag);
    162     }
    163 
    164     @Rpc(description = "Stop playing media file.", returns = "true if successful")
    165     public synchronized boolean mediaPlayStop(
    166             @RpcParameter(name = "tag", description = "string identifying resource")
    167             @RpcDefault(value = "default")
    168             String tag) {
    169         MediaPlayer player = getPlayer(tag);
    170         if (player == null) {
    171             return false;
    172         }
    173         player.stop();
    174         return !mediaIsPlaying(tag) && player.getCurrentPosition() == 0;
    175     }
    176 
    177     @Rpc(description = "Stop all players.")
    178     public synchronized void mediaPlayStopAll() {
    179         for (MediaPlayer p : mPlayers.values()) {
    180             p.stop();
    181         }
    182     }
    183 
    184     @Rpc(description = "Seek To Position", returns = "New Position (in ms)")
    185     public synchronized int mediaPlaySeek(@RpcParameter(name = "msec",
    186                                                         description = "Position in millseconds")
    187     Integer msec, @RpcParameter(name = "tag", description = "string identifying resource")
    188     @RpcDefault(value = "default")
    189     String tag) {
    190         MediaPlayer player = getPlayer(tag);
    191         if (player == null) {
    192             return 0;
    193         }
    194         player.seekTo(msec);
    195         return player.getCurrentPosition();
    196     }
    197 
    198     @Rpc(description = "Close media file", returns = "true if successful")
    199     public synchronized boolean mediaPlayClose(
    200             @RpcParameter(name = "tag", description = "string identifying resource")
    201             @RpcDefault(value = "default")
    202             String tag) throws Exception {
    203         if (!mPlayers.containsKey(tag)) {
    204             return false;
    205         }
    206         removeMp(tag);
    207         return true;
    208     }
    209 
    210     @Rpc(description = "Checks if media file is playing.", returns = "true if playing")
    211     public synchronized boolean mediaIsPlaying(
    212             @RpcParameter(name = "tag", description = "string identifying resource")
    213             @RpcDefault(value = "default")
    214             String tag) {
    215         MediaPlayer player = getPlayer(tag);
    216         return (player == null) ? false : player.isPlaying();
    217     }
    218 
    219     @Rpc(description = "Information on current media", returns = "Media Information")
    220     public synchronized Map<String, Object> mediaPlayGetInfo(
    221             @RpcParameter(name = "tag", description = "string identifying resource")
    222             @RpcDefault(value = "default")
    223             String tag) {
    224         Map<String, Object> result = new HashMap<String, Object>();
    225         MediaPlayer player = getPlayer(tag);
    226         result.put("tag", getDefault(tag));
    227         if (player == null) {
    228             result.put("loaded", false);
    229         } else {
    230             result.put("loaded", true);
    231             result.put("duration", player.getDuration());
    232             result.put("position", player.getCurrentPosition());
    233             result.put("isplaying", player.isPlaying());
    234             result.put("url", getUrl(tag));
    235             result.put("looping", player.isLooping());
    236         }
    237         return result;
    238     }
    239 
    240     @Rpc(description = "Lists currently loaded media", returns = "List of Media Tags")
    241     public Set<String> mediaPlayList() {
    242         return mPlayers.keySet();
    243     }
    244 
    245     @Rpc(description = "Set Looping", returns = "True if successful")
    246     public synchronized boolean mediaPlaySetLooping(@RpcParameter(name = "enabled")
    247     @RpcDefault(value = "true")
    248     Boolean enabled, @RpcParameter(name = "tag", description = "string identifying resource")
    249     @RpcDefault(value = "default")
    250     String tag) {
    251         MediaPlayer player = getPlayer(tag);
    252         if (player == null) {
    253             return false;
    254         }
    255         player.setLooping(enabled);
    256         return true;
    257     }
    258 
    259     @Rpc(description = "Checks if media file is playing.", returns = "true if playing")
    260     public synchronized void mediaSetNext(
    261             @RpcParameter(name = "tag", description = "string identifying resource")
    262             @RpcDefault(value = "default")
    263             String tag,
    264             @RpcParameter(name = "next", description = "tag of the next track to play.")
    265             String next) {
    266         MediaPlayer player = getPlayer(tag);
    267         MediaPlayer nPlayer = getPlayer(next);
    268         if (player == null) {
    269             throw new NullPointerException("Non-existent player tag " + tag);
    270         }
    271         if (nPlayer == null) {
    272             throw new NullPointerException("Non-existent player tag " + next);
    273         }
    274         player.setNextMediaPlayer(nPlayer);
    275     }
    276 
    277     @Override
    278     public synchronized void shutdown() {
    279         for (String key : mPlayers.keySet()) {
    280             MediaPlayer player = mPlayers.get(key);
    281             if (player != null) {
    282                 player.stop();
    283                 player.release();
    284                 player = null;
    285             }
    286         }
    287         mPlayers.clear();
    288         mUrls.clear();
    289     }
    290 
    291     @Override
    292     public void onCompletion(MediaPlayer player) {
    293         String tag = getTag(player);
    294         if (tag != null) {
    295             Map<String, Object> data = new HashMap<String, Object>();
    296             data.put("action", "complete");
    297             data.put("tag", tag);
    298             mEventFacade.postEvent("media", data);
    299         }
    300     }
    301 
    302     private String getTag(MediaPlayer player) {
    303         for (Entry<String, MediaPlayer> m : mPlayers.entrySet()) {
    304             if (m.getValue() == player) {
    305                 return m.getKey();
    306             }
    307         }
    308         return null;
    309     }
    310 }
    311