com.examples.gg.twitchplayers.VideoBuffer.java Source code

Java tutorial

Introduction

Here is the source code for com.examples.gg.twitchplayers.VideoBuffer.java

Source

/*
 * Copyright (C) 2013 yixia.com
 *
 * 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 com.examples.gg.twitchplayers;

import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;
import io.vov.vitamio.MediaPlayer.OnInfoListener;
import io.vov.vitamio.widget.VideoView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.json.JSONObject;
import org.json.JSONTokener;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.examples.gg.R;

public class VideoBuffer extends Activity implements OnInfoListener, OnBufferingUpdateListener {

    /**
     * TODO: Set the path variable to a streaming video URL or a local media
     * file path.
     */
    private String path = "";

    private Uri uri;
    private VideoView mVideoView;
    private ImageView qualityView;
    private TextView mMsgView;
    private boolean isStart;
    private ProgressBar pb;
    private TextView downloadRateView, loadRateView;
    private Handler h;
    private Handler h2;
    private int uiOptions;
    private int mLastSystemUiVis;
    private String responseString;
    private ArrayList<String> videoSources;
    private OnInfoListener mInfoListener;
    private OnBufferingUpdateListener mBufferListener;
    // private PopupMenu popup;
    private String channelName;
    private Context mContext;
    List<String> listItems = new ArrayList<String>();
    private SharedPreferences prefs;
    private String globalPath;
    private Runnable mNavHider = new Runnable() {
        @Override
        public void run() {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(uiOptions);
            qualityView.setVisibility(View.GONE);
        }

    };

    private Runnable mSettingHider = new Runnable() {
        @Override
        public void run() {
            qualityView.setVisibility(View.GONE);
        }

    };

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        if (!LibsChecker.checkVitamioLibs(this))
            return;
        setContentView(R.layout.videobuffer);
        mVideoView = (VideoView) findViewById(R.id.buffer);
        qualityView = (ImageView) findViewById(R.id.qualitySwitch);
        mMsgView = (TextView) findViewById(R.id.load_msg);
        pb = (ProgressBar) findViewById(R.id.probar);

        downloadRateView = (TextView) findViewById(R.id.download_rate);
        loadRateView = (TextView) findViewById(R.id.load_rate);

        mInfoListener = this;
        mBufferListener = this;

        // Initialize variables
        globalPath = null;
        responseString = null;
        videoSources = new ArrayList<String>();

        mContext = this;

        // Getting the prefs
        // prefs = this.getSharedPreferences("com.examples.gg",
        // Context.MODE_PRIVATE);
        prefs = PreferenceManager.getDefaultSharedPreferences(this);

        Intent intent = getIntent();
        channelName = intent.getStringExtra("video");
        // try {
        // popup = new PopupMenu(VideoBuffer.this, qualityView);
        // } catch (Exception e) {
        // }

        // Getting twitch sources
        try {
            channelName = URLEncoder.encode(channelName.toLowerCase(Locale.ENGLISH), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // new MyAsyncTask().execute("http://usher.twitch.tv/select/"
        // + channelName + ".json?nauthsig=&nauth=&allow_source=true");
        String token_api = String.format("http://api.twitch.tv/api/channels/%s/access_token",
                new Object[] { channelName });
        new MyAsyncTask().execute(token_api);

        mMsgView.setText("Loading channel data...");
    }

    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {
        switch (what) {
        case MediaPlayer.MEDIA_INFO_BUFFERING_START:
            if (mVideoView.isPlaying()) {
                mVideoView.pause();
                isStart = true;
                mMsgView.setText("Loading video...");
                pb.setVisibility(View.VISIBLE);
                // downloadRateView.setVisibility(View.VISIBLE);
                loadRateView.setVisibility(View.VISIBLE);

            }
            break;
        case MediaPlayer.MEDIA_INFO_BUFFERING_END:
            if (isStart) {
                mVideoView.start();
                pb.setVisibility(View.GONE);
                downloadRateView.setVisibility(View.GONE);
                loadRateView.setVisibility(View.GONE);
                qualityView.setVisibility(View.GONE);
                mMsgView.setVisibility(View.GONE);
            }
            break;
        // case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:
        // downloadRateView.setText("" + extra + "kb/s" + "  ");
        // break;
        }
        return true;
    }

    @Override
    public void onBufferingUpdate(MediaPlayer mp, int percent) {
        loadRateView.setText(percent + "%");
    }

    @Override
    public void onPause() {
        super.onPause();
        mVideoView.pause();

    }

    @Override
    public void onResume() {
        super.onResume();
        mVideoView.start();
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
            try {
                if (h != null) {
                    qualityView.setVisibility(View.VISIBLE);

                    h.removeCallbacks(mNavHider);
                    h.postDelayed(mNavHider, 3000);
                }
            } catch (Exception e) {
            }
        }

    }

    @SuppressLint("NewApi")
    private void hideBars() {
        try {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                // only for HONEYCOMB and newer versions

                View decorView = getWindow().getDecorView();
                // Hide both the navigation bar and the status bar.
                // SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1
                // and
                // higher, but as
                // a general rule, you should design your app to hide the status
                // bar
                // whenever you
                // hide the navigation bar.

                uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN;

                decorView.setSystemUiVisibility(uiOptions);
                qualityView.setVisibility(View.GONE);

                decorView.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {

                    @Override
                    public void onSystemUiVisibilityChange(int visibility) {
                        int diff = mLastSystemUiVis ^ visibility;
                        mLastSystemUiVis = visibility;
                        if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
                                && (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
                            if (h != null) {
                                // Log.i("debug", "in system");
                                qualityView.setVisibility(View.VISIBLE);

                                h.removeCallbacks(mNavHider);
                                h.postDelayed(mNavHider, 3000);
                            }
                        }

                    }
                });

            } else {
                // For android version 4.0 or lower

                getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                        WindowManager.LayoutParams.FLAG_FULLSCREEN);

            }
        } catch (Exception e) {
        }

    }

    public class MyAsyncTask extends AsyncTask<String, String, String> {

        protected MyAsyncTask() {

        }

        @Override
        protected String doInBackground(String... uri) {
            // Log.i("debug", "do in back");
            HttpURLConnection conn = null;
            try {

                URL url = new URL(uri[0]);
                conn = (HttpURLConnection) url.openConnection();

                conn.setRequestMethod("GET");
                conn.setReadTimeout(10000);

                conn.setRequestProperty("User-Agent",
                        "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");

                InputStream is = conn.getInputStream();

                int http_status = conn.getResponseCode();

                // better check it first
                if (http_status / 100 != 2) {
                    cancel(true);

                }

                responseString = getStringFromInputStream(is);
                is.close();

            } catch (Exception e) {
                // Log.d("debug", conn.getErrorStream().toString());
                // pb.setVisibility(View.GONE);
                // mMsgView.setVisibility(View.VISIBLE);
                // mMsgView.setText("Sorry, there is a problem connecting to the server.");
                // e.printStackTrace();
                cancel(true);

            } finally {

                // Log.d("AsyncDebug", "shutdown");
                if (conn != null)
                    conn.disconnect();

            }

            if (responseString != null) {
                String streams_api = null;
                try {
                    // Get token and sig in JSON
                    JSONTokener jsonParser = new JSONTokener(responseString.toString());
                    JSONObject wholeJson = (JSONObject) jsonParser.nextValue();
                    String token = wholeJson.getString("token");
                    String sig = wholeJson.getString("sig");

                    Object aobj[] = { "usher.twitch.tv", channelName, URLEncoder.encode(token, "UTF-8"), sig };

                    streams_api = String.valueOf(
                            new URL(String.format("http://%s/api/channel/hls/%s.m3u8?token=%s&sig=%s", aobj)));

                } catch (Exception e) {

                }

                if (streams_api != null) {
                    try {

                        URL url = new URL(streams_api);
                        conn = (HttpURLConnection) url.openConnection();

                        conn.setRequestMethod("GET");
                        conn.setReadTimeout(10000);

                        conn.setRequestProperty("User-Agent",
                                "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");

                        InputStream is = conn.getInputStream();

                        int http_status = conn.getResponseCode();

                        // better check it first
                        if (http_status / 100 != 2) {
                            cancel(true);

                        }

                        responseString = getStringFromInputStream2(is);
                        is.close();

                    } catch (Exception e) {
                        cancel(true);

                    } finally {

                        // Log.d("AsyncDebug", "shutdown");
                        if (conn != null)
                            conn.disconnect();

                    }
                }

            }
            return responseString;
        }

        @Override
        protected void onCancelled() {
            errorView();
        }

        @Override
        protected void onPostExecute(String result) {
            // Log.i("debug result", result);

            // Checking the number of stream sources
            if (videoSources.size() > 0) {
                // Check user preferred quality
                String pQuality = prefs.getString("preferredQuality", "none");
                path = getSpecificSource(videoSources, pQuality);
                if (path == null) {
                    // Set default quality to the medium one
                    path = getSpecificSource(videoSources, "medium");
                    if (path == null) {
                        // No medium stream, set to the first one
                        path = videoSources.get(0);
                    } else {
                        // Set default quality to medium
                        prefs.edit().putString("preferredQuality", "medium").commit();

                    }
                }
                globalPath = path;
                // Adding stream sources to quality switcher.
                addingMenuStreamSources();
            }

            // Setting up player
            if (path == "") {
                // Tell the user to provide a media file URL/path.
                Toast.makeText(VideoBuffer.this, "No stream sources", Toast.LENGTH_LONG).show();
                errorView();
                return;
            } else {
                /*
                 * Alternatively,for streaming media you can use
                 * mVideoView.setVideoURI(Uri.parse(URLstring));
                 */
                uri = Uri.parse(path);
                mVideoView.setBufferSize(0);
                mVideoView.setVideoURI(uri);
                // mVideoView.setMediaController(new MediaController(this));
                mVideoView.requestFocus();
                mVideoView.setOnInfoListener(mInfoListener);
                mVideoView.setOnBufferingUpdateListener(mBufferListener);
                h = new Handler();
                h2 = new Handler();
                mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                    @Override
                    public void onPrepared(MediaPlayer mediaPlayer) {
                        // optional need Vitamio 4.0
                        mediaPlayer.setPlaybackSpeed(1.0f);
                    }
                });

                // Setting up touch listener for screen touch
                mVideoView.setOnTouchListener(new OnTouchListener() {
                    @Override
                    public boolean onTouch(View arg0, MotionEvent arg1) {
                        qualityView.setVisibility(View.VISIBLE);
                        if (h2 != null) {

                            h2.removeCallbacks(mSettingHider);
                            h2.postDelayed(mSettingHider, 3000);
                        }
                        return true;
                    }
                });

                // Setting up click listener for source switcher
                qualityView.setOnClickListener(new OnClickListener() {

                    @SuppressLint("NewApi")
                    @Override
                    public void onClick(View v) {
                        try {
                            // // Inflating the Popup using xml file
                            // popup.getMenuInflater().inflate(R.menu.popup_menu,
                            // popup.getMenu());
                            //
                            // // registering popup with OnMenuItemClickListener
                            // popup.setOnMenuItemClickListener(new
                            // PopupMenu.OnMenuItemClickListener() {
                            // public boolean onMenuItemClick(MenuItem item) {
                            // Toast.makeText(VideoBuffer.this,
                            // "Quality: " + item.getTitle(),
                            // Toast.LENGTH_SHORT).show();
                            //
                            // // Reloading the stream according to the
                            // // selected source
                            // String mTitle = (String) item.getTitle();
                            //
                            // path = getSpecificSource(videoSources,
                            // mTitle);
                            // if (path != null) {
                            // uri = Uri.parse(path);
                            // mVideoView.setVideoURI(uri);
                            //
                            // // Set user preferred quality
                            // prefs.edit()
                            // .putString("preferredQuality",
                            // mTitle).commit();
                            // }
                            //
                            // return true;
                            // }
                            // });
                            //
                            // popup.show();// showing popup menu

                            openDialog();

                        } catch (Exception e) {
                        }

                    }
                });

                hideBars();

            }
        }

        // Open video quality chooser
        private void openDialog() {
            if (listItems.size() > 0) {
                // Open a alert dialog
                final CharSequence[] sources_radio = listItems.toArray(new CharSequence[listItems.size()]);

                String mq = prefs.getString("preferredQuality", "none");
                final int mIndex = getQualityIndex(mq, sources_radio);
                // Log.i("debug", Integer.toString(mIndex));

                new AlertDialog.Builder(mContext).setSingleChoiceItems(sources_radio, mIndex, null)
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                dialog.dismiss();

                                int selectedPosition = ((AlertDialog) dialog).getListView()
                                        .getCheckedItemPosition();
                                if (selectedPosition != -1) {
                                    Toast.makeText(VideoBuffer.this, "Quality: " + sources_radio[selectedPosition],
                                            Toast.LENGTH_SHORT).show();
                                    if (selectedPosition != mIndex) {
                                        // Reloading the stream
                                        // according to the
                                        // selected source
                                        String mTitle = (String) sources_radio[selectedPosition];

                                        path = getSpecificSource(videoSources, mTitle);
                                        if (path != null) {
                                            uri = Uri.parse(path);
                                            mVideoView.setVideoURI(uri);

                                            // Set user
                                            // preferred quality
                                            prefs.edit().putString("preferredQuality", mTitle).commit();
                                        }
                                    }
                                }
                            }
                        }).show();

            } else {
                Toast.makeText(VideoBuffer.this, "No other sources", Toast.LENGTH_SHORT).show();
            }

        }

        private int getQualityIndex(String quality, CharSequence[] mRadios) {
            for (int i = 0; i < mRadios.length; i++) {
                if (mRadios[i].equals(quality)) {
                    return i;
                }
            }
            return -1;
        }

        private void addingMenuStreamSources() {
            try {
                // Menu mMenu = popup.getMenu();
                for (int i = 0; i < videoSources.size(); i++) {
                    String mUri = videoSources.get(i);
                    if (mUri.contains("source")) {
                        // Adding menu items programmly
                        // mMenu.add(0, 0, 0, "source");
                        listItems.add("source");
                    } else if (mUri.contains("high")) {
                        // mMenu.add(0, 1, 1, "high");
                        listItems.add("high");
                    } else if (mUri.contains("medium")) {
                        // mMenu.add(0, 2, 2, "medium");
                        listItems.add("medium");
                    } else if (mUri.contains("low")) {
                        // mMenu.add(0, 3, 3, "low");
                        listItems.add("low");
                    } else if (mUri.contains("mobile")) {
                        // mMenu.add(0, 4, 4, "mobile");
                        listItems.add("mobile");
                    }
                }
            } catch (Exception e) {
            }
        }

        private String getStringFromInputStream(InputStream is) {
            // Log.i("debug", "in get");
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder();

            String line;
            try {

                br = new BufferedReader(new InputStreamReader(is));
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return sb.toString();

        }

        private String getStringFromInputStream2(InputStream is) {
            // Log.i("debug", "in get");
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder();

            String line;
            try {

                br = new BufferedReader(new InputStreamReader(is));
                while ((line = br.readLine()) != null) {
                    // pick up the video sources
                    if (line.contains("http")) {
                        videoSources.add(line);
                        // Log.i("debug", "source: " + line);
                    }
                    sb.append(line);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return sb.toString();

        }
    }

    // Return the stream source path according to given quality
    private String getSpecificSource(ArrayList<String> al, String key) {
        for (int i = 0; i < al.size(); i++) {
            if (al.get(i).contains(key)) {
                return al.get(i);
            }
        }
        return null;
    }

    private void errorView() {
        pb.setVisibility(View.GONE);
        mMsgView.setVisibility(View.VISIBLE);
        mMsgView.setText("Sorry, there is a problem connecting to the server.");
    }

}