android 智能电视视频播放器UDP通信

在做智能电视应用的时候,最头疼的就是焦点问题,特别是对于个人开发者,没有设备这是最最头疼的事情了,在没有设备的情况下,怎么实现智能电视应用呢,接下来我是用TV程序来做演示的,所以接下来的所有操作是在有网络的情况下,TV链接到一个路由器上面,做过开发的人都知道Socket编程分为两种一个是可靠传输的TCP,另一个是不可靠传输的UDP,TCP需要知道对方的IP才能实现,UDP虽然不可靠,但是它可以实现广播来进行通信,从而得知对方的IP地址,然后就可以TCP通信了,对于智能电视的TV开发,如果你没有设备,则可以利用UDP的这个特性来实现手机操控电视,建立通信协议,然后TV端Server接收广播,手机端作为Client发送广播,所有的操作放在手机端来实现,TV只接收并处理相应的命令。

一、UDP实现

首先就是实现UDP的广播通信,下面就是UDP的Server和Client代码:
Server:为了实现能够长时间的接收客户端的信息,所以要把Server端放在线程里面如下:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 实现后台监听广播 
  3.      * @author jwzhangjie 
  4.      */  
  5.     private class UdpServerRunable implements  Runnable {  
  6.         @Override  
  7.         public void run() {  
  8.             byte[] data = new byte[256];  
  9.             DatagramPacket udpPacket = new DatagramPacket(data, 256);  
  10.             try {  
  11.                 udpSocket = new DatagramSocket(43708);  
  12.             } catch (Exception e) {  
  13.                 e.printStackTrace();  
  14.             }  
  15.             while (!isStop) {  
  16.                 try {  
  17.                     udpSocket.receive(udpPacket);  
  18.                     if (udpPacket.getLength() != 0) {  
  19.                         Url = new String(data, 0, udpPacket.getLength());  
  20.                         Log.e(TAG, Url);  
  21.                         if (onUdpServerCallBackListener != null) {  
  22.                             onUdpServerCallBackListener.onPlayUrl(Url);  
  23.                         }  
  24.                     }  
  25.                 } catch (Exception e) {  
  26.                 }  
  27.             }  
  28.         }  
  29.     };  
为了测试方便我先阶段是Client放在PC端来实现的,为了实现循环测试,我也是把客户端放在一个线程里面,代码如下:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public class Test_UDP_Client{  
  2.       
  3.     public static void main(String[] args){  
  4.         new Thread(new Runnable() {  
  5.             int i = 0;  
  6.             private byte[] buffer = new byte[40];  
  7.               
  8.             @Override  
  9.             public void run() {  
  10.                 DatagramPacket dataPacket = null;  
  11.                 DatagramSocket udpSocket = null;  
  12.                 List<String> listData = new ArrayList<String>();  
  13.                 listData.add("http://live.gslb.letv.com/gslb?stream_id=hunan&tag=live&ext=m3u8&sign=live_tv");  
  14. <span style="white-space:pre">              </span>listData.add("http://play.api.pptv.com/web-m3u8-300161.m3u8?type=m3u8.web.pad");  
  15.                 try {  
  16.                     udpSocket = new DatagramSocket(43708);  
  17.                     dataPacket = new DatagramPacket(buffer, 40);  
  18.                     dataPacket.setPort(43708);     
  19.                     InetAddress broadcastAddr;  
  20.                     broadcastAddr = InetAddress.getByName("255.255.255.255");  
  21.                     dataPacket.setAddress(broadcastAddr);  
  22.                 } catch (Exception e) {  
  23.                 }  
  24.                 while (i < 30) {  
  25.                     i++;  
  26.                     try {  
  27.                         byte[] data = (listData.get(i%2)).getBytes();  
  28.                         dataPacket.setData( data );  
  29.                         dataPacket.setLength( data.length );  
  30.                         udpSocket.send(dataPacket);  
  31.                         Thread.sleep(20000);  
  32.                     } catch (Exception e) {  
  33.                         e.printStackTrace();  
  34.                     }  
  35.                 }  
  36.                 udpSocket.close();  
  37.             }  
  38.         }).start();  
  39.     }  
  40. }  

、Service启动Server的线程

线程是不可控的,如果Activity突然的挂掉,那么这个线程还是在后台运行的,所以我们要把Server放在Service里面,通过Service来启动服务端,代码如下:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.jwzhangjie.smart_tv.server;  
  2.   
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5.   
  6. import com.jwzhangjie.smart_tv.interfaces.UdpServerCallBackListener;  
  7.   
  8. import android.app.Service;  
  9. import android.content.Intent;  
  10. import android.os.Binder;  
  11. import android.os.IBinder;  
  12. import android.util.Log;  
  13.   
  14. public class CommandServer extends Service{  
  15.   
  16.     private static String TAG = CommandServer.class.getName();  
  17.     public static boolean isStop = false;  
  18.     private DatagramSocket udpSocket = null;  
  19.     private Thread udpServerThread;  
  20.     private String Url;  
  21.     /** 
  22.      * 设置视频连接的回调接口 
  23.      */  
  24.     private UdpServerCallBackListener onUdpServerCallBackListener;  
  25.       
  26.     @Override  
  27.     public IBinder onBind(Intent intent) {  
  28.         Log.e(TAG, "onBind");  
  29.         startListener();  
  30.         return new LocalBinder();  
  31.     }  
  32.     /** 
  33.      * 注册回调接口的方法,供外部调用 
  34.      * @param onUdpServerCallBackListener 
  35.      */  
  36.     public void setOnUdpServerCallBackListener(UdpServerCallBackListener onUdpServerCallBackListener){  
  37.         this.onUdpServerCallBackListener = onUdpServerCallBackListener;  
  38.     }  
  39.     @Override  
  40.     public void onCreate() {  
  41.         Log.e(TAG, "onCreate");  
  42.         super.onCreate();  
  43.     }  
  44.   
  45.     @Override  
  46.     public int onStartCommand(Intent intent, int flags, int startId) {  
  47.         Log.e(TAG, "onStartCommand");  
  48.         startListener();  
  49.         return super.onStartCommand(intent, flags, startId);  
  50.     }  
  51.   
  52.     @Override  
  53.     public void onDestroy() {  
  54.         Log.e(TAG, "onDestroy");  
  55.         isStop = true;  
  56.         udpSocket.disconnect();  
  57.         udpSocket.close();  
  58.         udpServerThread.interrupt();  
  59.         udpServerThread = null;  
  60.         super.onDestroy();  
  61.     }  
  62.   
  63.     @Override  
  64.     public boolean onUnbind(Intent intent) {  
  65.         Log.e(TAG, "onUnbind");  
  66.         return super.onUnbind(intent);  
  67.     }  
  68.   
  69.     /** 
  70.      * 开始监听广播 
  71.      */  
  72.     private void startListener(){  
  73.         if (udpServerThread == null) {  
  74.             Log.e(TAG, "run");  
  75.             udpServerThread = new Thread(new UdpServerRunable());  
  76.             udpServerThread.start();  
  77.         }  
  78.     }  
  79.     /** 
  80.      * 实现后台监听广播 
  81.      * @author pig_video 
  82.      */  
  83.     private class UdpServerRunable implements  Runnable {  
  84.         @Override  
  85.         public void run() {  
  86.             byte[] data = new byte[256];  
  87.             DatagramPacket udpPacket = new DatagramPacket(data, 256);  
  88.             try {  
  89.                 udpSocket = new DatagramSocket(43708);  
  90.             } catch (Exception e) {  
  91.                 e.printStackTrace();  
  92.             }  
  93.             while (!isStop) {  
  94.                 try {  
  95.                     udpSocket.receive(udpPacket);  
  96.                     if (udpPacket.getLength() != 0) {  
  97.                         Url = new String(data, 0, udpPacket.getLength());  
  98.                         Log.e(TAG, Url);  
  99.                         if (onUdpServerCallBackListener != null) {  
  100.                             onUdpServerCallBackListener.onPlayUrl(Url);  
  101.                         }  
  102.                     }  
  103.                 } catch (Exception e) {  
  104.                 }  
  105.             }  
  106.         }  
  107.     };  
  108.   
  109.     //定义内部类继承Binder  
  110.     public class LocalBinder extends Binder{  
  111.         //返回本地服务  
  112.         public CommandServer getService(){  
  113.             return CommandServer.this;  
  114.         }  
  115.     }  
  116. }  

3.Activity与Service的绑定

我这里启动Service的方式是通过Activity的onBind来启动的,当Activity关闭的时候,也将Service关闭同时关闭Server的线程,当然常驻后台也行,不过用户可能不太喜欢,毕竟需要资源,播放器我选用的是免费的Vitamio主要是他们把上层应用的代码也提供出来了,非常省事。

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.jwzhangjie.smart_tv.player;  
  2.   
  3. import io.vov.vitamio.LibsChecker;  
  4. import io.vov.vitamio.MediaPlayer;  
  5. import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;  
  6. import io.vov.vitamio.MediaPlayer.OnErrorListener;  
  7. import io.vov.vitamio.MediaPlayer.OnInfoListener;  
  8. import io.vov.vitamio.MediaPlayer.OnTimedTextListener;  
  9. import io.vov.vitamio.widget.MediaController;  
  10. import io.vov.vitamio.widget.VideoView;  
  11.   
  12. import com.jwzhangjie.smart_tv.R;  
  13. import com.jwzhangjie.smart_tv.dialog.JWDialogLoading;  
  14. import com.jwzhangjie.smart_tv.interfaces.UdpServerCallBackListener;  
  15. import com.jwzhangjie.smart_tv.server.CommandServer;  
  16.   
  17. import android.os.Bundle;  
  18. import android.os.IBinder;  
  19. import android.util.Log;  
  20. import android.view.View;  
  21. import android.widget.TextView;  
  22. import android.widget.Toast;  
  23. import android.app.Activity;  
  24. import android.content.ComponentName;  
  25. import android.content.Context;  
  26. import android.content.Intent;  
  27. import android.content.ServiceConnection;  
  28.   
  29. public class SmartTV_Server extends Activity implements OnInfoListener, OnBufferingUpdateListener{  
  30.       
  31.     private static final String TAG = SmartTV_Server.class.getName();  
  32.     private String path = "http://live.gslb.letv.com/gslb?stream_id=guangdong&tag=live&ext=m3u8";  
  33.     private String subtitle_path = "";  
  34.     private VideoView mVideoView;  
  35.     private TextView mSubtitleView;  
  36.     private long mPosition = 0;  
  37.     private int mVideoLayout = 0;  
  38.     private JWDialogLoading mDialogLoading;  
  39.     private CommandServer pigBackServices;  
  40.     private boolean isFirst = true;  
  41.     @Override  
  42.     protected void onCreate(Bundle savedInstanceState) {  
  43.         super.onCreate(savedInstanceState);  
  44.         if (!LibsChecker.checkVitamioLibs(this))  
  45.             return;  
  46.         setContentView(R.layout.subtitle2);  
  47.         mDialogLoading = new JWDialogLoading(this, R.style.dialog);  
  48.         //绑定后台接收视频连接的Service  
  49.         Intent intent = new Intent(SmartTV_Server.this, CommandServer.class);  
  50.         bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  51.         mVideoView = (VideoView) findViewById(R.id.surface_view);  
  52.         mSubtitleView = (TextView) findViewById(R.id.subtitle_view);  
  53.           
  54.         if (path == "") {  
  55.             // Tell the user to provide a media file URL/path.  
  56.             Toast.makeText(SmartTV_Server.this"Please select video, and set path" + " variable to your media file URL/path", Toast.LENGTH_LONG).show();  
  57.             return;  
  58.         } else {  
  59.             /* 
  60.              * Alternatively,for streaming media you can use 
  61.              * mVideoView.setVideoURI(Uri.parse(URLstring)); 
  62.              */  
  63.             isFirst = false;  
  64.             mVideoView.setVideoPath(path);  
  65.   
  66.             mVideoView.setMediaController(new MediaController(this));  
  67.             mVideoView.requestFocus();  
  68.             mVideoView.setOnErrorListener(new OnErrorListener() {  
  69.                 @Override  
  70.                 public boolean onError(MediaPlayer mp, int what, int extra) {  
  71.                     return true;  
  72.                 }  
  73.             });  
  74.             mVideoView.setOnInfoListener(this);  
  75.             mVideoView.setOnBufferingUpdateListener(this);  
  76.             mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {  
  77.                 @Override  
  78.                 public void onPrepared(MediaPlayer mediaPlayer) {  
  79.                     // optional need Vitamio 4.0  
  80.                     mediaPlayer.setPlaybackSpeed(1.0f);  
  81.                     mVideoView.addTimedTextSource(subtitle_path);  
  82.                     mVideoView.setTimedTextShown(true);  
  83.                 }  
  84.             });  
  85.             mVideoView.setOnTimedTextListener(new OnTimedTextListener() {  
  86.                 @Override  
  87.                 public void onTimedText(String text) {  
  88.                     mSubtitleView.setText(text);  
  89.                 }  
  90.                 @Override  
  91.                 public void onTimedTextUpdate(byte[] pixels, int width, int height) {  
  92.                 }  
  93.             });  
  94.         }  
  95.     }  
  96.     @Override  
  97.     protected void onPause() {  
  98.         mPosition = mVideoView.getCurrentPosition();  
  99.         mVideoView.stopPlayback();  
  100.         super.onPause();  
  101.     }  
  102.   
  103.     @Override  
  104.     protected void onResume() {  
  105.         if (mPosition > 0) {  
  106.             mVideoView.seekTo(mPosition);  
  107.             mPosition = 0;  
  108.         }  
  109.         super.onResume();  
  110.         mVideoView.start();  
  111.     }  
  112.   
  113.     public void changeLayout(View view) {  
  114.         mVideoLayout++;  
  115.         if (mVideoLayout == 4) {  
  116.             mVideoLayout = 0;  
  117.         }  
  118.         switch (mVideoLayout) {  
  119.         case 0:  
  120.             mVideoLayout = VideoView.VIDEO_LAYOUT_ORIGIN;  
  121.             view.setBackgroundResource(R.drawable.mediacontroller_sreen_size_100);  
  122.             break;  
  123.         case 1:  
  124.             mVideoLayout = VideoView.VIDEO_LAYOUT_SCALE;  
  125.             view.setBackgroundResource(R.drawable.mediacontroller_screen_fit);  
  126.             break;  
  127.         case 2:  
  128.             mVideoLayout = VideoView.VIDEO_LAYOUT_STRETCH;  
  129.             view.setBackgroundResource(R.drawable.mediacontroller_screen_size);  
  130.             break;  
  131.         case 3:  
  132.             mVideoLayout = VideoView.VIDEO_LAYOUT_ZOOM;  
  133.             view.setBackgroundResource(R.drawable.mediacontroller_sreen_size_crop);  
  134.             break;  
  135.         }  
  136.         mVideoView.setVideoLayout(mVideoLayout, 0);  
  137.     }  
  138.     ServiceConnection conn = new ServiceConnection() {  
  139.         @Override  
  140.         public void onServiceDisconnected(ComponentName name) {  
  141.             pigBackServices = null;  
  142.         }  
  143.           
  144.         @Override  
  145.         public void onServiceConnected(ComponentName name, IBinder service) {  
  146.             pigBackServices = ((CommandServer.LocalBinder)service).getService();  
  147.             pigBackServices.setOnUdpServerCallBackListener(new UdpServerCallBackListener() {  
  148.                 @Override  
  149.                 public void onPlayUrl(String url) {  
  150.                     path = url;  
  151.                     if (isFirst) {  
  152.                         isFirst = false;  
  153.                         mVideoView.setVideoPath(url);  
  154.   
  155.                         mVideoView.setMediaController(new MediaController(SmartTV_Server.this));  
  156.                         mVideoView.requestFocus();  
  157.   
  158.                         mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {  
  159.                             @Override  
  160.                             public void onPrepared(MediaPlayer mediaPlayer) {  
  161.                                 // optional need Vitamio 4.0  
  162.                                 mediaPlayer.setPlaybackSpeed(1.0f);  
  163.                                 mVideoView.addTimedTextSource(subtitle_path);  
  164.                                 mVideoView.setTimedTextShown(true);  
  165.                             }  
  166.                         });  
  167.                         mVideoView.setOnTimedTextListener(new OnTimedTextListener() {  
  168.                             @Override  
  169.                             public void onTimedText(String text) {  
  170.                                 mSubtitleView.setText(text);  
  171.                             }  
  172.                             @Override  
  173.                             public void onTimedTextUpdate(byte[] pixels, int width, int height) {  
  174.                             }  
  175.                         });  
  176.                     }else{  
  177.                         if (mVideoView.isPlaying()) {  
  178.                             mVideoView.stopPlayback();  
  179.                         }  
  180.                         mVideoView.setVideoPath(url);  
  181.                     }  
  182.                     Log.e(TAG, url);  
  183.                 }  
  184.             });  
  185.         }  
  186.     };  
  187.       
  188.     @Override  
  189.     protected void onDestroy() {  
  190.         if (pigBackServices != null) {  
  191.             unbindService(conn);  
  192.         }  
  193.         super.onDestroy();  
  194.     }  
  195.     @Override  
  196.     public void onBufferingUpdate(MediaPlayer mp, int percent) {  
  197.         mDialogLoading.setProgreess(percent);  
  198.     }  
  199.     private boolean isStart;  
  200.     @Override  
  201.     public boolean onInfo(MediaPlayer mp, int what, int extra) {  
  202.         switch (what) {  
  203.         case MediaPlayer.MEDIA_INFO_BUFFERING_START:  
  204.             if (mVideoView.isPlaying()) {  
  205.                 mVideoView.pause();  
  206.                 isStart = true;  
  207.                 if (!mDialogLoading.isShowing()) {  
  208.                     mDialogLoading.show();  
  209.                 }  
  210.             }  
  211.             break;  
  212.         case MediaPlayer.MEDIA_INFO_BUFFERING_END:  
  213.             if (isStart) {  
  214.                 mVideoView.start();  
  215.                 if (mDialogLoading.isShowing()) {  
  216.                     mDialogLoading.dismiss();  
  217.                 }  
  218.             }  
  219.             break;  
  220.         case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:  
  221.             mDialogLoading.setValue("" + extra + "kb/s" + "  ");  
  222.             break;  
  223.         }  
  224.         return true;  
  225.     }  
  226.       
  227. }  

4.加载进度

可能你在上面的播放器代码里面会看到JWDialogLoading,这个是一个网上的环形进度框,能够提示视频的记载进度和下载速度,不过你要覆写Vitamio里面的OnInfoListener, OnBufferingUpdateListener这两个接口才行。

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. package com.jwzhangjie.smart_tv.dialog;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.Dialog;  
  5. import android.os.Bundle;  
  6. import android.view.Display;  
  7. import android.view.Window;  
  8. import android.view.WindowManager.LayoutParams;  
  9. import android.widget.LinearLayout;  
  10.   
  11. public class JWDialogLoading extends Dialog{  
  12.   
  13.     private RadialProgressWidget mProgressWidget;  
  14.     private Activity context;  
  15.     public JWDialogLoading(Activity context) {  
  16.         super(context);  
  17.         this.context = context;  
  18.         mProgressWidget = new RadialProgressWidget(context);  
  19.     }  
  20.       
  21.     public JWDialogLoading(Activity context, int style) {  
  22.         super(context, style);  
  23.         this.context = context;  
  24.         mProgressWidget = new RadialProgressWidget(context);  
  25.     }  
  26.   
  27.     @SuppressWarnings("deprecation")  
  28.     @Override  
  29.     protected void onCreate(Bundle savedInstanceState) {  
  30.         super.onCreate(savedInstanceState);  
  31.         LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);  
  32.         setContentView(mProgressWidget, param);  
  33.         mProgressWidget.setSecondaryText("Loading...");  
  34.         mProgressWidget.setTouchEnabled(false);   
  35.         Window window = getWindow();  
  36.         LayoutParams params = window.getAttributes();  
  37.         Display display = context.getWindowManager().getDefaultDisplay();  
  38.         params.height = (int)(display.getWidth()*0.3);  
  39.         params.width = (int)(display.getWidth()*0.3);  
  40.         params.alpha = 1.0f;  
  41.         window.setAttributes(params);  
  42.     }  
  43.       
  44.     public void setProgreess(int value){  
  45.         mProgressWidget.setCurrentValue(value);  
  46.         mProgressWidget.invalidate();  
  47.         if (value == 100) {  
  48.             dismiss();  
  49.         }  
  50.     }  
  51.       
  52.     public void setValue(String content){  
  53.         mProgressWidget.setSecondaryText("Loading...  "+content);  
  54.     }  
  55.   
  56. }  

5.效果图如下:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值