13

I want to implement online video playing functionality along with downloading it. I mean same download stream should be used to download and play so that video can be saved for offline use and prevent two times data cost for playing and downloading separately.

So far i have implemented video downloading with asyncTask and play it on OnPostExecute. Following is the code:

public class MainActivity extends AppCompatActivity {


private Button btnPlay;
private MediaPlayer player;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    outFilePath = getExternalFilesDir("/") + "/video.mp4";
    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    prepareVideoView();

}

private VideoView videoView;
String videoPath = "http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_5mb.mp4";
String outFilePath = "";//

private void prepareVideoView() {
    MediaController mediaController = new MediaController(this);
    videoView = (VideoView) findViewById(R.id.videoView);
    mediaController.setAnchorView(videoView);
    videoView.setMediaController(mediaController);
    btnPlay = (Button) findViewById(R.id.btnPlayVideo);
    btnPlay.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new VideoDownloader().execute(videoPath);
        }
    });

    videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            player = mp;

            player.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
                @Override
                public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
                    Log.w("download","size changed");
                }
            });
        }
    });
}
File outFile;
class VideoDownloader extends AsyncTask<String, Integer, Void> {

    @Override
    protected Void doInBackground(String... params) {


        outFile = new File(outFilePath);
        FileOutputStream out = null;
        BufferedInputStream input = null;
        try {
            out = new FileOutputStream(outFile,true);

            try {
                URL url = new URL(videoPath);

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.connect();
                if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    throw new RuntimeException("response is not http_ok");
                }
                int fileLength = connection.getContentLength();

                input = new BufferedInputStream(connection.getInputStream());
                byte data[] = new byte[2048];
                long readBytes = 0;
                int len;
                boolean flag = true;
                int readb = 0;
                while ((len = input.read(data)) != -1) {
                    out.write(data,0,len);
                    readBytes += len;
   // Following commented code is to play video along with downloading but not working.
/*                      readb += len;
                    if(readb > 1000000)
                    {
                        out.flush();
                        playVideo();
                        readb = 0;
                    }
*/
                    Log.w("download",(readBytes/1024)+"kb of "+(fileLength/1024)+"kb");
                }



            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (out != null)
                    out.flush();
                    out.close();
                if(input != null)
                    input.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.w("download", "Done");
       playVideo();

    }
}

private void playVideo() {

    videoView.setVideoPath(outFile.getAbsolutePath());
    videoView.start();
}
}

Above code is working properly to download and then play. There is some line of code in comment in DoInBackground that I tried to achieve my goal but it says "cant play video". Anyone knows about solution? please help me.

Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • Are you asking about streaming a video like what happens in Youtube? – midhunhk Jan 06 '16 at 05:16
  • in youtube there is only streaming i think. what i need is use only one inputstream to play video online and save it for offline purpose. – Sachin Chandil Jan 06 '16 at 05:20
  • Your `playVideo()` method uses a file with `setVideoPath()`. In other words, `playVideo()` will *only* work with a fully downloaded video on disk. You will need to pass individual frames and audio to a media player to stream, and then simply pass those bytes to a file after viewing to cache for offline use. – MeetTitan Jan 06 '16 at 05:41
  • What you will have to do is while downloading the video, stream the frames to the player as mentioned by @MeetTitan. In effect you will need to create a streaming server on the phone which would stream the data to your own player instead of streaming happening on the server. – midhunhk Jan 06 '16 at 05:43
  • thanks for quick replay. @midhunhk what do you mean by `streaming server on phone`, Would you elaborate it in more technical terms? – Sachin Chandil Jan 06 '16 at 06:20
  • Hi @chandil03, What I meant is to have a service to be running in the background which will download the video and be able to send the video as a stream to the player. This would essentially be streaming done from the client side. Normally the streaming will be done from the server side. Note that I have not checked if this technically possible, but we may need to do more research on these ideas. – midhunhk Jan 06 '16 at 07:11
  • hi @midhunk and MeetTitan, I did some research over it and came across to a solution but stuck in a problem, here is the link if u want to have a look: http://stackoverflow.com/questions/34728935/android-local-video-server. – Sachin Chandil Jan 12 '16 at 02:19
  • @chandil03 I am trying to achieve something similar , can you please share some code on how to achieve this , it will be great . – khetanrajesh Jan 13 '17 at 11:50
  • @khetanrajesh that is still unfinished yet. Sorry for not able to help you. – Sachin Chandil Jan 13 '17 at 15:16
  • 1
    @khetanrajesh i have implemented this, if you wish to have a look at it please refer https://github.com/chandilsachin/VideoDownloadAndPlay – Sachin Chandil May 17 '17 at 07:37

2 Answers2

9

You can create local proxy that will save the stream.

Create two background threads: download thread and streaming thread.

In the streaming thread create ServerSocket and stream data that are just being downloaded.

In the VideoView open the localhost url of your ServerSocket.

You will need to handle buffering, synchronizing threads etc.

Milos Fec
  • 828
  • 6
  • 11
  • i dont want to create two thread. I want to have only one inputStream to save double data consumption. BTW i dont know how to create local proxy. An explanation will be appreciated. Thanks. – Sachin Chandil Jan 08 '16 at 16:05
  • One thread for downloading from internet, second thread for streaming to media player (VideoView) = no double data consumtion. Here you have example of using ServerSocket: http://stackoverflow.com/questions/15541804/creating-the-serversocket-in-a-separate-thread – Milos Fec Jan 08 '16 at 16:45
  • MediaPlayer accepts only web url, fileDescripter, and local file uri to play a video. So how StreamingThread will play video? – Sachin Chandil Jan 08 '16 at 17:33
  • 2
    When you create ServerSocket you will have localhost url (i.e. 127.0.0.1:6543/ or localhost:6543/). You can set your own port (i.e. 6543) or let system assign you some free port. – Milos Fec Jan 08 '16 at 17:55
  • 1
    I did whatever u suggested. Now i have a new problem that whenever i hit local socket server link an exception is thrown at server side. See this que http://stackoverflow.com/questions/34728935/android-local-video-server – Sachin Chandil Jan 11 '16 at 19:11
6

With the help of Milos Fec's answer i solved this issue. I had to create two thread one for downloading and another for streaming that downloaded content over a socketServer while taking care of synchronisation of data downloaded and data being played.

I have put the whole code as a library on github check here

EDIT: This lib does not work with all the types of videos where video servers imposes restrictions over downloading by breaking video into chunks and so on....

I have tested it with mp4 only. This lib requires a public link.

I worked on this on the early days of my Android development to just learn syncing between local server and remote server. There are a lot of room for improvements in this lib and I am not doing any because there are other options that fulfill the same requirement and I don't get enough time from my other stuff also.

Sachin Chandil
  • 17,133
  • 8
  • 47
  • 65
  • 1
    how can i get serverPath?? – Vikash Sharma Jul 17 '18 at 09:51
  • can you tell me some sample inputs what are we need to pass in the below code videoService = VideoDownloadAndPlayService.startServer(getActivity(), videoPath,outFilePath, serverPath, new VideoDownloadAndPlayService.VideoStreamInterface() { @Override public void onServerStart(String videoStreamUrl) { // use videoStreamUrl to play video through media player } }); – Bala Saikrupa Puram Sep 23 '18 at 11:08
  • what id server serverPath and how can i get that??? Im using firebase – John dahat May 30 '20 at 12:44
  • @Johndahat serverPath is `http://localhost:portNo`. Check this demo app that uses this lib https://github.com/chandilsachin/VideoPlayerAndDownloaderDemo. – Sachin Chandil May 31 '20 at 17:25
  • @Johndahat There are issues with my lib. I don't have enough time to fix them. I would suggest you have a look at this https://github.com/danikula/AndroidVideoCache. – Sachin Chandil May 31 '20 at 17:30