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

      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流实现边缓存边播放<br/>
 * 该类可以实现,但存在以下不足<br/>
 * 1、播放过程会稍微卡一下这是 由于播放时候setOnCompletionListener中方法被执行<br/>
 * 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();
	}
	
	
}


评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cuiran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值