zoukankan      html  css  js  c++  java
  • 智能电视TV开发---客户端和服务器通信

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

    一、UDP实现

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

    /**
    	 * 实现后台监听广播
    	 * @author jwzhangjie
    	 */
    	private class UdpServerRunable implements  Runnable {
    		@Override
    		public void run() {
    			byte[] data = new byte[256];
    			DatagramPacket udpPacket = new DatagramPacket(data, 256);
    			try {
    				udpSocket = new DatagramSocket(43708);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			while (!isStop) {
    				try {
    					udpSocket.receive(udpPacket);
    					if (udpPacket.getLength() != 0) {
    						Url = new String(data, 0, udpPacket.getLength());
    						Log.e(TAG, Url);
    						if (onUdpServerCallBackListener != null) {
    							onUdpServerCallBackListener.onPlayUrl(Url);
    						}
    					}
    				} catch (Exception e) {
    				}
    			}
    		}
    	};

    为了测试方便我先阶段是Client放在PC端来实现的,为了实现循环测试,我也是把客户端放在一个线程里面,代码如下:

    public class Test_UDP_Client{
    	
    	public static void main(String[] args){
    		new Thread(new Runnable() {
    			int i = 0;
    			private byte[] buffer = new byte[40];
    			
    			@Override
    			public void run() {
    				DatagramPacket dataPacket = null;
    				DatagramSocket udpSocket = null;
    				List<String> listData = new ArrayList<String>();
    				listData.add("http://live.gslb.letv.com/gslb?stream_id=hunan&tag=live&ext=m3u8&sign=live_tv");
    listData.add("http://play.api.pptv.com/web-m3u8-300161.m3u8?type=m3u8.web.pad");
    				try {
    					udpSocket = new DatagramSocket(43708);
    					dataPacket = new DatagramPacket(buffer, 40);
    				    dataPacket.setPort(43708);   
    				    InetAddress broadcastAddr;
    				    broadcastAddr = InetAddress.getByName("255.255.255.255");
    				    dataPacket.setAddress(broadcastAddr);
    				} catch (Exception e) {
    				}
    				while (i < 30) {
    					i++;
    					try {
    						byte[] data = (listData.get(i%2)).getBytes();
    					    dataPacket.setData( data );
    					    dataPacket.setLength( data.length );
    						udpSocket.send(dataPacket);
    						Thread.sleep(20000);
    					} catch (Exception e) {
    						e.printStackTrace();
    					}
    				}
    				udpSocket.close();
    			}
    		}).start();
    	}
    }

    、Service启动Server的线程

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

    package com.jwzhangjie.smart_tv.server;
    
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    import com.jwzhangjie.smart_tv.interfaces.UdpServerCallBackListener;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.util.Log;
    
    public class CommandServer extends Service{
    
    	private static String TAG = CommandServer.class.getName();
    	public static boolean isStop = false;
    	private DatagramSocket udpSocket = null;
    	private Thread udpServerThread;
    	private String Url;
    	/**
    	 * 设置视频连接的回调接口
    	 */
    	private UdpServerCallBackListener onUdpServerCallBackListener;
    	
    	@Override
    	public IBinder onBind(Intent intent) {
    		Log.e(TAG, "onBind");
    		startListener();
    		return new LocalBinder();
    	}
    	/**
    	 * 注册回调接口的方法,供外部调用
    	 * @param onUdpServerCallBackListener
    	 */
    	public void setOnUdpServerCallBackListener(UdpServerCallBackListener onUdpServerCallBackListener){
    		this.onUdpServerCallBackListener = onUdpServerCallBackListener;
    	}
    	@Override
    	public void onCreate() {
    		Log.e(TAG, "onCreate");
    		super.onCreate();
    	}
    
    	@Override
    	public int onStartCommand(Intent intent, int flags, int startId) {
    		Log.e(TAG, "onStartCommand");
    		startListener();
    		return super.onStartCommand(intent, flags, startId);
    	}
    
    	@Override
    	public void onDestroy() {
    		Log.e(TAG, "onDestroy");
    		isStop = true;
    		udpSocket.disconnect();
    		udpSocket.close();
    		udpServerThread.interrupt();
    		udpServerThread = null;
    		super.onDestroy();
    	}
    
    	@Override
    	public boolean onUnbind(Intent intent) {
    		Log.e(TAG, "onUnbind");
    		return super.onUnbind(intent);
    	}
    
    	/**
    	 * 开始监听广播
    	 */
    	private void startListener(){
    		if (udpServerThread == null) {
    			Log.e(TAG, "run");
    			udpServerThread = new Thread(new UdpServerRunable());
    			udpServerThread.start();
    		}
    	}
    	/**
    	 * 实现后台监听广播
    	 * @author pig_video
    	 */
    	private class UdpServerRunable implements  Runnable {
    		@Override
    		public void run() {
    			byte[] data = new byte[256];
    			DatagramPacket udpPacket = new DatagramPacket(data, 256);
    			try {
    				udpSocket = new DatagramSocket(43708);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			while (!isStop) {
    				try {
    					udpSocket.receive(udpPacket);
    					if (udpPacket.getLength() != 0) {
    						Url = new String(data, 0, udpPacket.getLength());
    						Log.e(TAG, Url);
    						if (onUdpServerCallBackListener != null) {
    							onUdpServerCallBackListener.onPlayUrl(Url);
    						}
    					}
    				} catch (Exception e) {
    				}
    			}
    		}
    	};
    
    	//定义内部类继承Binder
    	public class LocalBinder extends Binder{
    		//返回本地服务
    		public CommandServer getService(){
    			return CommandServer.this;
    		}
    	}
    }

    3.Activity与Service的绑定

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

    package com.jwzhangjie.smart_tv.player;
    
    import io.vov.vitamio.LibsChecker;
    import io.vov.vitamio.MediaPlayer;
    import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;
    import io.vov.vitamio.MediaPlayer.OnErrorListener;
    import io.vov.vitamio.MediaPlayer.OnInfoListener;
    import io.vov.vitamio.MediaPlayer.OnTimedTextListener;
    import io.vov.vitamio.widget.MediaController;
    import io.vov.vitamio.widget.VideoView;
    
    import com.jwzhangjie.smart_tv.R;
    import com.jwzhangjie.smart_tv.dialog.JWDialogLoading;
    import com.jwzhangjie.smart_tv.interfaces.UdpServerCallBackListener;
    import com.jwzhangjie.smart_tv.server.CommandServer;
    
    import android.os.Bundle;
    import android.os.IBinder;
    import android.util.Log;
    import android.view.View;
    import android.widget.TextView;
    import android.widget.Toast;
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    
    public class SmartTV_Server extends Activity implements OnInfoListener, OnBufferingUpdateListener{
    	
    	private static final String TAG = SmartTV_Server.class.getName();
    	private String path = "http://live.gslb.letv.com/gslb?stream_id=guangdong&tag=live&ext=m3u8";
    	private String subtitle_path = "";
    	private VideoView mVideoView;
    	private TextView mSubtitleView;
    	private long mPosition = 0;
    	private int mVideoLayout = 0;
    	private JWDialogLoading mDialogLoading;
    	private CommandServer pigBackServices;
    	private boolean isFirst = true;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		if (!LibsChecker.checkVitamioLibs(this))
    			return;
    		setContentView(R.layout.subtitle2);
    		mDialogLoading = new JWDialogLoading(this, R.style.dialog);
    		//绑定后台接收视频连接的Service
    		Intent intent = new Intent(SmartTV_Server.this, CommandServer.class);
    		bindService(intent, conn, Context.BIND_AUTO_CREATE);
    		mVideoView = (VideoView) findViewById(R.id.surface_view);
    		mSubtitleView = (TextView) findViewById(R.id.subtitle_view);
    		
    		if (path == "") {
    			// Tell the user to provide a media file URL/path.
    			Toast.makeText(SmartTV_Server.this, "Please select video, and set path" + " variable to your media file URL/path", Toast.LENGTH_LONG).show();
    			return;
    		} else {
    			/*
    			 * Alternatively,for streaming media you can use
    			 * mVideoView.setVideoURI(Uri.parse(URLstring));
    			 */
    			isFirst = false;
    			mVideoView.setVideoPath(path);
    
    			mVideoView.setMediaController(new MediaController(this));
    			mVideoView.requestFocus();
    			mVideoView.setOnErrorListener(new OnErrorListener() {
    				@Override
    				public boolean onError(MediaPlayer mp, int what, int extra) {
    					return true;
    				}
    			});
    			mVideoView.setOnInfoListener(this);
    			mVideoView.setOnBufferingUpdateListener(this);
    			mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    				@Override
    				public void onPrepared(MediaPlayer mediaPlayer) {
    					// optional need Vitamio 4.0
    					mediaPlayer.setPlaybackSpeed(1.0f);
    					mVideoView.addTimedTextSource(subtitle_path);
    					mVideoView.setTimedTextShown(true);
    				}
    			});
    			mVideoView.setOnTimedTextListener(new OnTimedTextListener() {
    				@Override
    				public void onTimedText(String text) {
    					mSubtitleView.setText(text);
    				}
    				@Override
    				public void onTimedTextUpdate(byte[] pixels, int width, int height) {
    				}
    			});
    		}
    	}
    	@Override
    	protected void onPause() {
    		mPosition = mVideoView.getCurrentPosition();
    		mVideoView.stopPlayback();
    		super.onPause();
    	}
    
    	@Override
    	protected void onResume() {
    		if (mPosition > 0) {
    			mVideoView.seekTo(mPosition);
    			mPosition = 0;
    		}
    		super.onResume();
    		mVideoView.start();
    	}
    
    	public void changeLayout(View view) {
    		mVideoLayout++;
    		if (mVideoLayout == 4) {
    			mVideoLayout = 0;
    		}
    		switch (mVideoLayout) {
    		case 0:
    			mVideoLayout = VideoView.VIDEO_LAYOUT_ORIGIN;
    			view.setBackgroundResource(R.drawable.mediacontroller_sreen_size_100);
    			break;
    		case 1:
    			mVideoLayout = VideoView.VIDEO_LAYOUT_SCALE;
    			view.setBackgroundResource(R.drawable.mediacontroller_screen_fit);
    			break;
    		case 2:
    			mVideoLayout = VideoView.VIDEO_LAYOUT_STRETCH;
    			view.setBackgroundResource(R.drawable.mediacontroller_screen_size);
    			break;
    		case 3:
    			mVideoLayout = VideoView.VIDEO_LAYOUT_ZOOM;
    			view.setBackgroundResource(R.drawable.mediacontroller_sreen_size_crop);
    			break;
    		}
    		mVideoView.setVideoLayout(mVideoLayout, 0);
    	}
    	ServiceConnection conn = new ServiceConnection() {
    		@Override
    		public void onServiceDisconnected(ComponentName name) {
    			pigBackServices = null;
    		}
    		
    		@Override
    		public void onServiceConnected(ComponentName name, IBinder service) {
    			pigBackServices = ((CommandServer.LocalBinder)service).getService();
    			pigBackServices.setOnUdpServerCallBackListener(new UdpServerCallBackListener() {
    				@Override
    				public void onPlayUrl(String url) {
    					path = url;
    					if (isFirst) {
    						isFirst = false;
    						mVideoView.setVideoPath(url);
    
    						mVideoView.setMediaController(new MediaController(SmartTV_Server.this));
    						mVideoView.requestFocus();
    
    						mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    							@Override
    							public void onPrepared(MediaPlayer mediaPlayer) {
    								// optional need Vitamio 4.0
    								mediaPlayer.setPlaybackSpeed(1.0f);
    								mVideoView.addTimedTextSource(subtitle_path);
    								mVideoView.setTimedTextShown(true);
    							}
    						});
    						mVideoView.setOnTimedTextListener(new OnTimedTextListener() {
    							@Override
    							public void onTimedText(String text) {
    								mSubtitleView.setText(text);
    							}
    							@Override
    							public void onTimedTextUpdate(byte[] pixels, int width, int height) {
    							}
    						});
    					}else{
    						if (mVideoView.isPlaying()) {
    							mVideoView.stopPlayback();
    						}
    						mVideoView.setVideoPath(url);
    					}
    					Log.e(TAG, url);
    				}
    			});
    		}
    	};
    	
    	@Override
    	protected void onDestroy() {
    		if (pigBackServices != null) {
    			unbindService(conn);
    		}
    		super.onDestroy();
    	}
    	@Override
    	public void onBufferingUpdate(MediaPlayer mp, int percent) {
    		mDialogLoading.setProgreess(percent);
    	}
    	private boolean isStart;
    	@Override
    	public boolean onInfo(MediaPlayer mp, int what, int extra) {
    		switch (what) {
    		case MediaPlayer.MEDIA_INFO_BUFFERING_START:
    			if (mVideoView.isPlaying()) {
    				mVideoView.pause();
    				isStart = true;
    				if (!mDialogLoading.isShowing()) {
    					mDialogLoading.show();
    				}
    			}
    			break;
    		case MediaPlayer.MEDIA_INFO_BUFFERING_END:
    			if (isStart) {
    				mVideoView.start();
    				if (mDialogLoading.isShowing()) {
    					mDialogLoading.dismiss();
    				}
    			}
    			break;
    		case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:
    			mDialogLoading.setValue("" + extra + "kb/s" + "  ");
    			break;
    		}
    		return true;
    	}
    	
    }

    4.加载进度

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

    package com.jwzhangjie.smart_tv.dialog;
    
    import android.app.Activity;
    import android.app.Dialog;
    import android.os.Bundle;
    import android.view.Display;
    import android.view.Window;
    import android.view.WindowManager.LayoutParams;
    import android.widget.LinearLayout;
    
    public class JWDialogLoading extends Dialog{
    
    	private RadialProgressWidget mProgressWidget;
    	private Activity context;
    	public JWDialogLoading(Activity context) {
    		super(context);
    		this.context = context;
    		mProgressWidget = new RadialProgressWidget(context);
    	}
    	
    	public JWDialogLoading(Activity context, int style) {
    		super(context, style);
    		this.context = context;
    		mProgressWidget = new RadialProgressWidget(context);
    	}
    
    	@SuppressWarnings("deprecation")
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    		setContentView(mProgressWidget, param);
    		mProgressWidget.setSecondaryText("Loading...");
    		mProgressWidget.setTouchEnabled(false); 
    		Window window = getWindow();
    		LayoutParams params = window.getAttributes();
    		Display display = context.getWindowManager().getDefaultDisplay();
    		params.height = (int)(display.getWidth()*0.3);
    		params.width = (int)(display.getWidth()*0.3);
    		params.alpha = 1.0f;
    		window.setAttributes(params);
    	}
    	
    	public void setProgreess(int value){
    		mProgressWidget.setCurrentValue(value);
    		mProgressWidget.invalidate();
    		if (value == 100) {
    			dismiss();
    		}
    	}
    	
    	public void setValue(String content){
    		mProgressWidget.setSecondaryText("Loading...  "+content);
    	}
    
    }
    

    5.效果图如下:



  • 相关阅读:
    ZOJ 2601 Warehouse Keeper
    POJ 2175 Evacuation Plan
    NYIST 1108 最低的惩罚
    二进制 与 十进制 互转
    javascript学习(9)——[设计模式]单例
    2013 Changsha Regional 一样的木板一样的气球
    muduo简化(1):Reactor的关键结构
    Oracle 11g New 热补丁
    跳转表实例
    [置顶] android 心跳包的分析
  • 原文地址:https://www.cnblogs.com/fuhaots2009/p/3481557.html
Copyright © 2011-2022 走看看