在做智能电视应用的时候,最头疼的就是焦点问题,特别是对于个人开发者,没有设备这是最最头疼的事情了,在没有设备的情况下,怎么实现智能电视应用呢,接下来我是用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.效果图如下:

智能电视TV开发---客户端和服务器通信的更多相关文章

  1. 智能电视TV开发---直播视频客户端结构设计和实现

    在智能电视TV开发---客户端和服务器通信里面我们实现了客户端和服务端的简单通信,接下来我们做一个简单的客户端界面,来实现手机端来操控智能电视的TV端. 一.存储视频的结构设计 我们在做客户端的时候, ...

  2. 智能电视TV开发---如何实现程序省电

    对于很多使用智能手机的用户来,很多抱怨手机耗电太快,很多人买手机的时候卖家都是推荐买两块电池,还有如果用户留心的话,在买手机的网页上,卖家会显示播放视频多长时间,听音乐多长时间,待机多长时间,不过看的 ...

  3. Socket与SocketServer结合多线程实现多客户端与服务器通信

    需求说明:实现多客户端用户登录,实现多客户端登录一般都需要使用线程技术: (1)创建服务器端线程类,run()方法中实现对一个请求的响应处理: (2)修改服务器端代码,实现循环监听状态: (3)服务器 ...

  4. java学习笔记——Java多客户端与服务器通信

    先说一下大概的思路: 应用多线程来实现服务器与多客户端之间的通信 1.服务器端创建ServerSocket,循环调用accept()等待客户端连接: 2.客户端创建一个Socket并请求与服务器端连接 ...

  5. Java网络编程客户端和服务器通信

    在java网络编程中,客户端和服务器的通信例子: 先来服务器监听的代码 package com.server; import java.io.IOException; import java.io.O ...

  6. H5活动全屏滚动页面在安卓智能电视TV调试

    前段时间公司做一个线上活动,在电视上商品促销.产品的要求是每个商品介绍刚好满一屏,按下遥控器向下键可以整屏切换.这种功能如果实在PC端,实现起来非常容易,引用jQuery插件就能实现.但是在安卓智能电 ...

  7. 客户端(android,ios)与服务器通信

    android,ios客户端与服务器通信为了便于理解,直接用PHP作为服务器端语言 其实就是一个 http请求响应的过程序,先从 B/S模式说起浏览器发起http请求,服务器响应请求,并把数据返回给浏 ...

  8. C#Socket_TCP(客户端,服务器端通信)

    客户端与服务器通信,通过IP(识别主机)+端口号(识别应用程序). IP地址查询方式:Windows+R键,输入cmd,输入ipconfig. 端口号:可自行设定,但通常为4位. 服务器端: usin ...

  9. 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...

随机推荐

  1. ERDAS IMAGINE 9.2安装破解方法

    Install the application. Copy the license.dat and ERDAS.exe to C:\Program Files\Leica Geosystems\Sha ...

  2. CAS SiteMinder (单点登录)

    http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/

  3. hadoop之eclipse环境的配置

    http://hi.baidu.com/kongxianghe123/item/ea352e1040cdeffd86ad4e28

  4. SharePoint2013切换帐户登录菜单

    SharePoint2013帐户姓名显示的地方没有切换帐户的菜单,这个功能对于终端用户是可有可无的,但对于sharepoint管理员或sharepoint开发人员来讲,却是一个很重要的菜单,由于经常要 ...

  5. 学习Android之内部类

    java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又分为:常规内部类.局部内部类.匿名内部类和静态嵌套类四种.我们内部类的知识在Android手机开发中经常用到. 一.常规内部 ...

  6. openSession()和getCureentSession()的区别

    openSession():永远是打开一个新的session getCureentSession():如果当前环境有session,则取得原来已经存在的session,如果没有,则创建一个新的sess ...

  7. autofac 学习记录

    builder.RegisterModule(new ConfigurationSettingsReader()); 需要注册上面一句才能读到.config里的节点,xml配置方式如下 <con ...

  8. iframe控件

    function goTo(url) { document.getElementById("iframeid").src = url; //获得要显示的页面,当点击时就会在ifra ...

  9. bq27441-G1 工作机制

    /*************************************************************************** * bq27441-G1 工作机制 * 声明: ...

  10. Mint Linuxubuntu 字体配置文件

    <?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "fonts.dtd"><fon ...