Android接收UDP TS流实现边缓存边播放_郝三旭的博客-程序员秘密

技术标签: android  

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

具体实现代码如下:

[java]  view plain   copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.cayden.videodemo;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.net.DatagramPacket;  
  7. import java.net.DatagramSocket;  
  8.   
  9.   
  10. import android.app.Activity;  
  11. import android.app.ProgressDialog;  
  12. import android.content.Intent;  
  13. import android.media.MediaPlayer;  
  14. import android.media.MediaPlayer.OnCompletionListener;  
  15. import android.media.MediaPlayer.OnErrorListener;  
  16. import android.media.MediaPlayer.OnPreparedListener;  
  17. import android.os.Bundle;  
  18. import android.os.Environment;  
  19. import android.os.Handler;  
  20. import android.os.Message;  
  21. import android.util.Log;  
  22. import android.view.View;  
  23. import android.widget.MediaController;  
  24. import android.widget.TextView;  
  25. import android.widget.VideoView;  
  26. import com.cayden.videodemo.R;  
  27. /** 
  28.  *  
  29.  *  接收UDP TS流实现边缓存边播放<br/> 
  30.  * 该类可以实现,但存在以下不足<br/> 
  31.  * 1、播放过程会稍微卡一下这是 由于播放时候setOnCompletionListener中方法被执行<br/> 
  32.  * 2、需要思考怎么解决调用onError方法 
  33.  * @author cuiran 
  34.  * @version 1.0.0 
  35.  */  
  36. public class UDPFileMPlayer extends Activity {  
  37.   
  38.     private static final String TAG="UDPFileMPlayer";  
  39.     private VideoView mVideoView;  
  40.     private TextView tvcache;  
  41.     private String remoteUrl;  
  42.     private String localUrl;  
  43.     private ProgressDialog progressDialog = null;  
  44.     private Thread receiveThread=null;  
  45.     /** 
  46.      * 定义了初始缓存区的大小,当视频加载到初始缓存区满的时候,播放器开始播放, 
  47.      */  
  48.     private static final int READY_BUFF = 1316 * 1024*10;  
  49.       
  50.     private static final String FILE_DIR=Environment.getExternalStorageDirectory().getAbsolutePath()+"/VideoCache/";  
  51.               
  52.     /** 
  53.      * 核心交换缓存区,主要是用来动态调节缓存区,当网络环境较好的时候,该缓存区为初始大小, 
  54.      * 当网络环境差的时候,该缓存区会动态增加,主要就是为了避免视频播放的时候出现一卡一卡的现象。 
  55.      */  
  56.     private static final int CACHE_BUFF = 10 * 1024;  
  57.     /** 
  58.      * 单播或组播端口 
  59.      */  
  60.     private static final int PORT = 1234;  
  61.       
  62.     private boolean isready = false;  
  63.     private boolean iserror = false;  
  64.     private int errorCnt = 0;  
  65.     private int curPosition = 0;  
  66.     private long mediaLength = 0;  
  67.     private long readSize = 0;  
  68.   
  69.     @Override  
  70.     protected void onCreate(Bundle savedInstanceState) {  
  71.         super.onCreate(savedInstanceState);  
  72.         setContentView(R.layout.bbvideoplayer);  
  73.   
  74.         findViews();  
  75.         init();  
  76.         playvideo();  
  77.     }  
  78.     /** 
  79.      * 初始化组件 
  80.      * 2013-11-21 下午2:20:10 
  81.      * 
  82.      */  
  83.     private void findViews() {  
  84.         this.mVideoView = (VideoView) findViewById(R.id.bbvideoview);  
  85.         this.tvcache = (TextView) findViewById(R.id.tvcache);  
  86.     }  
  87.       
  88.     private void init() {  
  89.         Intent intent = getIntent();  
  90.   
  91.         this.remoteUrl = intent.getStringExtra("url");  
  92.         System.out.println("remoteUrl: " + remoteUrl);  
  93.   
  94.         if (this.remoteUrl == null) {  
  95.             finish();  
  96.             return;  
  97.         }  
  98.   
  99. //      this.localUrl = intent.getStringExtra("cache");  
  100.         mVideoView.setMediaController(new MediaController(this));  
  101.         mVideoView.setOnPreparedListener(new OnPreparedListener() {  
  102.   
  103.             public void onPrepared(MediaPlayer mediaplayer) {  
  104.                 Log.i(TAG, "onPrepared");  
  105.                 dismissProgressDialog();  
  106.                 mVideoView.seekTo(curPosition);  
  107.                 mediaplayer.start();  
  108.             }  
  109.         });  
  110.   
  111.         mVideoView.setOnCompletionListener(new OnCompletionListener() {  
  112.   
  113.             public void onCompletion(MediaPlayer mediaplayer) {  
  114.                 Log.i(TAG, "onCompletion"+localUrl);  
  115. //              curPosition = 0;  
  116.                 if(localUrl.endsWith("1.mp4")){  
  117.                     localUrl=localUrl.replace("1.mp4""2.mp4");  
  118.                     mVideoView.setVideoPath(localUrl);  
  119.                     mVideoView.start();  
  120.                 }else if(localUrl.endsWith("2.mp4")){  
  121.                     localUrl=localUrl.replace("2.mp4""3.mp4");  
  122.                     mVideoView.setVideoPath(localUrl);  
  123.                     mVideoView.start();  
  124.                 }else{  
  125.                     localUrl=localUrl.replace("3.mp4""1.mp4");  
  126.                     mVideoView.setVideoPath(localUrl);  
  127.                     mVideoView.start();  
  128.                 }  
  129.                   
  130.             }  
  131.         });  
  132.   
  133.         mVideoView.setOnErrorListener(new OnErrorListener() {  
  134.   
  135.             public boolean onError(MediaPlayer mediaplayer, int i, int j) {  
  136.                 Log.i(TAG, "onError");  
  137.                 iserror = true;  
  138.                 errorCnt++;  
  139.                 mVideoView.pause();  
  140.                 showProgressDialog();  
  141.                 return true;  
  142.             }  
  143.         });  
  144.     }  
  145.   
  146.     private void showProgressDialog() {  
  147.         mHandler.post(new Runnable() {  
  148.   
  149.             @Override  
  150.             public void run() {  
  151.                 if (progressDialog == null) {  
  152.                     progressDialog = ProgressDialog.show(UDPFileMPlayer.this,  
  153.                             "视频缓存""正在努力加载中 ..."truefalse);  
  154.                 }  
  155.             }  
  156.         });  
  157.     }  
  158.   
  159.     private void dismissProgressDialog() {  
  160.         mHandler.post(new Runnable() {  
  161.   
  162.             @Override  
  163.             public void run() {  
  164.                 if (progressDialog != null) {  
  165.                     progressDialog.dismiss();  
  166.                     progressDialog = null;  
  167.                 }  
  168.             }  
  169.         });  
  170.     }  
  171.     /** 
  172.      * 播放视频 
  173.      * 2013-11-21 下午2:20:34 
  174.      * 
  175.      */  
  176.     private void playvideo() {  
  177.           
  178.   
  179.         showProgressDialog();  
  180.   
  181.         receiveThread=new Thread(new Runnable() {  
  182.   
  183.             @Override  
  184.             public void run() {  
  185.                 FileOutputStream out = null;  
  186.                 DatagramSocket dataSocket=null;  
  187.                 DatagramPacket dataPacket=null;  
  188.                 try {  
  189.                      dataSocket = new DatagramSocket(PORT);  
  190.                     byte[] receiveByte = new byte[8192];  
  191.                      dataPacket = new DatagramPacket(receiveByte, receiveByte.length);  
  192.                     Log.i(TAG, "UDP服务启动...");  
  193.                     if (localUrl == null) {  
  194.                         localUrl = FILE_DIR+"1.mp4";  
  195.                     }  
  196.                     Log.i(TAG, "localUrl="+localUrl);  
  197.                     File cacheFile = new File(localUrl);  
  198.   
  199.                     if (!cacheFile.exists()) {  
  200.                         cacheFile.getParentFile().mkdirs();  
  201.                         cacheFile.createNewFile();  
  202.                     }  
  203.                       
  204.                     out = new FileOutputStream(cacheFile, true);  
  205.                     int size = 0;  
  206.                     long lastReadSize = 0;  
  207.                     int number=0;  
  208.                       
  209.                     int fileNum=0;  
  210. //                  mHandler.sendEmptyMessage(VIDEO_STATE_UPDATE);  
  211.                   
  212.                      while(size==0){  
  213.                         // 无数据,则循环  
  214.                          dataSocket.receive(dataPacket);  
  215.                          size = dataPacket.getLength();  
  216.                           if (size > 0) {  
  217.                                 try {  
  218.                                     if(readSize>=READY_BUFF){  
  219.                                         fileNum++;  
  220.                                           
  221.                                         switch(fileNum%3){  
  222.                                             case 0:  
  223.                                                 out=new FileOutputStream(FILE_DIR+"1.mp4");  
  224.                                                 break;  
  225.                                             case 1:  
  226.                                                 out=new FileOutputStream(FILE_DIR+"2.mp4");  
  227.                                                 break;  
  228.                                             case 2:  
  229.                                                 out=new FileOutputStream(FILE_DIR+"3.mp4");  
  230.                                                 break;  
  231.                                         }  
  232.                                           
  233.                                         readSize=0;  
  234.                                         if (!isready) {  
  235.                                             mHandler.sendEmptyMessage(CACHE_VIDEO_READY);  
  236.                                         }  
  237.                                     }  
  238.                                     out.write(dataPacket.getData(), 0, size);  
  239.                                     out.flush();  
  240.                                     readSize += size;  
  241.                                     size = 0;// 循环接收  
  242.                                       
  243.                                       
  244.                                 } catch (Exception e) {  
  245.                                     Log.e(TAG, "出现异常0",e);  
  246.                                 }  
  247.                                   
  248.                           }else{  
  249.                               Log.i(TAG, "TS流停止发送数据");  
  250.                           }  
  251.                             
  252.                      }  
  253.                   
  254.                     mHandler.sendEmptyMessage(CACHE_VIDEO_END);  
  255.                 } catch (Exception e) {  
  256.                     Log.e(TAG, "出现异常",e);  
  257.                 } finally {  
  258.                     if (out != null) {  
  259.                         try {  
  260.                             out.close();  
  261.                         } catch (IOException e) {  
  262.                             //  
  263.                             Log.e(TAG, "出现异常1",e);  
  264.                         }  
  265.                     }  
  266.   
  267.                     if (dataSocket != null) {  
  268.                         try {  
  269.                             dataSocket.close();  
  270.                         } catch (Exception e) {  
  271.                             Log.e(TAG, "出现异常2",e);  
  272.                         }  
  273.                     }  
  274.                 }  
  275.   
  276.             }  
  277.         });  
  278.         receiveThread.start();  
  279.     }  
  280.   
  281.     private final static int VIDEO_STATE_UPDATE = 0;  
  282.     /** 
  283.      * 缓存准备 
  284.      */  
  285.     private final static int CACHE_VIDEO_READY = 1;  
  286.     /** 
  287.      * 缓存修改 
  288.      */  
  289.     private final static int CACHE_VIDEO_UPDATE = 2;  
  290.     /** 
  291.      * 缓存结束 
  292.      */  
  293.     private final static int CACHE_VIDEO_END = 3;  
  294.     /** 
  295.      * 缓存播放 
  296.      */  
  297.     private final static int CACHE_VIDEO_PLAY = 4;  
  298.   
  299.     private final Handler mHandler = new Handler() {  
  300.         @Override  
  301.         public void handleMessage(Message msg) {  
  302.             switch (msg.what) {  
  303.             case VIDEO_STATE_UPDATE:  
  304.                 boolean isPlay=mVideoView.isPlaying();  
  305.                 Log.i(TAG, "更新显示 isPlay="+isPlay);  
  306.                 double cachepercent = readSize * 100.00 / mediaLength * 1.0;  
  307.                 String s = String.format("已缓存: [%.2f%%]", cachepercent);  
  308.                 if (isPlay) {  
  309.                     curPosition = mVideoView.getCurrentPosition();  
  310.                     int duration = mVideoView.getDuration();  
  311.                     duration = duration == 0 ? 1 : duration;  
  312.   
  313.                     double playpercent = curPosition * 100.00 / duration * 1.0;  
  314.   
  315.                     int i = curPosition / 1000;  
  316.                     int hour = i / (60 * 60);  
  317.                     int minute = i / 60 % 60;  
  318.                     int second = i % 60;  
  319.   
  320.                     s += String.format(" 播放: %02d:%02d:%02d [%.2f%%]", hour,  
  321.                             minute, second, playpercent);  
  322.                 }  
  323. //  
  324. //              tvcache.setText(s);  
  325.                 tvcache.setVisibility(View.GONE);  
  326.                 mHandler.sendEmptyMessageDelayed(VIDEO_STATE_UPDATE, 1000);  
  327.                   
  328.   
  329.                   
  330.                 break;  
  331.   
  332.             case CACHE_VIDEO_READY:  
  333.                 Log.i(TAG, "缓存准备");  
  334.                 isready = true;  
  335.                 mVideoView.setVideoPath(localUrl);  
  336.                 mVideoView.start();  
  337.                   
  338.                 break;  
  339.   
  340.             case CACHE_VIDEO_UPDATE:  
  341.                 Log.i(TAG, "缓存修改"+iserror);  
  342.                 if (iserror) {  
  343.                     mVideoView.setVideoPath(localUrl);  
  344.                     mVideoView.start();  
  345.                     iserror = false;  
  346.                 }  
  347.                 break;  
  348.   
  349.             case CACHE_VIDEO_END:  
  350.                 Log.i(TAG, "缓存结束"+iserror);  
  351.                 if (iserror) {  
  352.                       
  353.                     mVideoView.setVideoPath(localUrl);  
  354.                     mVideoView.start();  
  355.                     iserror = false;  
  356.                 }  
  357.                 break;  
  358.             case CACHE_VIDEO_PLAY:  
  359.                 Log.i(TAG, "CACHE_VIDEO_PLAY");  
  360.                 mVideoView.setVideoPath(localUrl);  
  361.                 mVideoView.start();  
  362.                 mHandler.sendEmptyMessageDelayed(CACHE_VIDEO_PLAY, 5000);  
  363.                 break;  
  364.             }  
  365.   
  366.             super.handleMessage(msg);  
  367.         }  
  368.     };  
  369.   
  370.     @Override  
  371.     protected void onDestroy() {  
  372.         // TODO Auto-generated method stub  
  373.         if(mVideoView!=null){  
  374.             mVideoView.stopPlayback();  
  375.         }  
  376.         super.onDestroy();  
  377.     }  
  378.       
  379.       
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011414643/article/details/52460895

智能推荐

CSS选取子标签元素:nth-child、first-child、last-child_雪梅零落的博客-程序员秘密

1、first-childfirst-child表示选择列表中的第一个标签。代码如下:li:first-child{background:#090}上面的意思是,li 列表中的 第一个li模块的背景颜色。2、last-childlast-child表示选择列表中的最后一个标签,代码如下:li:last-child{background:#090}3、nth-...

js事件clik、event.ketcode == 13等多次触发解决方案_js 触发click code13命令_senyzy的博客-程序员秘密

工作中碰到的第一次发现是下拉加载的时候,js会多次触发某个点击事件,第二次就是在做keydown中的event.keycode == 13解决方法:$('xxx').unbind('事件').事件(function(){});例子:$('#obj').unbind('keydown').keydown(function(event){});$('#obj').unbind('on').on('cl...

机器学习第三章单变量线性回归(《大话Python机器学习》学习笔记)_BianchiHB的博客-程序员秘密

3.1回归本质3.1.1拟合概念通过数据之间的关系建立一种近似的函数关系3.1.2拟合与回归的区别通过拟合方法可能找到关系曲线,但是不能确定对未知的数据拟合程度寻找合适的曲线,越来越难拟合是人们主观感知3.1.3回归的诞生《遗传的身高向平均数方向的回归》Regression3.1.4回归的本质含义不仅是数据拟合手段,更是预测,不断向平均值回归3.2单变量线性回归算法用两个变量建立线性回归方程,来拟合与预测两变量关系的算法常规求解:  最小二乘法(实际值与预测值之间偏差的平方

cJSON代码阅读(4)——解析JSON数据的流程_cjson跳过无用字符_NB_vol_1的博客-程序员秘密

解析JSON数据的主函数是cJSON_Parse,这个函数默认调用不带选项的cJSON_ParseWithOpts函数。cJSON_ParseWithOpts函数首先创建一个JSON节点,然后跳过空白字符,接着调用parse_value函数进行数据的解析,然后判断解析是否出错,如果出错,那么释放内存,然后返回空指针,全局变量ep(char*类型)记录了出错的位置。如果没有出错,那么返回树形结构

Android SwipeRefreshLayout的 swipe.setRefreshing(true)无法自动刷新问题_android refreshlayout 刷新一次后无法再次刷新_解牛之术的博客-程序员秘密

在项目中要求进入Activity时,即出现下拉刷新样式,以提醒用户正在数据正在刷新,但是用swipe.setRefreshing(true)方法却达不到效果,没有作用,经查找需要 swipe.post(new Runnable() { @Override public void run() { if (swipe.is

随便推点

深度解析linux下信号的注册和注销原理详解及配合信号更好解决僵尸进程_linux注册信号_月半木斤的博客-程序员秘密

1.信号的概念:信号是一个软中断 软中断: 例如:看到绿灯你可以选择走或选择不走。红绿灯只是一个提醒你可以选择走或不走 1.1只是告诉有这样一个信号,但是具体这个信号怎么处理,什么时候处理由进程决定,所以是软中断。 2.信号的产生 2.1硬件产生(按键盘中的按键): ctrl+c:2号信号 SIGINT,按下ctrl+c其实是进程收到了2号信号,2号信号导致进程的退出。 ctrl+z :20号信号SIGTSTP, ctrl+|:3号信号SIGQUIT

软件开发组的团队精神_aina1877的博客-程序员秘密

软件开发组的团队精神--- 一个程序员在IBM的开发经验 来源: http://www.itheroes.cn/show.asp?id=1065 ...

element之表单el-form布局_element form布局_上分的菜鸡的博客-程序员秘密

element表单el-form布局最近表单用的比较多,简单总结一下行内表单普通表单自定义表单以上两个都是element官网的例子,但并不足以满足业务需求。例如下图:element官方文档里并没有这种布局,那么直接上代码&lt;el-form label-width="150px"&gt; &lt;el-row align="middle" :gutter="10"&gt; &lt;el-col :span="10"&gt; &lt;el-form-item pro

函数式编程学习之路(六)_aibo110110的博客-程序员秘密

找到点干货:PROFESSIONALFUNCTIONAL PROGRAMMING IN C#看了下目录,正是我想要的,同时也发现,函数式编程果然博大精深,看目录,就有第一次看编程语言或是面向对象的感觉,里面的章节,基本上没有一个概念是熟悉的.先从中文目录起把概念列出来:惰性列表:迭代器闭包表达式树局部套用部分应用惰性求值缓存技术递归调用高...

【人工智能】人工智能革命与机遇_人工智能学家的博客-程序员秘密

来源 | 北大AI公开课编辑 | Vincent、Natalie课程导师:雷鸣,天使投资人,百度创始七剑客之一,酷我音乐创始人,北京大学信科人工智能创新中心主任,2000年获得北京大学计...

北师大计算机组成原理离线作业,北师大网络教育 离线作业 计算机组成原理(一、二、三)..._白条说再来一碗的博客-程序员秘密

《计算机组成原理》作业(一)一、CPU:Central Processing Unit 中央处理单元 执行存放在主存储器中的程序即机器指令.CPU是由控制器和运算器.PC:Personal Computer 个人电脑 能独立运行、完成特定功能的个人计算机 IR:Immediate Rendering 直接渲染CU:本文来自: 疯狂英语([url]http://doc...

推荐文章

热门文章

相关标签