Android Streaming Live Camera Video to Web Page

    Rating: ★★★★☆
    View: 300
    Download: 151
    Từ khóa:

    If you are beginner, this article will introduce you to lot new things like real time streaming protocols (RTSP, RTMP), Wowza Media Engine, libstreaming, WAMP and jwplayer.

    VIDEO DEMO

    Below are the two protocols on which real time streaming really works. Go through the wikipedia links to get enough knowledge on underlaying technology behind real time streaming.

    RTSP Protocol
    Real Time Streaming Protocol is a networking protocol mainly used to stream real time media data like audio or video. It establishes a streaming session between client and server. In this tutorial we use this protocol while sending video stream from android mobile to streaming server.

    Lean more about Real Time Streaming Protocol

    RTMP Protocol
    Real Time Messaging Protocol was developed by Adobe for Flash Player to transmit the realtime media (audio, video) between server and flash player. This protocol we use to receive video stream from server to flash player.

    Lean more about Real Time Messaging Protocol

    Below diagram is a high level architecture diagram of android video streaming. First android streams camera video to wowza media engine. Second wowza decodes the video and starts a streaming channel. Thirst webpage consumes wowza stream and plays the video on the page.

    1. Downloading Libstreaming library

    Building a RTSP library involves deep understanding of real time streaming protocols and good command over multiple java media APIs which is not easy for every beginner. Luckly Fyhertz made our lives easier by providing an excellent RTSP library called libstreaming for android. Using this library, streaming video / audio from android mobile can done with very few lines of code.

    Download the library and keep it aside.

    2. Installing & Configuring Wowza Media Engine

    Wowza Media Engine is very popular streaming engine which can stream high quality video and audio. In our project it acts as server side streaming framework which receives video from android device and starts a streaming service which will be again consumed by webpage to display the video.

    Wowza also comes with an admin panel called Wowza Media Engine Manager to control streaming channels, publishers and other stuff. Unfortunately wowza is not a free software, you will have to buy a commercial license. But don’t worry, it comes with a trail period of 6 months which is more than enough for testing.

    2.1 Registration
    In order to use Wowa media engine you need to register and get a license key first. The key will be sent to your email address after the registration. So make sure that you entered a valid email address instead of abc@abc.com

    Once you complete registration here, you can find the license key in the email.

    2.2 Downloading & Installing
    1. Download Wowza Media Engine from here
    2. Run the installer and enter the license key when it asks for it.

    2.3 Creating publisher username and password
    Once wowza installed, open Wowza Streaming Engine Manager from Start => All Programs. This opens up an admin panel in the browser. While streaming from android mobile, the streaming video needs to be authenticated with Wowza before start decoding it. So we need to create a publisher username and password first. These credentials we will use in our android app later.

    To create a publisher, click on Server ⇒ Publisher ⇒ Add Publisher and enter credentials.

    2.4 Creating streaming channel
    You can create your own streaming url in the admin panel. But Wowza already creates a channel(app) for you named live. I am going to use the same app in this tutorial. If you want, you can create your own.

    Here we completes wowza setup. Now let’s start the android project.

    3. Creating Android project

    1. In Eclipse create a new project by navigating to File ⇒ New ⇒ Android Application Project and fill required details.

    2. Now import the downloaded libstreaming project into your Eclipse workspace. File ⇒ Import ⇒ Android ⇒ Existing Android Code Into Workspace and browse to libstreaming.

    3. Now add the libstreaming project as a library project to our project. Right click on our project ⇒ Properties. It will open up a dialog window. On the left select Android and on the Right under Libraryadd libstreaming project.

    4. As we are accessing the camera, we need to add the permissions in AndroidManifest.xml first. AddINTERNETWRITE_EXTERNAL_STORAGERECORD_AUDIOCAMERA permissions in your mainifest file.

    AndroidManifest.xml
    
    
     
        
     
        
        
     
        
     
        
        
        
        
     
        
            
                
                    
     
                    
                
            
        
     
    

    5. Open the activity layout file main activity and add the following content (My layout file nameactivity_main.xml). Here we are adding libstreaming surfaceview for camera preview.

    activity_main.xml
    
     
        
     
    

    6. Now I am creating a class to keep wowza configuration values like streaming url, publisher username and password. Create a class named AppConfig.java and add the following. Replace the streaming url (keep your PC ip address), username and password with your wowza details.

    AppConfig.java
    public class AppConfig {
        public static final String STREAM_URL = "rtsp://192.168.43.233:1935/live/android_test";
        public static final String PUBLISHER_USERNAME = "ravi";
        public static final String PUBLISHER_PASSWORD = "passtemp";
    }

    7. Open your main activity class and implement the class from RtspClient.CallbackSession.Callback,SurfaceHolder.Callback and initialize required variables and class instances.

    public class MainActivity extends Activity implements RtspClient.Callback,
            Session.Callback, SurfaceHolder.Callback {
    import net.majorkernelpanic.streaming.Session;
    import net.majorkernelpanic.streaming.gl.SurfaceView;
    import net.majorkernelpanic.streaming.rtsp.RtspClient;
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.SurfaceHolder;
    import android.view.Window;
    import android.view.WindowManager;
     
    public class MainActivity extends Activity implements RtspClient.Callback,
            Session.Callback, SurfaceHolder.Callback {
     
        // log tag
        public final static String TAG = MainActivity.class.getSimpleName();
     
        // surfaceview
        private static SurfaceView mSurfaceView;
     
        // Rtsp session
        private Session mSession;
        private static RtspClient mClient;
         
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            // getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
     
            setContentView(R.layout.activity_main);
     
            mSurfaceView = (SurfaceView) findViewById(R.id.surface);
     
            mSurfaceView.getHolder().addCallback(this);
     
            // Initialize RTSP client
            initRtspClient();
        }
     
        @Override
        protected void onResume() {
            super.onResume();
             
            toggleStreaming();
        }
         
        @Override
        protected void onPause(){
            super.onPause();
             
            toggleStreaming();
        }
     
        @Override
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
        }
     
        @Override
        public void surfaceCreated(SurfaceHolder arg0) {
     
        }
     
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
     
        }
     
        @Override
        public void onBitrareUpdate(long bitrate) {
     
        }
     
        @Override
        public void onSessionError(int reason, int streamType, Exception e) {
     
        }
     
        @Override
        public void onPreviewStarted() {
     
        }
     
        @Override
        public void onSessionConfigured() {
     
        }
     
        @Override
        public void onSessionStarted() {
     
        }
     
        @Override
        public void onSessionStopped() {
     
        }
     
        @Override
        public void onRtspUpdate(int message, Exception exception) {
     
        }
    }

    8initRtspClient() will initialize Rtsp client which takes care of streaming video to wowza media server. Add the following code in your main activity. toggleStreaming() takes care of toggling video stream on/off. Finally in OnDestroy() method we have to release rtsp client, session and surfaceview.

    private void initRtspClient() {
            // Configures the SessionBuilder
            mSession = SessionBuilder.getInstance()
                    .setContext(getApplicationContext())
                    .setAudioEncoder(SessionBuilder.AUDIO_NONE)
                    .setAudioQuality(new AudioQuality(8000, 16000))
                    .setVideoEncoder(SessionBuilder.VIDEO_H264)
                    .setSurfaceView(mSurfaceView).setPreviewOrientation(0)
                    .setCallback(this).build();
     
            // Configures the RTSP client
            mClient = new RtspClient();
            mClient.setSession(mSession);
            mClient.setCallback(this);
            mSurfaceView.setAspectRatioMode(SurfaceView.ASPECT_RATIO_PREVIEW);
            String ip, port, path;
     
            // We parse the URI written in the Editext
            Pattern uri = Pattern.compile("rtsp://(.+):(\d+)/(.+)");
            Matcher m = uri.matcher(AppConfig.STREAM_URL);
            m.find();
            ip = m.group(1);
            port = m.group(2);
            path = m.group(3);
     
            mClient.setCredentials(AppConfig.PUBLISHER_USERNAME,
                    AppConfig.PUBLISHER_PASSWORD);
            mClient.setServerAddress(ip, Integer.parseInt(port));
            mClient.setStreamPath("/" + path);
        }
     
        private void toggleStreaming() {
            if (!mClient.isStreaming()) {
                // Start camera preview
                mSession.startPreview();
     
                // Start video stream
                mClient.startStream();
            } else {
                // already streaming, stop streaming
                // stop camera preview
                mSession.stopPreview();
     
                // stop streaming
                mClient.stopStream();
            }
        }
     
        @Override
        public void onDestroy() {
            super.onDestroy();
            mClient.release();
            mSession.release();
            mSurfaceView.getHolder().removeCallback(this);
        }

    Complete Code
    Following is the full code of my main activity.

    MainActivity.java
    package info.androidhive.androidvideostreaming;
     
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
     
    import net.majorkernelpanic.streaming.Session;
    import net.majorkernelpanic.streaming.SessionBuilder;
    import net.majorkernelpanic.streaming.audio.AudioQuality;
    import net.majorkernelpanic.streaming.gl.SurfaceView;
    import net.majorkernelpanic.streaming.rtsp.RtspClient;
    import net.majorkernelpanic.streaming.video.VideoQuality;
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.SurfaceHolder;
    import android.view.Window;
    import android.view.WindowManager;
     
    public class MainActivity extends Activity implements RtspClient.Callback,
            Session.Callback, SurfaceHolder.Callback {
        // log tag
        public final static String TAG = MainActivity.class.getSimpleName();
     
        // surfaceview
        private static SurfaceView mSurfaceView;
     
        // Rtsp session
        private Session mSession;
        private static RtspClient mClient;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            // getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
     
            setContentView(R.layout.activity_main);
     
            mSurfaceView = (SurfaceView) findViewById(R.id.surface);
     
            mSurfaceView.getHolder().addCallback(this);
     
            // Initialize RTSP client
            initRtspClient();       
     
        }
     
        @Override
        protected void onResume() {
            super.onResume();
             
            toggleStreaming();
        }
         
        @Override
        protected void onPause(){
            super.onPause();
             
            toggleStreaming();
        }
     
        private void initRtspClient() {
            // Configures the SessionBuilder
            mSession = SessionBuilder.getInstance()
                    .setContext(getApplicationContext())
                    .setAudioEncoder(SessionBuilder.AUDIO_NONE)
                    .setAudioQuality(new AudioQuality(8000, 16000))             
                    .setVideoEncoder(SessionBuilder.VIDEO_H264)
                    .setSurfaceView(mSurfaceView).setPreviewOrientation(0)
                    .setCallback(this).build();
     
            // Configures the RTSP client
            mClient = new RtspClient();
            mClient.setSession(mSession);
            mClient.setCallback(this);
            mSurfaceView.setAspectRatioMode(SurfaceView.ASPECT_RATIO_PREVIEW);
            String ip, port, path;
     
            // We parse the URI written in the Editext
            Pattern uri = Pattern.compile("rtsp://(.+):(\d+)/(.+)");
            Matcher m = uri.matcher(AppConfig.STREAM_URL);
            m.find();
            ip = m.group(1);
            port = m.group(2);
            path = m.group(3);
     
            mClient.setCredentials(AppConfig.PUBLISHER_USERNAME,
                    AppConfig.PUBLISHER_PASSWORD);
            mClient.setServerAddress(ip, Integer.parseInt(port));
            mClient.setStreamPath("/" + path);
        }
     
        private void toggleStreaming() {
            if (!mClient.isStreaming()) {
                // Start camera preview
                mSession.startPreview();
     
                // Start video stream
                mClient.startStream();
            } else {
                // already streaming, stop streaming
                // stop camera preview
                mSession.stopPreview();
     
                // stop streaming
                mClient.stopStream();
            }
        }
     
        @Override
        public void onDestroy() {
            super.onDestroy();
            mClient.release();
            mSession.release();
            mSurfaceView.getHolder().removeCallback(this);
        }
     
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
     
        @Override
        public void onSessionError(int reason, int streamType, Exception e) {
            switch (reason) {
            case Session.ERROR_CAMERA_ALREADY_IN_USE:
                break;
            case Session.ERROR_CAMERA_HAS_NO_FLASH:
                break;
            case Session.ERROR_INVALID_SURFACE:
                break;
            case Session.ERROR_STORAGE_NOT_READY:
                break;
            case Session.ERROR_CONFIGURATION_NOT_SUPPORTED:
                break;
            case Session.ERROR_OTHER:
                break;
            }
     
            if (e != null) {
                alertError(e.getMessage());
                e.printStackTrace();
            }
        }
     
        private void alertError(final String msg) {
            final String error = (msg == null) ? "Unknown error: " : msg;
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setMessage(error).setPositiveButton("Ok",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                        }
                    });
            AlertDialog dialog = builder.create();
            dialog.show();
        }
     
        @Override
        public void onRtspUpdate(int message, Exception exception) {
            switch (message) {
            case RtspClient.ERROR_CONNECTION_FAILED:
            case RtspClient.ERROR_WRONG_CREDENTIALS:
                alertError(exception.getMessage());
                exception.printStackTrace();
                break;
            }
        }
     
        @Override
        public void onPreviewStarted() {
        }
     
        @Override
        public void onSessionConfigured() {
        }
     
        @Override
        public void onSessionStarted() {
        }
     
        @Override
        public void onSessionStopped() {
        }
     
        @Override
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
        }
     
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }
     
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
     
        @Override
        public void onBitrareUpdate(long bitrate) {
        }
     
    }

    Now we are done with our android application. Let’s create a web application which displays the streaming video on web page.

    4. Creating web app

    4.1 Installing WAMP:
    I have already explained lot of times what is WAMP and it’s use (here & here). Download and Install WAMP from http://www.wampserver.com/en/ and start the program from Start => All Programs. Once started, you should be able to access http://localhost/ in the browser.



    4.2 jWPlayer
    On the web page to play streamed I am using jwplayer which supports RTMP protocol. jwplayer is a flash player with javascript API enabled which means you can control the player with javascript methods.

    Create an account at http://www.jwplayer.com/sign-up/ and download the jWplayer. Once you login into jwplayer.com, you can download a copy from https://account.jwplayer.com/#/account. Click onDownload Self-Hosted Player at the bottom of the page.



    1. Now create a folder in your wamp/www directory (Normally wamp will be located at C:/wamp). I am creating a folder named wowza_web_app inside www.

    2. Inside wowza_web_app create two more folders named css and js.

    3. Paste jwplayer files inside js folder. You have to copy jwplayer.jsjwplayer.html5.js andjwplayer.flash.swf. Also paste jquery-1.9.1.min. Download jquery from here

    4. Create an html file named index.html in wowza_web_app and paste the following content. This page will have jwplayer and other UI to play and stop streaming video.

    index.html
    
    
        
            
            Android Video Streaming
            
                   
            
            
                   
        
     
        
            
            
            
     
            
            




    5. Inside css folder create a file named style.css and paste the following styles.

    style.css
    /* 
        Document   : style
        Created on : May 31, 2014, 2:09:28 AM
        Author     : Ravi Tamada    
    */
     
    body { 
        padding:0;
        margin: 0;
    }
    .clear{
        clear: both;
    }
    .container{
        width: 1100px;
        margin: 0 auto;
        padding: 0;
    }
    #header{
        text-align: left; 
        box-shadow: 0px 3px 3px #e3e3e3;
    }
    #header h1{
        font:normal 35px arial;
        color: #ed4365;
        margin: 0;
        padding: 15px 0;
    }
    #video_preview{
        text-align: center;
    }
    #player, #player_wrapper{
        margin: 0 auto !important;
        margin-bottom: 20px !important;
        margin-top: 60px !important;    
    }
    input#stream_url{
        background: none;
        border: 2px solid #92d07f;
        outline: none;
        padding: 5px 10px;
        font: 18px arial;
        color: #666;
        width: 600px;
        text-align: center;
    }
    #btn_start, #btn_stop{
        padding: 8px 30px;
        color: #fff;
        border: none;
        outline: none;
        font: normal 16px arial;
        border-radius: 6px;
        cursor: pointer;
        margin-top: 15px;
    }
    #btn_start{
        background: #3bbe13;    
    }
    #btn_stop{
        background: #e6304f;   
    }
    .info{
        margin-top: 80px;
        text-align: center;    
        font:normal 13px verdana;
    }
    .info p{
        line-height: 25px;
    }
    .info a{
        color: #f05539;
    }

    6. Create player.js file inside js folder and paste following javascript. This script takes care of initializing jwplayer and other button click events.

    player.js
    var data = [];
    var jw_width = 640, jw_height = 360;
     
    // Outputs some logs about jwplayer
    function print(t, obj) {
        for (var a in obj) {
            if (typeof obj[a] === "object")
                print(t + '.' + a, obj[a]);
            else
                data[t + '.' + a] = obj[a];
        }
    }
     
    $(document).ready(function() {
     
        jwplayer('player').setup({
            wmode: 'transparent',
            width: jw_width,
            height: jw_height,
            stretching: 'exactfit'
        });
     
        $('#btn_start').click(function() {
            startPlayer($('#stream_url').val());
        });
     
        $('#btn_stop').click(function() {
            jwplayer('player').stop();
        });
     
     
     
        startPlayer($('#stream_url').val());
    });
     
    // Starts the flash player
    function startPlayer(stream) {
     
        jwplayer('player').setup({
            height: jw_height,
            width: jw_width,
            stretching: 'exactfit',
            sources: [{
                    file: stream
                }],
            rtmp: {
                bufferlength: 3
            }
        });
     
        jwplayer("player").onMeta(function(event) {
            var info = "";
            for (var key in data) {
                info += key + " = " + data[key] + "
    "; } print("event", event); }); jwplayer('player').play(); }

    7. Now if you access http://localhost/wowza_web_app/index.html in browser, the page should render like below.

    loading Đang tải...

    template được ưa chuộng