下载视频时同时播放

13

我希望能够实现在线视频播放功能以及下载。我的意思是,应该使用相同的下载流来下载和播放视频,以便视频可以保存以供离线使用,并避免分别播放和下载时的两倍数据成本。

到目前为止,我已经使用asyncTask实现了视频下载,并在OnPostExecute中播放它。以下是代码:

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();
}
}

以上代码可以正常下载和播放。在DoInBackground中有一些被注释掉的代码行,我试图通过它来实现我的目标,但它显示“无法播放视频”。 有人知道解决方法吗?请帮帮我。


在YouTube上只有流媒体,我想。我需要的是仅使用一个输入流在线播放视频并将其保存以供离线使用。 - Sachin Chandil
你的playVideo()方法使用带有setVideoPath()的文件。换句话说,playVideo()仅适用于磁盘上完全下载的视频。你需要将单个帧和音频传递给媒体播放器进行流式传输,然后在查看后将这些字节简单地传递到文件中以进行缓存以供离线使用。 - MeetTitan
你需要做的是在下载视频时,将每一帧流式传输到播放器中,如@MeetTitan所提到的那样。实际上,你需要在手机上创建一个流媒体服务器,将数据流式传输到自己的播放器上,而不是在服务器上进行流媒体传输。 - midhunhk
感谢您的快速回复。@midhunhk,您所说的“手机上的流媒体服务器”是什么意思?您能否用更多的技术术语来详细说明一下? - Sachin Chandil
1
@khetanrajesh 我已经实现了这个功能,如果你想查看,请参考 https://github.com/chandilsachin/VideoDownloadAndPlay - Sachin Chandil
显示剩余5条评论
2个回答

9

您可以创建本地代理来保存流。

创建两个后台线程:下载线程流媒体线程

流媒体线程中创建ServerSocket并流数据,这些数据刚刚被下载。

在VideoView中打开您的ServerSocket的本地主机URL。

您将需要处理缓冲,同步线程等。


我不想创建两个线程。我只想有一个输入流来节省双倍的数据消耗。顺便说一下,我不知道如何创建本地代理。如果能给我解释一下就好了。谢谢。 - Sachin Chandil
一个线程用于从互联网下载,第二个线程用于流式传输到媒体播放器(VideoView)=没有双重数据消耗。这里有使用ServerSocket的示例:https://dev59.com/iGUp5IYBdhLWcg3wEEXd - Milos Fec
MediaPlayer只接受网络URL、文件描述符和本地文件URI来播放视频。那么StreamingThread如何播放视频呢? - Sachin Chandil
2
当您创建ServerSocket时,将拥有本地主机URL(即127.0.0.1:6543/或localhost:6543/)。您可以设置自己的端口(即6543),也可以让系统为您分配一些空闲端口。 - Milos Fec
1
我按照您的建议做了。现在我有一个新问题,每当我点击本地套接字服务器链接时,在服务器端会抛出异常。请参见此问题http://stackoverflow.com/questions/34728935/android-local-video-server - Sachin Chandil

6
通过 Milos Fec 的答案,我解决了这个问题。我不得不创建两个线程,一个用于下载,另一个用于通过 socketServer 流式传输已下载的内容,同时注意数据下载和播放数据的同步。
我将整个代码作为库放在 github 上,请在此检查编辑:该库不适用于所有类型的视频,其中视频服务器通过分块等方式对下载施加限制...
我只测试了 mp4 格式。该库需要公共链接。
我在 Android 开发的早期阶段就开始研究本地服务器和远程服务器之间的同步,这个库有很大的改进空间,但由于有其他满足相同要求的选择,并且我没有足够的时间做其他事情,所以我没有进行任何改进。

1
我该如何获取服务器路径? - Vikash Sharma
您能告诉我在下面的代码中需要传递哪些示例输入吗? videoService = VideoDownloadAndPlayService.startServer(getActivity(), videoPath,outFilePath, serverPath, new VideoDownloadAndPlayService.VideoStreamInterface() { @Override public void onServerStart(String videoStreamUrl) { // 使用 videoStreamUrl 通过媒体播放器播放视频 } }); - Bala Saikrupa Puram
什么是服务器ID、服务器路径,我该如何获取?我正在使用Firebase。 - John dahat
@Johndahat 的 serverPath 是 http://localhost:portNo。请查看使用此库的演示应用程序 https://github.com/chandilsachin/VideoPlayerAndDownloaderDemo。 - Sachin Chandil
@Johndahat,我的库存在问题,我没有足够的时间来修复它们。我建议你看一下这个 https://github.com/danikula/AndroidVideoCache。 - Sachin Chandil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接