0

It's already been a month and I am losing hope now.

I am trying to capture a video from device 1 and stream it to device 2.

I can already save a video file but the problem is when I stream it into another phone simultaneously while the device 1 is capturing video using camera.

I've seen many tutorials on streaming videos and applied on mine but the videos are already saved in the device, not being captured and streamed simultaneously.

I hope you can help me with this.

.

UPDATE................................................................

Currently I can send a text through socket. I am just including the image capture there to test saving the data in my phone, but it's not sending it through the socket yet.

This is my current code right now:

CameraActivity.Java (the one that serves both as server and the one that captures the data)

package com.example.martinlloydjose.shaveyourback;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
//import android.graphics.Camera;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.provider.MediaStore.Files.FileColumns;



//import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.os.Bundle;
import android.widget.Toast;

import static android.content.ContentValues.TAG;
import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;

public class CameraActivity extends AppCompatActivity{

    //camera --------------------------------------------------------------------------------------------
    /*camera
    private static final int CAMERA_REQUEST = 1888;
    private ImageView imageView;
    */
    private Camera mCamera;
    private SurfaceView mPreview;
    private MediaRecorder mMediaRecorder;
    //end of camera --------------------------------------------------------------------------------------------


    //server ////////////////////////////////////////////////////////////////////////////////////////////
    EditText welcomeMsg;
    TextView infoIp;
    TextView infoMsg;
    String msgLog = "";

    ServerSocket httpServerSocket;
    //end of server ////////////////////////////////////////////////////////////////////////////////////////////

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        //camera --------------------------------------------------------------------------------------------

        /*
        this.imageView = (ImageView)this.findViewById(R.id.imageView1);
        Button photoButton = (Button) this.findViewById(R.id.button1);
        photoButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_REQUEST);
            }
        });
        */

        // Create an instance of Camera
        /** Showing current image */
        try {
            new AlertDialog.Builder(this).setTitle("Test").setMessage("Running").setNeutralButton("Close", null).show();
            mCamera = getCameraInstance();

            mCamera.setDisplayOrientation(90);

            // Create our Preview view and set it as the content of our activity.
            mPreview = new CameraPreview(this, mCamera);
            FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
            preview.addView(mPreview);
        }catch (Exception error){
            Toast.makeText(this, "An error occured with your camera: "+error.toString(), Toast.LENGTH_LONG).show();
        }

        /** end of Showing current image */

        /** Capturing Image */
        try {
            Button captureButton = (Button) findViewById(R.id.button_capture);
            captureButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            // get an image from the camera
                            mCamera.takePicture(null, null, mPicture);
                        }
                    }
            );
        }catch (Exception error){
            Toast.makeText(this, "An error occured with your camera: "+error.toString(), Toast.LENGTH_LONG).show();
        }
        /** end of Capturing Image */

        //end of camera --------------------------------------------------------------------------------------------



        //server ////////////////////////////////////////////////////////////////////////////////////////////
        welcomeMsg = (EditText) findViewById(R.id.welcomemsg);
        infoIp = (TextView) findViewById(R.id.infoip);
        infoMsg = (TextView) findViewById(R.id.msg);

        infoIp.setText(getIpAddress() + ":" + HttpServerThread.HttpServerPORT + "\n");

        HttpServerThread httpServerThread = new HttpServerThread();
        httpServerThread.start();
        //end of server ////////////////////////////////////////////////////////////////////////////////////////////

    }


    //camera --------------------------------------------------------------------------------------------
    /*
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {
            Bitmap photo = (Bitmap) data.getExtras().get("data");
            imageView.setImageBitmap(photo);
        }
    }
    */

    /** Check if this device has a camera */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }
    /** end of Check if this device has a camera */

    /** A safe way to get an instance of the Camera object. */
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }
    /** end of A safe way to get an instance of the Camera object. */

    /** Capture Pictures */
    private PictureCallback mPicture = new PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {

            File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (pictureFile == null){
                Log.d(TAG, "Error creating media file, check storage permissions: "
                        /*e.getMessage()*/);
                return;
            }

            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
        }
    };

    /** end of Capture Pictures */


    /** Setting the storage */

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;

    /** Create a file Uri for saving an image or video */
    private static Uri getOutputMediaFileUri(int type){
        return Uri.fromFile(getOutputMediaFile(type));
    }

    private static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "MyCameraApp");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("MyCameraApp", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }

        return mediaFile;
    }
    /** end of Setting the storage */


    /** releasing camera */
    @Override
    protected void onPause() {
        super.onPause();
        //releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    /*private void releaseMediaRecorder(){
        if (mMediaRecorder != null) {
            mMediaRecorder.reset();   // clear recorder configuration
            mMediaRecorder.release(); // release the recorder object
            mMediaRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }*/

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }
    /** end of releasing camera */

    //end of camera --------------------------------------------------------------------------------------------



    //server -001 ////////////////////////////////////////////////////////////////////////////////////////////
    @Override
    protected void onDestroy() {
        super.onDestroy();

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


    private String getIpAddress() {
        String ip = "";
        try {
            Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
                    .getNetworkInterfaces();
            while (enumNetworkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = enumNetworkInterfaces
                        .nextElement();
                Enumeration<InetAddress> enumInetAddress = networkInterface
                        .getInetAddresses();
                while (enumInetAddress.hasMoreElements()) {
                    InetAddress inetAddress = enumInetAddress.nextElement();

                    if (inetAddress.isSiteLocalAddress()) {
                        ip += "SiteLocalAddress: "
                                + inetAddress.getHostAddress() + "\n";
                    }
                }
            }

        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            ip += "Something Wrong! " + e.toString() + "\n" +e.getMessage();
        }

        return ip;
    }


    private class HttpServerThread extends Thread {

        static final int HttpServerPORT = 8888;

        @Override
        public void run() {
            Socket socket = null;

            try {
                httpServerSocket = new ServerSocket(HttpServerPORT);

                while(true){
                    socket = httpServerSocket.accept();

                    HttpResponseThread httpResponseThread =
                            new HttpResponseThread(
                                    socket,
                                    welcomeMsg.getText().toString());
                    httpResponseThread.start();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private class HttpResponseThread extends Thread {

        Socket socket;
        String h1;

        HttpResponseThread(Socket socket, String msg){
            this.socket = socket;
            h1 = msg;
        }

        @Override
        public void run() {
            BufferedReader is;
            PrintWriter os;
            String request;


            try {
                is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                request = is.readLine();

                os = new PrintWriter(socket.getOutputStream(), true);

                String response =
                        "<html><head></head>" +
                                "<body>" +
                                "<h1>" + h1 + "</h1>" +
                                "</body></html>";

                os.print("HTTP/1.0 200" + "\r\n");
                os.print("Content type: text/html" + "\r\n");
                os.print("Content length: " + response.length() + "\r\n");
                os.print("\r\n");
                os.print(response + "\r\n");
                os.flush();
                socket.close();


                msgLog += "Request of " + request
                        + " from " + socket.getInetAddress().toString() + "\n";
                CameraActivity.this.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        infoMsg.setText(msgLog);
                    }
                });

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return;
        }
    }
    //end of server -001 ////////////////////////////////////////////////////////////////////////////////////////////


}

ViewerActivity.java

package com.example.martinlloydjose.shaveyourback;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;

public class ViewerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_viewer);

        WebView webView = (WebView)findViewById(R.id.webViewStream);
        webView.loadUrl("http://10.0.2.15:8888");
        webView.getSettings().setBuiltInZoomControls(true);
    }
}

Activity_Camera.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="0dp"
    android:paddingRight="0dp"
    android:paddingTop="0dp"
    android:paddingBottom="0dp"
    tools:context="com.example.martinlloydjose.shaveyourback.CameraActivity">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Connect using the following details"
        android:textStyle="bold" />

    <EditText
        android:id="@+id/welcomemsg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Test Message"
        android:layout_below="@+id/tvTitle"
        />

    <TextView
        android:id="@+id/infoip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/welcomemsg"
        />

    <ScrollView
        android:id="@+id/scrollSlider"
        android:layout_width="match_parent"
        android:layout_height="100sp"
        android:layout_below="@+id/infoip">

        <TextView
            android:id="@+id/msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </ScrollView>

    <!--
    <ImageView
        android:id="@+id/imageView1"
        android:layout_height="fill_parent"
        android:src="@drawable/icon"
        android:layout_width="fill_parent"
        android:layout_below="@+id/scrollSlider"
        >
    </ImageView>

    <Button
        android:id="@+id/button1"
        android:layout_below="@id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="-50sp"
        android:text="capture">
    </Button>
    -->

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_below="@+id/scrollSlider"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        />

    <Button
        android:id="@+id/button_capture"
        android:text="Capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_below="@+id/camera_preview"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="-50sp"
        />

</RelativeLayout>

Activity_Viewer.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="0dp"
    android:paddingRight="0dp"
    android:paddingTop="0dp"
    android:paddingBottom="0dp"
    tools:context="com.example.martinlloydjose.shaveyourback.ViewerActivity">

    <WebView
        android:id="@+id/webViewStream"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp" />

</RelativeLayout>

2 Answers2

0

I assume you already have a server as a mediator. In that case, you can setup an NGINX server environment and install nginx-rtmp-module on top of that. Also, you can use SRS live streaming cluster, which also creates its own server environment.

Since, both of them are open source, they will be cost efficient.

Also, srs 3.0 is unrealistically more advanced than nginx-rtmp-module because of its huge amount of features. But for simple streaming, which I assume is your case, both are equally useful, though i would suggest nginx-rtmo-module because of the power of nginx.

That covers everything for server creation. After that, you can use vlc on android, or any rtmp streaming repo from github to do the streaming. For receiving your streams, most of the major video player support live streaming. So you will just need to enter your streaming rtmp://.... address to it.


The following are some references, though they are outdated, the core is still the same.

Nginx Rtmp 1

Nginx Rtmp 2

Nginx Rtmp 3

  • I will try to do that. I tried using socket and rendering text on another phone. It works fine, but is it possible to use it as a medium of transferring video to the other device? I also updated the question with code provided, do you have some idea if is it possible with just using this concept for streaming video? – Martin Lloyd Jose Dec 18 '17 at 07:33
  • i am seeing this now. have you solved this? if not, i will look into it tomorrow. – Zameel Pichen Apr 19 '18 at 18:29
0

If only need to stream from Android to another Android, use RTMP/HLS is ok, both nginx or SRS are enough for your use scenario:

Android(publisher) --RTMP--> SRS/Nginx--RTMP/HLS--> Android(player)

Note: For publisher, you could find a lot of libraries, like FFmpeg binding, or flutter, or others. For player, also lots of libraries, while HLS is support by native Android player.

But, if you need to extend your use scenario, like:

  • How to enable the Android talk to another Android? You need WebRTC definitely.
  • How to play stream from SRT device, which is used widely in TV solution, or in long-distance transport like cross oceans.
  • How to support cluster or K8S?

For server-side, SRS 3.0 or 4.0 is a better choice, which supports WebRTC/SRT/K8S and latest update protocols like QUIC (in plan at 2021.12).

ChrisF
  • 134,786
  • 31
  • 255
  • 325
Winlin
  • 1,136
  • 6
  • 25