


{"id":11083,"date":"2017-09-08T22:24:35","date_gmt":"2017-09-08T22:24:35","guid":{"rendered":"https:\/\/expressplay.local\/?page_id=11083"},"modified":"2021-07-09T08:42:45","modified_gmt":"2021-07-09T15:42:45","slug":"tutorial-android-app-bb","status":"publish","type":"page","link":"https:\/\/www.expressplay.com\/ko\/developer\/tutorial-android-app-bb\/","title":{"rendered":"Tutorial: Android App (Using BB)"},"content":{"rendered":"<style type=\"text\/css\">\n<p>    hr, img {\n        margin: 20px auto;\n    }<\/p>\n<p>    code {\n        word-break: break-all;\n        white-space: pre-wrap;\n        font-size: 14px;\n        color: #555;\n        margin: 20px 0;\n    }\n<\/style>\n<div class=\"flex flex-wrap -mx-6 \">\n<div class=\"w-full lg:w-7\/12 px-6\">\n<h2>Android App Tutorial (Using BB Token)<\/h2>\n<h4 style=\"font-size:18px; font-weight: 400;\">This tutorial guides you through the process of streaming encrypted content on an Android device and creating a barebones Android ExpressPlay enabled media app to play the content using a Marlin BB token.<\/h4>\n<div class=\"bg-grey-100 p-8 mb-12 mt-12\">\n<h4 style=\"margin-bottom: 10px; font-weight: 400; font-size: 20px;\">Recommended equipment:<\/h4>\n<ul class=\"tutorial_container\">\n<li>A device that runs on Android 4.0.X or higher<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"w-full lg:w-5\/12\">\n      <img decoding=\"async\" src=\"\/wp-content\/uploads\/2017\/08\/developer-sdk-large.png\" alt=\"\"\/>\n<\/div>\n<\/div>\n<hr \/>\n<div class=\"mb-12 mt-12\">\n<p>STEP 1 &#8211; Install the Android SDK<\/p>\n<\/div>\n<p><span style=\"font-size:15px\">Download the Android SDK; the latest release can be found here: <\/span><a target=\"_blank\" href=\"http:\/\/developer.android.com\/sdk\/index.html\" rel=\"noopener noreferrer nofollow\">http:\/\/developer.android.com\/sdk\/index.html<\/a><\/p>\n<hr \/>\n<div class=\"mb-12 mt-12\">\n<p>STEP 2 &#8211; Download the latest ExpressPlay SDK<\/p>\n<\/div>\n<p>The latest copy of the ExpressPlay SDK can be download from the <a class=\"underline\" target=\"_blank\" href=\"https:\/\/admin.expressplay.com\/sdk\/\" rel=\"noopener noreferrer nofollow\">ExpressPlay Admin Portal<\/a>.<\/p>\n<p>Be sure to remember where you saved your file.<\/p>\n<hr \/>\n<div class=\"mb-12 mt-12\">\n<p>STEP 3 &#8211; Import the ExpressPlayExampleBB Project from the ExpressPlay SDK <\/p>\n<\/p><\/div>\n<p>Please refer to <a target=\"_blank\" href=\"\/developer\/tutorial-android-app\" rel=\"noopener noreferrer\">http:\/\/www.expressplay.com\/developer\/tutorial-android-app<\/a> for importing the project into either Android Studio or Eclipse.<\/p>\n<hr \/>\n<p><span style=\"font-size:15px\">The imports are essentially the same as for the basic tutorial:<\/span><\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n          <code><br \/>\nimport java.io.ByteArrayOutputStream;<br \/>\nimport java.io.IOException;<br \/>\nimport java.io.InputStream;<\/p>\n<p>import android.app.Activity;<br \/>\nimport android.app.Fragment;<br \/>\nimport android.net.Uri;<br \/>\nimport android.os.Bundle;<br \/>\nimport android.util.Log;<br \/>\nimport android.view.LayoutInflater;<br \/>\nimport android.view.Menu;<br \/>\nimport android.view.MenuItem;<br \/>\nimport android.view.View;<br \/>\nimport android.view.ViewGroup;<br \/>\nimport android.widget.MediaController;<br \/>\nimport android.widget.VideoView;<\/p>\n<p>import com.intertrust.wasabi.ErrorCodeException;<br \/>\nimport com.intertrust.wasabi.Runtime;<br \/>\nimport com.intertrust.wasabi.media.PlaylistProxy;<br \/>\nimport com.intertrust.wasabi.media.PlaylistProxy.MediaSourceParams;<br \/>\nimport com.intertrust.wasabi.media.PlaylistProxy.MediaSourceType;<\/code>\n        <\/div>\n<hr \/>\n<hr \/>\n<p>The Activity class defines some constants\/enums and the fragment that implements personalization, marlin broadband license token acquisition, and streams the video through the <tt>VideoView<\/tt>.<\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n\/*<br \/>\n * this enum simply maps the media types to the mimetypes required for the playlist proxy<br \/>\n *\/<br \/>\nenum ContentTypes {<br \/>\n  DASH(\"application\/dash+xml\"), HLS(\"application\/vnd.apple.mpegurl\"), PDCF(<br \/>\n          \"video\/mp4\"), M4F(\"video\/mp4\"), DCF(\"application\/vnd.oma.drm.dcf\"), BBTS(<br \/>\n          \"video\/mp2t\");<br \/>\n  String mediaSourceParamsContentType = null;<\/p>\n<p>  private ContentTypes(String mediaSourceParamsContentType) {<br \/>\n    this.mediaSourceParamsContentType = mediaSourceParamsContentType;<br \/>\n  }<\/p>\n<p>  public String getMediaSourceParamsContentType() {<br \/>\n    return mediaSourceParamsContentType;<br \/>\n  }<br \/>\n}<\/p>\n<p>public class MarlinBroadbandExample extends Activity {<\/p>\n<p>  @Override<br \/>\n  protected void onCreate(Bundle savedInstanceState) {<br \/>\n    super.onCreate(savedInstanceState);<br \/>\n    setContentView(R.layout.activity_marlin_broadband_example);<\/p>\n<p>    if (savedInstanceState == null) {<br \/>\n      getFragmentManager().beginTransaction()<br \/>\n              .add(R.id.container, new MBB_Playback_Fragment()).commit();<br \/>\n    }<br \/>\n  }<\/p>\n<p>  @Override<br \/>\n  public boolean onCreateOptionsMenu(Menu menu) {<\/p>\n<p>    \/\/ Inflate the menu; this adds items to the action bar if it is present.<br \/>\n    getMenuInflater().inflate(R.menu.marlin_broadband_example, menu);<br \/>\n    return true;<br \/>\n  }<\/p>\n<p>  @Override<br \/>\n  public boolean onOptionsItemSelected(MenuItem item) {<br \/>\n    \/\/ Handle action bar item clicks here. The action bar will<br \/>\n    \/\/ automatically handle clicks on the Home\/Up button, so long<br \/>\n    \/\/ as you specify a parent activity in AndroidManifest.xml.<br \/>\n    int id = item.getItemId();<br \/>\n    if (id == R.id.action_settings) {<br \/>\n      return true;<br \/>\n    }<br \/>\n    return super.onOptionsItemSelected(item);<br \/>\n  }<\/p>\n<p>  \/**<br \/>\n   * A placeholder fragment containing a simple view.<br \/>\n   *\/<br \/>\n  public static class MBB_Playback_Fragment extends Fragment {<\/p>\n<p>    private PlaylistProxy playerProxy;<br \/>\n    static final String TAG = \"SampleBBPlayer\";<\/p>\n<p>    public MBB_Playback_Fragment() {<br \/>\n    }<\/p>\n<p>    @Override<br \/>\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,<br \/>\n            Bundle savedInstanceState) {<br \/>\n      View rootView = inflater.inflate(<br \/>\n              R.layout.fragment_marlin_broadband_example, container,<br \/>\n              false);<\/p>\n<p>      \/*<br \/>\n       * Create a VideView for playback<br \/>\n       *\/<br \/>\n      VideoView videoView = (VideoView) rootView<br \/>\n              .findViewById(R.id.videoView);<br \/>\n      MediaController mediaController = new MediaController(<br \/>\n              getActivity(), false);<br \/>\n      mediaController.setAnchorView(videoView);<br \/>\n      videoView.setMediaController(mediaController);<\/p>\n<p>      try {<br \/>\n        \/*<br \/>\n         * Initialize the Wasabi Runtime (necessary only once for each<br \/>\n         * instantiation of the application)<br \/>\n         *<br \/>\n         * ** Note: Set Runtime Properties as needed for your<br \/>\n         * environment<br \/>\n         *\/<br \/>\n        Runtime.initialize(getActivity().getDir(\"wasabi\", MODE_PRIVATE)<br \/>\n                .getAbsolutePath());<br \/>\n        \/*<br \/>\n         * Personalize the application (acquire DRM keys). This is only<br \/>\n         * necessary once each time the application is freshly installed<br \/>\n         *<br \/>\n         * ** Note: personalize() is a blocking call and may take long<br \/>\n         * enough to complete to trigger ANR (Application Not<br \/>\n         * Responding) errors. In a production application this should<br \/>\n         * be called in a background thread.<br \/>\n         *\/<br \/>\n        if (!Runtime.isPersonalized())<br \/>\n          Runtime.personalize();<\/p>\n<p>      } catch (NullPointerException e) {<br \/>\n        return rootView;<br \/>\n      } catch (ErrorCodeException e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"runtime initialization or personalization error: \"<br \/>\n                + e.getLocalizedMessage());<br \/>\n        return rootView;<br \/>\n      }<\/p>\n<p>      \/*<br \/>\n       * Acquire a Marlin Broadband License. The license is acquired using<br \/>\n       * a License Acquisition token. Such tokens for sample content can<br \/>\n       * be obtained from http:\/\/content.intertrust.com\/express\/ and in<br \/>\n       * this example are stored in the Android project \/assets directory<br \/>\n       * using the filename \"license-token.xml\".<br \/>\n       *<br \/>\n       * For instance, you can download such a token from<br \/>\n       * http:\/\/content-access.intertrust-dev.com\/EXPR005\/bb, and save it<br \/>\n       * to the assets directory as license-token.xml\"<br \/>\n       *<br \/>\n       * *** Note: processServiceToken() is a blocking call and may take<br \/>\n       * long enough to complete to trigger ANR (Application Not<br \/>\n       * Responding) errors. In a production application this should be<br \/>\n       * called in a background thread.<br \/>\n       *\/<\/p>\n<p>      String licenseAcquisitionToken = getActionTokenFromAssets(LICENSE_TOKEN_FILENAME);<br \/>\n      if (licenseAcquisitionToken == null) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not find action token in the assets directory - exiting\");<br \/>\n        return rootView;<br \/>\n      }<br \/>\n      long start = System.currentTimeMillis();<br \/>\n      try {<br \/>\n        Runtime.processServiceToken(licenseAcquisitionToken);<br \/>\n        Log.i(TAG,<br \/>\n                \"License successfully acquired in (ms): \"<br \/>\n                        + (System.currentTimeMillis() - start));<br \/>\n      } catch (ErrorCodeException e1) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not acquire the license from the license acquisition token - exiting\");<br \/>\n        return rootView;<br \/>\n      }<\/p>\n<p>      \/*<br \/>\n       * create a playlist proxy and start it<br \/>\n       *\/<br \/>\n      try {<br \/>\n        playerProxy = new PlaylistProxy();<br \/>\n        playerProxy.start();<br \/>\n      } catch (ErrorCodeException e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"playlist proxy error: \" + e.getLocalizedMessage());<br \/>\n        return rootView;<br \/>\n      }<\/p>\n<p>      \/*<br \/>\n       * Acquire a media stream URL encrypted with the key delivered in<br \/>\n       * the above license. Media Stream URLs can be obtained at<br \/>\n       * http:\/\/content.intertrust.com\/express\/.<br \/>\n       *<br \/>\n       * For instance, a DASH content stream protected with the license<br \/>\n       * token example above is<br \/>\n       * \"http:\/\/content.intertrust.com\/express\/dash\/mpd.xml\"<br \/>\n       *<br \/>\n       * Note that the MediaSourceType must be adapted to the stream type<br \/>\n       * (DASH or HLS). Similarly,<br \/>\n       * the MediaSourceParams need to be set according to the media type<br \/>\n       * if MediaSourceType is SINGLE_FILE<br \/>\n       *\/<\/p>\n<p>      String dash_url = DASH_MPD_URL;<br \/>\n      ContentTypes contentType = ContentTypes.FIXTHIS;<\/p>\n<p>      MediaSourceParams params = new MediaSourceParams();<br \/>\n      params.sourceContentType = contentType<br \/>\n              .getMediaSourceParamsContentType();<\/p>\n<p>      \/*<br \/>\n       * if the content has separate audio tracks (eg languages) you may<br \/>\n       * select one using MediaSourceParams, eg params.language=\"es\";<br \/>\n       *\/<\/p>\n<p>      \/*<br \/>\n       * Create a PlaylistProxy URL and pass it to the VideView and start<br \/>\n       * playback<br \/>\n       *\/<br \/>\n      String proxy_url = null;<br \/>\n      try {<br \/>\n        proxy_url = playerProxy.makeUrl(dash_url, MediaSourceType.DASH,<br \/>\n                params);<br \/>\n        videoView.setVideoURI(Uri.parse(proxy_url));<br \/>\n        videoView.start();<\/p>\n<p>      } catch (Exception e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"playback error: \" + e.getLocalizedMessage());<br \/>\n        e.printStackTrace();<br \/>\n        return rootView;<br \/>\n      }<\/p>\n<p>      return rootView;<\/p>\n<p>    }<\/p>\n<p>    \/**************************************<br \/>\n     * Helper methods to avoid cluttering *<br \/>\n     **************************************\/<\/p>\n<p>    \/*<br \/>\n     * Read an action token file from the assets directory<br \/>\n     *\/<br \/>\n    protected String getActionTokenFromAssets(String tokenFileName) {<br \/>\n      String token = null;<br \/>\n      byte[] readBuffer = new byte[1024];<br \/>\n      ByteArrayOutputStream baos = new ByteArrayOutputStream();<br \/>\n      InputStream is = null;<br \/>\n      int bytesRead = 0;<\/p>\n<p>      try {<br \/>\n        is = getActivity().getAssets()<br \/>\n                .open(tokenFileName, MODE_PRIVATE);<br \/>\n        while ((bytesRead = is.read(readBuffer)) != -1) {<br \/>\n          baos.write(readBuffer, 0, bytesRead);<br \/>\n        }<br \/>\n        baos.close();<br \/>\n        is.close();<br \/>\n      } catch (IOException e) {<br \/>\n        e.printStackTrace();<br \/>\n        return null;<br \/>\n      }<br \/>\n      token = new String(baos.toByteArray());<br \/>\n      return token;<br \/>\n    }<\/p>\n<p>  }<\/p>\n<p>}<br \/>\n<\/code>\n        <\/div>\n<p> Now let&#8217;s take a look at what the code does. <\/p>\n<p> The MBB_Playback_Fragment Fragment&#8217;s onCreateView method begins by setting the content to the xml file created with the <tt>VideoView<\/tt> inside it.<\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n          <code><br \/>\n      View rootView = inflater.inflate(<br \/>\n              R.layout.fragment_marlin_broadband_example, container,<br \/>\n              false);<br \/>\n<\/code>\n        <\/div>\n<p> Next, the <tt>VideoView<\/tt> is set up.<\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n          <code><br \/>\n      \/*<br \/>\n       * Create a VideView for playback<br \/>\n       *\/<br \/>\n      VideoView videoView = (VideoView) rootView<br \/>\n              .findViewById(R.id.videoView);<br \/>\n      MediaController mediaController = new MediaController(<br \/>\n              getActivity(), false);<br \/>\n      mediaController.setAnchorView(videoView);<br \/>\n      videoView.setMediaController(mediaController);<\/code>\n        <\/div>\n<p>Then, personalization.<\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n      try {<br \/>\n        \/*<br \/>\n         * Initialize the Wasabi Runtime (necessary only once for each<br \/>\n         * instantiation of the application)<br \/>\n         *<br \/>\n         * ** Note: Set Runtime Properties as needed for your<br \/>\n         * environment<br \/>\n         *\/<br \/>\n        Runtime.initialize(getActivity().getDir(\"wasabi\", MODE_PRIVATE)<br \/>\n                .getAbsolutePath());<br \/>\n        \/*<br \/>\n         * Personalize the application (acquire DRM keys). This is only<br \/>\n         * necessary once each time the application is freshly installed<br \/>\n         *<br \/>\n         * ** Note: personalize() is a blocking call and may take long<br \/>\n         * enough to complete to trigger ANR (Application Not<br \/>\n         * Responding) errors. In a production application this should<br \/>\n         * be called in a background thread.<br \/>\n         *\/<br \/>\n        if (!Runtime.isPersonalized())<br \/>\n          Runtime.personalize();<\/p>\n<p>      } catch (NullPointerException e) {<br \/>\n        return rootView;<br \/>\n      } catch (ErrorCodeException e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"runtime initialization or personalization error: \"<br \/>\n                + e.getLocalizedMessage());<br \/>\n        return rootView;<br \/>\n      }<br \/>\n<\/code>\n<\/div>\n<p><span style=\"font-size:15px\">Then, the Marlin Broadband License is acquired. Following the comments in the code, get a License Acquisition Token by clicking <\/span><a target=\"_blank\" href=\"http:\/\/content.intertrust.com\/express\/\" rel=\"noopener noreferrer nofollow\">here<\/a><br \/><span style=\"font-size:15px\">and saving the file in &#8220;license-token.xml&#8221;. Save this file in the assets folder of your Android Application Project. <\/span><br \/><span style=\"font-size:15px\">Note: After some time, this license will expire. But a new one can be acquired following the same process.<\/span><\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n      \/*<br \/>\n       * Acquire a Marlin Broadband License. The license is acquired using<br \/>\n       * a License Acquisition token. Such tokens for sample content can<br \/>\n       * be obtained from http:\/\/content.intertrust.com\/express\/ and in<br \/>\n       * this example are stored in the Android project \/assets directory<br \/>\n       * using the filename \"license-token.xml\".<br \/>\n       *<br \/>\n       * For instance, you can download such a token from<br \/>\n       * http:\/\/content-access.intertrust-dev.com\/EXPR005\/bb, and save it<br \/>\n       * to the assets directory as license-token.xml\"<br \/>\n       *<br \/>\n       * *** Note: processServiceToken() is a blocking call and may take<br \/>\n       * long enough to complete to trigger ANR (Application Not<br \/>\n       * Responding) errors. In a production application this should be<br \/>\n       * called in a background thread.<br \/>\n       *\/<\/p>\n<p>      String licenseAcquisitionToken = getActionTokenFromAssets(LICENSE_TOKEN_FILENAME);<br \/>\n      if (licenseAcquisitionToken == null) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not find action token in the assets directory - exiting\");<br \/>\n        return rootView;<br \/>\n      }<br \/>\n      long start = System.currentTimeMillis();<br \/>\n      try {<br \/>\n        Runtime.processServiceToken(licenseAcquisitionToken);<br \/>\n        Log.i(TAG,<br \/>\n                \"License successfully acquired in (ms): \"<br \/>\n                        + (System.currentTimeMillis() - start));<br \/>\n      } catch (ErrorCodeException e1) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not acquire the license from the license acquisition token - exiting\");<br \/>\n        return rootView;<br \/>\n      }<br \/>\n<\/code>\n<\/div>\n<p> Then, get the media stream URL. This URL is encrypted with the key found in the above license token. <\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n      \/*<br \/>\n       * Acquire a Marlin Broadband License. The license is acquired using<br \/>\n       * a License Acquisition token. Such tokens for sample content can<br \/>\n       * be obtained from http:\/\/content.intertrust.com\/express\/ and in<br \/>\n       * this example are stored in the Android project \/assets directory<br \/>\n       * using the filename \"license-token.xml\".<br \/>\n       *<br \/>\n       * For instance, you can download such a token from<br \/>\n       * http:\/\/content-access.intertrust-dev.com\/EXPR005\/bb, and save it<br \/>\n       * to the assets directory as license-token.xml\"<br \/>\n       *<br \/>\n       * *** Note: processServiceToken() is a blocking call and may take<br \/>\n       * long enough to complete to trigger ANR (Application Not<br \/>\n       * Responding) errors. In a production application this should be<br \/>\n       * called in a background thread.<br \/>\n       *\/<\/p>\n<p>      String licenseAcquisitionToken = getActionTokenFromAssets(LICENSE_TOKEN_FILENAME);<br \/>\n      if (licenseAcquisitionToken == null) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not find action token in the assets directory - exiting\");<br \/>\n        return rootView;<br \/>\n      }<br \/>\n      long start = System.currentTimeMillis();<br \/>\n      try {<br \/>\n        Runtime.processServiceToken(licenseAcquisitionToken);<br \/>\n        Log.i(TAG,<br \/>\n                \"License successfully acquired in (ms): \"<br \/>\n                        + (System.currentTimeMillis() - start));<br \/>\n      } catch (ErrorCodeException e1) {<br \/>\n        Log.e(TAG,<br \/>\n                \"Could not acquire the license from the license acquisition token - exiting\");<br \/>\n        return rootView;<br \/>\n      }<br \/>\n<\/code>\n<\/div>\n<p> Then, create the PlaylistProxy and start it.  Acquire the protected media stream corresponding to the license, create a PlaylistProxy URL from that media url, and set the <tt>VideoView<\/tt> to play from the PlaylistProxy URL. <\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n      \/*<br \/>\n       * create a playlist proxy and start it<br \/>\n       *\/<br \/>\n      try {<br \/>\n        playerProxy = new PlaylistProxy();<br \/>\n        playerProxy.start();<br \/>\n      } catch (ErrorCodeException e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"playlist proxy error: \" + e.getLocalizedMessage());<br \/>\n        return rootView;<br \/>\n      }<br \/>\n  \/*<br \/>\n       * Acquire a media stream URL encrypted with the key delivered in<br \/>\n       * the above license. Media Stream URLs can be obtained at<br \/>\n       * http:\/\/content.intertrust.com\/express\/.<br \/>\n       *<br \/>\n       * For instance, a DASH content stream protected with the license<br \/>\n       * token example above is<br \/>\n       * \"http:\/\/content.intertrust.com\/express\/dash\/mpd.xml\"<br \/>\n       *<br \/>\n       * Note that the MediaSourceType must be adapted to the stream type<br \/>\n       * (DASH or HLS). Similarly,<br \/>\n       * the MediaSourceParams need to be set according to the media type<br \/>\n       * if MediaSourceType is SINGLE_FILE<br \/>\n       *\/<\/p>\n<p>      String dash_url = DASH_MPD_URL;<br \/>\n      ContentTypes contentType = ContentTypes.FIXTHIS;<\/p>\n<p>      MediaSourceParams params = new MediaSourceParams();<br \/>\n      params.sourceContentType = contentType<br \/>\n              .getMediaSourceParamsContentType();<\/p>\n<p>      \/*<br \/>\n       * if the content has separate audio tracks (eg languages) you may<br \/>\n       * select one using MediaSourceParams, eg params.language=\"es\";<br \/>\n       *\/<\/p>\n<p>      \/*<br \/>\n       * Create a PlaylistProxy URL and pass it to the VideView and start<br \/>\n       * playback<br \/>\n       *\/<br \/>\n      String proxy_url = null;<br \/>\n      try {<br \/>\n        proxy_url = playerProxy.makeUrl(dash_url, MediaSourceType.DASH,<br \/>\n                params);<br \/>\n        videoView.setVideoURI(Uri.parse(proxy_url));<br \/>\n        videoView.start();<\/p>\n<p>      } catch (Exception e) {<br \/>\n        \/\/ Consult WasabiErrors.txt for resolution of the error codes<br \/>\n        Log.e(TAG, \"playback error: \" + e.getLocalizedMessage());<br \/>\n        e.printStackTrace();<br \/>\n        return rootView;<br \/>\n      }<\/p>\n<p><\/code>\n<\/div>\n<p><span style=\"font-size:15px\">The following method acquires the token from the license-token.xml file located in the assets folder.<\/span><\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n<code><br \/>\n    \/**************************************<br \/>\n     * Helper methods to avoid cluttering *<br \/>\n     **************************************\/<\/p>\n<p>    \/*<br \/>\n     * Read an action token file from the assets directory<br \/>\n     *\/<br \/>\n    protected String getActionTokenFromAssets(String tokenFileName) {<br \/>\n      String token = null;<br \/>\n      byte[] readBuffer = new byte[1024];<br \/>\n      ByteArrayOutputStream baos = new ByteArrayOutputStream();<br \/>\n      InputStream is = null;<br \/>\n      int bytesRead = 0;<\/p>\n<p>      try {<br \/>\n        is = getActivity().getAssets()<br \/>\n                .open(tokenFileName, MODE_PRIVATE);<br \/>\n        while ((bytesRead = is.read(readBuffer)) != -1) {<br \/>\n          baos.write(readBuffer, 0, bytesRead);<br \/>\n        }<br \/>\n        baos.close();<br \/>\n        is.close();<br \/>\n      } catch (IOException e) {<br \/>\n        e.printStackTrace();<br \/>\n        return null;<br \/>\n      }<br \/>\n      token = new String(baos.toByteArray());<br \/>\n      return token;<br \/>\n    }<br \/>\n<\/code>\n<\/div>\n<hr \/>\n<hr \/>\n<div class=\"mb-12 mt-12\">\n<p>STEP 4 &#8211; Permission<\/p>\n<\/p><\/div>\n<p><span style=\"font-size:15px\">Go to the package explorer and open &#8216;AndroidManifest.xml&#8217;. Go to the actual xml code.<\/span><\/p>\n<p>      <img decoding=\"async\" src=\"\/wp-content\/uploads\/2017\/08\/tutorial-android-bb1.png\" alt=\"\" style=\"margin: 20px 0; border: 1px solid #e7e7e7;\" \/><\/p>\n<p><span style=\"font-size:15px\">To give the ExpressPlay SDK internet permission as well as permission to write to the external storage of the Android device, add the following code block in between <\/span><em>android:targetSdkVersion=&quot;17&quot;\/&gt;<\/em><span style=\"font-size:15px\"> and <\/span><em>&lt;application<\/em><span style=\"font-size:15px\"> as shown in the picture above.<\/span><\/p>\n<div class=\"bg-grey-100 p-8 mt-12 mb-12\">\n        <code>&lt;uses-permission android:name=\"android.permission.INTERNET\"&gt;&lt;\/uses-permission&gt;<br \/>\n&lt;uses-permission android:name=&quot;android.permission.ACCESS_NETWORK_STATE&quot;&gt;&lt;\/uses-permission&gt;<\/code>\n      <\/div>\n<p>Now the code is ready to run.<\/p>\n<p>Note: If you are using an android virtual machine, as opposed to an actual device, the video may not work, but the audio will.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Android App Tutorial (Using BB Token) This tutorial guides you through the process of streaming encrypted content on an Android device and creating a barebones Android ExpressPlay enabled media app to play the content using a Marlin BB token. Recommended equipment: A device that runs on Android 4.0.X or higher STEP 1 &#8211; Install the [&hellip;]<\/p>\n","protected":false},"author":124,"featured_media":0,"parent":10924,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-expressplay-developer.php","meta":{"_acf_changed":false,"footnotes":""},"tax_page_type":[512],"coauthors":[621],"class_list":["post-11083","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/pages\/11083","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/users\/124"}],"replies":[{"embeddable":true,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/comments?post=11083"}],"version-history":[{"count":0,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/pages\/11083\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/pages\/10924"}],"wp:attachment":[{"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/media?parent=11083"}],"wp:term":[{"taxonomy":"tax_page_type","embeddable":true,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/tax_page_type?post=11083"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.expressplay.com\/ko\/wp-json\/wp\/v2\/coauthors?post=11083"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}