android + red5 + rtmp
背景:在已有的red5服务器环境下实现android客户端的视频直播
要实现客户端视频直播就先先对服务器端有所了解
Red5流媒体服务器是Adboe的产品,免费并且是开源的,与Flash搭配的时候可谓是天生一对,但使用Java和Android作为客户端调用却可谓一波三折。
Red5是一个采用Java开发开源的Flash流媒体服务器。它支持:把音频(MP3)和视频(FLV)转换成播放流; 录制客户端播放流(只支持FLV);共享对象;现场直播流发布;远程调用。Red5使用RSTP作为流媒体传输协议,在其自带的一些示例中演示了在线录制,flash流媒体播放,在线聊天,视频会议等一些基本功能。
服务器的相关配置
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>/FlashMediaGateway</param-value>
</context-param> [root@lapaccweb WEB-INF]# more red5-web.properties
webapp.contextPath=/FlashMediaGateway
webapp.virtualHosts=*:1935,*:443
recordPath=/opt/Avaya/AccWeb/data/SessionRecording/
playbackPath=/opt/Avaya/AccWeb/data/SessionRecording/
absolutePath=true
Adobe的Red5源代码里有一个RTMPClient的类,这个类在使用上其实不复杂,但却没办法成功调用。观察日志,发现是连接成功后在开始创建流的时候,服务端把连接断开了。我能想到的解释就是可能公司现在所使用的Red5服务器的版本与这个RTMPClient不兼容。
国外还有一个收费的RTMPClient,价值是395刀。具体网址和产品的名称我就不指出了,有心人肯定会找得到。这个客户端类库很强大,使用也很方便,我注册了一个试用的key,发现能和Red5服务器成功连接并且通讯良好。(国内有破解版)
之前用vlc和Vitamio尝试过,结果播放不了直播流,可能是我太笨了吧.
有可能的原因如下:
http://blog.csdn.net/qiuchangyong/article/details/18862247
利用LocalServerSocket采集视频数据的实时流数据,android自带的LocalServerSocket是和java 的serverSocket是不同的,因为LocalServerSocket的客户端和服务端都是必须在本机,所以我们可以在调用RecodMedia录制视频的时候建立客户端LocalSocket来向服务端发送数据,在服务端接收数据调用juv—rtmp—client将接收的数据打包成RTMP协议数据向流媒体服务器发送数据。
利用android自带的Camera来录制数据,这里主要用到了照相机的CallBack回调接口,在这个回调接口里面可以实时的接收照相机录制的视频数据,这里面录制的视频数据是YUV420SP格式的数据流,这是最原始的数据流,是无法显示的服务器界面的。将数据流转化成YUV420SP2RGB格式的数据,然后利用juv—rtmp—client向流媒体服务器发送数据。
juv—rtmp—client这个jar包封装了很多和流媒体服务器交互的方法,当然有视频数据传输格式。在流媒体服务提供了三种格式的传输方式:Record、Live、Append。
利用juv—rtmp—client有几个主要的类
其中最重要的几个类是NetConnection,NetStream, License,其中NetConnection,NetStream这两个类是负责创建连接和回调服务端的数据。而License则顾名思义是负责验证有没有授权。由于按照官方给出的使用说明,在使用前必须调用License.setKey()方法传入注册所得到的key。
CameraVideoActivity
private void init() {
aCamera = new AndroidCamera(CameraVideoActivity.this);
hour = (TextView) findViewById(R.id.mediarecorder_TextView01);
minute = (TextView) findViewById(R.id.mediarecorder_TextView03);
second = (TextView) findViewById(R.id.mediarecorder_TextView05);
mStart = (Button) findViewById(R.id.mediarecorder_VideoStartBtn);
mStop = (Button) findViewById(R.id.mediarecorder_VideoStopBtn);
mReturn = (Button) findViewById(R.id.mediarecorder_VideoReturnBtn);
// 开始录像
mStart.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (streaming == false) {
aCamera.start();
}
aCamera.startVideo();
isTiming = true;
handler.postDelayed(task, 1000);
// 设置按钮状态
mStart.setEnabled(false);
mReturn.setEnabled(false);
mStop.setEnabled(true);
}
});
mReturn.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (RTMPConnectionUtil.netStream != null) {
RTMPConnectionUtil.netStream.close();
RTMPConnectionUtil.netStream = null;
}
if (aCamera != null) {
aCamera = null; }
finish();
}
});
mStop.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
aCamera.stop();
// 设置按钮状态
mStart.setEnabled(true);
mReturn.setEnabled(true);
mStop.setEnabled(false);
isTiming = false;
}
});
} public class AndroidCamera extends AbstractCamera implements
SurfaceHolder.Callback, Camera.PreviewCallback { private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Camera camera; private int width;
private int height; private boolean init; int blockWidth;
int blockHeight;
int timeBetweenFrames; // 1000 / frameRate
int frameCounter;
byte[] previous; public AndroidCamera(Context context) { surfaceView = (SurfaceView) findViewById(R.id.mediarecorder_Surfaceview);
surfaceView.setVisibility(View.VISIBLE);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(AndroidCamera.this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); width = 352 / 2;
height = 288 / 2; init = false;
Log.d("DEBUG", "AndroidCamera()");
} private void startVideo() {
Log.d("DEBUG", "startVideo()"); RTMPConnectionUtil.ConnectRed5(CameraVideoActivity.this);
RTMPConnectionUtil.netStream = new UltraNetStream(
RTMPConnectionUtil.connection);
RTMPConnectionUtil.netStream
.addEventListener(new NetStream.ListenerAdapter() { @Override
public void onNetStatus(final INetStream source,
final Map<String, Object> info) {
Log.d("DEBUG", "Publisher#NetStream#onNetStatus: "
+ info); final Object code = info.get("code"); if (NetStream.PUBLISH_START.equals(code)) {
if (CameraVideoActivity.aCamera != null) {
RTMPConnectionUtil.netStream
.attachCamera(aCamera, -1 /* snapshotMilliseconds */);
Log.d("DEBUG", "aCamera.start()");
aCamera.start();
} else {
Log.d("DEBUG", "camera == null");
}
}
}
}); RTMPConnectionUtil.netStream.publish("anystringdata", NetStream.LIVE);
} public void start() {
camera.startPreview();
streaming = true;
} public void stop() {
camera.stopPreview();
streaming = false;
} public void printHexString(byte[] b) {
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
} }
} @Override
public void onPreviewFrame(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub
// if (!active) return;
if (!init) {
blockWidth = 32;
blockHeight = 32;
timeBetweenFrames = 100; // 1000 / frameRate
frameCounter = 0;
previous = null;
init = true;
}
final long ctime = System.currentTimeMillis(); /** 将采集的YUV420SP数据转换为RGB格式 */
byte[] current = RemoteUtil.decodeYUV420SP2RGB(arg0, width, height);
try {
// final byte[] packet = RemoteUtil.encode(current, previous,
blockWidth, blockHeight, width, height);
fireOnVideoData(new MediaDataByteArray(timeBetweenFrames,
new ByteArray(packet)));
previous = current;
if (++frameCounter % 10 == 0)
previous = null; } catch (Exception e) {
e.printStackTrace();
}
final int spent = (int) (System.currentTimeMillis() - ctime);
try { Thread.sleep(Math.max(0, timeBetweenFrames - spent));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
// camera.startPreview();
// camera.unlock(); // Log.d("DEBUG", "surfaceChanged()"); // startVideo();
} @Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
camera = Camera.open();
try {
camera.setPreviewDisplay(surfaceHolder);
camera.setPreviewCallback(this);
Camera.Parameters params = camera.getParameters();
params.setPreviewSize(width, height);
camera.setParameters(params);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
camera.release();
camera = null;
} Log.d("DEBUG", "surfaceCreated()");
} @Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
} } } /* 定时器设置,实现计时 */
private Handler handler = new Handler();
int s, h, m, s1, m1, h1;
private Runnable task = new Runnable() {
public void run() {
if (isTiming) {
handler.postDelayed(this, 1000);
s++;
if (s < 60) {
second.setText(format(s));
} else if (s < 3600) {
m = s / 60;
s1 = s % 60;
minute.setText(format(m));
second.setText(format(s1));
} else {
h = s / 3600;
m1 = (s % 3600) / 60;
s1 = (s % 3600) % 60;
hour.setText(format(h));
minute.setText(format(m1));
second.setText(format(s1));
}
}
}
};
RTMPConnectionUtil
public static void ConnectRed5(Context context) {
// License.setKey("63140-D023C-D7420-00B15-91FC7");
connection = new UltraNetConnection();
connection.configuration().put(UltraNetConnection.Configuration.INACTIVITY_TIMEOUT, -1);
connection.configuration().put(UltraNetConnection.Configuration.RECEIVE_BUFFER_SIZE, 256 * 1024);
connection.configuration().put(UltraNetConnection.Configuration.SEND_BUFFER_SIZE, 256 * 1024); //connection.client(new ClientHandler(context));
connection.addEventListener(new NetConnectionListener());
//Log.d("DEBUG", User.id + " - " + User.phone);
connection.connect(red5_url);
}
附带一款测试Red5用rtmp是否能够连接工具
博客地址:http://qiaoyihang.iteye.com/
android + red5 + rtmp的更多相关文章
- Android流媒体开发之路二:NDK开发Android端RTMP直播推流程序
NDK开发Android端RTMP直播推流程序 经过一番折腾,成功把RTMP直播推流代码,通过NDK交叉编译的方式,移植到了Android下,从而实现了Android端采集摄像头和麦克缝数据,然后进行 ...
- Android平台RTMP/RTSP播放器开发系列--解码和绘制
本文主要抛砖引玉,粗略介绍下Android平台RTMP/RTSP播放器中解码和绘制相关的部分(Github). 解码 提到解码,大家都知道软硬解,甚至一些公司觉得硬解码已经足够通用,慢慢抛弃软解了,如 ...
- Android ffmpeg rtmp(source code)
souce code: Android.mk 编译生成APK需要调用的so文件 LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODU ...
- 可能是目前市面上唯一能够支持全平台的RTMP推流组件:Windows、Linux、Android、iOS、ARM
EasyRTMP是什么? EasyRTMP是一套RTMP直播推送功能组件,内部集成了包括:基本RTMP协议.断线重连.异步推送.环形缓冲区.推送网络拥塞自动丢帧.缓冲区关键帧检索.事件回调(断线.音视 ...
- Android同屏、摄像头RTMP推送常用的数据接口设计探讨
前言 好多开发者在调用Android平台RTMP推送或轻量级RTSP服务接口时,采集到的video数据类型多样化,如420sp.I420.yv12.nv21.rgb的,还有的拿到的图像是倒置的,如果开 ...
- Android平台摄像头/屏幕/外部数据采集及RTMP推送接口设计描述
好多开发者提到,为什么大牛直播SDK的Android平台RTMP推送接口怎么这么多?不像一些开源或者商业RTMP推送一样,就几个接口,简单明了. 不解释,以Android平台RTMP推送模块常用接口, ...
- Android IOS WebRTC 音视频开发总结(三三)-- Periscope介绍
本文主要介绍Periscope,文章来自博客园RTC.Blacker,支持原创,转载请说明出处. 可能国内很多人没听说过Periscope,这可是现在Twitter上很火的一个APP,先看看人家自己是 ...
- github上十二款最著名的Android播放器开源项目
1.ijkplayer 项目地址: https://github.com/Bilibili/ijkplayer 介绍:Ijkplayer 是Bilibili发布的基于 FFplay 的轻量级 Andr ...
- EasyPlayer RTSP 安卓Android播放器显示模式设置方法
一般对于一个播放器,应该支持如下几种显示模式: 等比例,最大化区域显示,不裁剪 等比例,最大区域显示,裁剪 拉伸显示,铺满全屏 要实现这几种显示模式,其实只要对播放控件的布局进行些许调整即可.那Eas ...
随机推荐
- iOS 阶段学习第十天笔记(结构体)
iOS学习(C语言)知识点整理 一.数据结构 1)概念:数据结构是指计算机程序中所操作的对象——数据以及数据元素之间的相互关系和运算. 2)结构体必须有struct 关键字修饰. 实例代码: stru ...
- 不需要写代码,文件夹右键cmd定位指定目录
引子 这篇文章其实本来不是这样的,因为我用C#的代码实现了一个程序,后面才突然发现,我太傻太天真了,明明不需要写程序和写代码的,结果自己把自己二住了. 我们来看看效果图. 由于,我自己的原因,这个功能 ...
- NoSQL入门概述
入门概述 1 NoSQL是什么? NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关 ...
- Jconsole远程监控tomcat 的JVM内存(linux、windows)
Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到.它用于连接正在运行的本地或者远程的JVM,对运行在java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界 ...
- LR--Controller的Pacing设置(不容忽视的设置)
运行时的Pacing设置主要影响什么? Pacing主要用来设置重复迭代脚本的间隔时间.共有三种方法: A:上次迭代结束后立刻开始. B:上次迭代结束后等待固定时间. C:按固定或随机的时间间 ...
- GJM :Sql 各种语句 以及函数 [转载]
版权声明:本文原创发表于 [请点击连接前往] ,未经作者同意必须保留此段声明!如有侵权请联系我删帖处理! 1.更改数据库的名称 2.表中有数据的情况下再添加列.删除列 3.在SQLServer 中各种 ...
- sql server和mysql中分别实现分页功能
MySQL 在MySQL中,可以用 Limit 来查询第 m 列到第 n 列的记录, 例如: select * from tablename limit m, n sql="select * ...
- Manifesto – HTML5 离线应用程序缓存校验工具
Manifesto 是一个 HTML5 离线应用程序缓存校验工具,提供了快速校验 HTML5 manifest 文件有效性的方法.离线应用程序缓存在使用中最困难的部分之一就是无法正常工作的时候没有明显 ...
- 学习 Mobile App 网站制作的11个优秀案例
我喜欢收集美丽的,精心设计的移动应用程序网站.在我看来,为 App 提供一个美丽的网站显示了设计者和开发者对它的用户和产品的关心,除了开发应用程序,他们去加倍努力去促进应用和传播关于它的 App. 我 ...
- pywebsocket的搭建
Python可以搭建pywebsocket(Web服务器,python websocket),搭建pywebsocket必须要已经安装了python,点我查看python的下载与安装.在这篇Blog中 ...