IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Android接收UDP TS流实现边缓存边播放

    cuiran发表于 2014-11-06 09:23:53
    love 0

    Android播放本地文件视频是硬解,这样对CPU占用比较少,所以将直播收到的数据临时缓存3个文件,给定每个文件的大小,然后进行播放。后续还会进行优化。

    具体实现代码如下:

    package com.cayden.videodemo;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.content.Intent;
    import android.media.MediaPlayer;
    import android.media.MediaPlayer.OnCompletionListener;
    import android.media.MediaPlayer.OnErrorListener;
    import android.media.MediaPlayer.OnPreparedListener;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.View;
    import android.widget.MediaController;
    import android.widget.TextView;
    import android.widget.VideoView;
    import com.cayden.videodemo.R;
    /**
     * 
     *  接收UDP TS流实现边缓存边播放
    * 该类可以实现,但存在以下不足
    * 1、播放过程会稍微卡一下这是 由于播放时候setOnCompletionListener中方法被执行
    * 2、需要思考怎么解决调用onError方法 * @author cuiran * @version 1.0.0 */ public class UDPFileMPlayer extends Activity { private static final String TAG="UDPFileMPlayer"; private VideoView mVideoView; private TextView tvcache; private String remoteUrl; private String localUrl; private ProgressDialog progressDialog = null; private Thread receiveThread=null; /** * 定义了初始缓存区的大小,当视频加载到初始缓存区满的时候,播放器开始播放, */ private static final int READY_BUFF = 1316 * 1024*10; private static final String FILE_DIR=Environment.getExternalStorageDirectory().getAbsolutePath()+"/VideoCache/"; /** * 核心交换缓存区,主要是用来动态调节缓存区,当网络环境较好的时候,该缓存区为初始大小, * 当网络环境差的时候,该缓存区会动态增加,主要就是为了避免视频播放的时候出现一卡一卡的现象。 */ private static final int CACHE_BUFF = 10 * 1024; /** * 单播或组播端口 */ private static final int PORT = 1234; private boolean isready = false; private boolean iserror = false; private int errorCnt = 0; private int curPosition = 0; private long mediaLength = 0; private long readSize = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.bbvideoplayer); findViews(); init(); playvideo(); } /** * 初始化组件 * 2013-11-21 下午2:20:10 * */ private void findViews() { this.mVideoView = (VideoView) findViewById(R.id.bbvideoview); this.tvcache = (TextView) findViewById(R.id.tvcache); } private void init() { Intent intent = getIntent(); this.remoteUrl = intent.getStringExtra("url"); System.out.println("remoteUrl: " + remoteUrl); if (this.remoteUrl == null) { finish(); return; } // this.localUrl = intent.getStringExtra("cache"); mVideoView.setMediaController(new MediaController(this)); mVideoView.setOnPreparedListener(new OnPreparedListener() { public void onPrepared(MediaPlayer mediaplayer) { Log.i(TAG, "onPrepared"); dismissProgressDialog(); mVideoView.seekTo(curPosition); mediaplayer.start(); } }); mVideoView.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mediaplayer) { Log.i(TAG, "onCompletion"+localUrl); // curPosition = 0; if(localUrl.endsWith("1.mp4")){ localUrl=localUrl.replace("1.mp4", "2.mp4"); mVideoView.setVideoPath(localUrl); mVideoView.start(); }else if(localUrl.endsWith("2.mp4")){ localUrl=localUrl.replace("2.mp4", "3.mp4"); mVideoView.setVideoPath(localUrl); mVideoView.start(); }else{ localUrl=localUrl.replace("3.mp4", "1.mp4"); mVideoView.setVideoPath(localUrl); mVideoView.start(); } } }); mVideoView.setOnErrorListener(new OnErrorListener() { public boolean onError(MediaPlayer mediaplayer, int i, int j) { Log.i(TAG, "onError"); iserror = true; errorCnt++; mVideoView.pause(); showProgressDialog(); return true; } }); } private void showProgressDialog() { mHandler.post(new Runnable() { @Override public void run() { if (progressDialog == null) { progressDialog = ProgressDialog.show(UDPFileMPlayer.this, "视频缓存", "正在努力加载中 ...", true, false); } } }); } private void dismissProgressDialog() { mHandler.post(new Runnable() { @Override public void run() { if (progressDialog != null) { progressDialog.dismiss(); progressDialog = null; } } }); } /** * 播放视频 * 2013-11-21 下午2:20:34 * */ private void playvideo() { showProgressDialog(); receiveThread=new Thread(new Runnable() { @Override public void run() { FileOutputStream out = null; DatagramSocket dataSocket=null; DatagramPacket dataPacket=null; try { dataSocket = new DatagramSocket(PORT); byte[] receiveByte = new byte[8192]; dataPacket = new DatagramPacket(receiveByte, receiveByte.length); Log.i(TAG, "UDP服务启动..."); if (localUrl == null) { localUrl = FILE_DIR+"1.mp4"; } Log.i(TAG, "localUrl="+localUrl); File cacheFile = new File(localUrl); if (!cacheFile.exists()) { cacheFile.getParentFile().mkdirs(); cacheFile.createNewFile(); } out = new FileOutputStream(cacheFile, true); int size = 0; long lastReadSize = 0; int number=0; int fileNum=0; // mHandler.sendEmptyMessage(VIDEO_STATE_UPDATE); while(size==0){ // 无数据,则循环 dataSocket.receive(dataPacket); size = dataPacket.getLength(); if (size > 0) { try { if(readSize>=READY_BUFF){ fileNum++; switch(fileNum%3){ case 0: out=new FileOutputStream(FILE_DIR+"1.mp4"); break; case 1: out=new FileOutputStream(FILE_DIR+"2.mp4"); break; case 2: out=new FileOutputStream(FILE_DIR+"3.mp4"); break; } readSize=0; if (!isready) { mHandler.sendEmptyMessage(CACHE_VIDEO_READY); } } out.write(dataPacket.getData(), 0, size); out.flush(); readSize += size; size = 0;// 循环接收 } catch (Exception e) { Log.e(TAG, "出现异常0",e); } }else{ Log.i(TAG, "TS流停止发送数据"); } } mHandler.sendEmptyMessage(CACHE_VIDEO_END); } catch (Exception e) { Log.e(TAG, "出现异常",e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // Log.e(TAG, "出现异常1",e); } } if (dataSocket != null) { try { dataSocket.close(); } catch (Exception e) { Log.e(TAG, "出现异常2",e); } } } } }); receiveThread.start(); } private final static int VIDEO_STATE_UPDATE = 0; /** * 缓存准备 */ private final static int CACHE_VIDEO_READY = 1; /** * 缓存修改 */ private final static int CACHE_VIDEO_UPDATE = 2; /** * 缓存结束 */ private final static int CACHE_VIDEO_END = 3; /** * 缓存播放 */ private final static int CACHE_VIDEO_PLAY = 4; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case VIDEO_STATE_UPDATE: boolean isPlay=mVideoView.isPlaying(); Log.i(TAG, "更新显示 isPlay="+isPlay); double cachepercent = readSize * 100.00 / mediaLength * 1.0; String s = String.format("已缓存: [%.2f%%]", cachepercent); if (isPlay) { curPosition = mVideoView.getCurrentPosition(); int duration = mVideoView.getDuration(); duration = duration == 0 ? 1 : duration; double playpercent = curPosition * 100.00 / duration * 1.0; int i = curPosition / 1000; int hour = i / (60 * 60); int minute = i / 60 % 60; int second = i % 60; s += String.format(" 播放: %02d:%02d:%02d [%.2f%%]", hour, minute, second, playpercent); } // // tvcache.setText(s); tvcache.setVisibility(View.GONE); mHandler.sendEmptyMessageDelayed(VIDEO_STATE_UPDATE, 1000); break; case CACHE_VIDEO_READY: Log.i(TAG, "缓存准备"); isready = true; mVideoView.setVideoPath(localUrl); mVideoView.start(); break; case CACHE_VIDEO_UPDATE: Log.i(TAG, "缓存修改"+iserror); if (iserror) { mVideoView.setVideoPath(localUrl); mVideoView.start(); iserror = false; } break; case CACHE_VIDEO_END: Log.i(TAG, "缓存结束"+iserror); if (iserror) { mVideoView.setVideoPath(localUrl); mVideoView.start(); iserror = false; } break; case CACHE_VIDEO_PLAY: Log.i(TAG, "CACHE_VIDEO_PLAY"); mVideoView.setVideoPath(localUrl); mVideoView.start(); mHandler.sendEmptyMessageDelayed(CACHE_VIDEO_PLAY, 5000); break; } super.handleMessage(msg); } }; @Override protected void onDestroy() { // TODO Auto-generated method stub if(mVideoView!=null){ mVideoView.stopPlayback(); } super.onDestroy(); } }




沪ICP备19023445号-2号
友情链接