android视频开发之一Android 如何使用juv-rtmp-client.jar向Red5服务器发布实时视频数据

本文主要是介绍android视频开发之一Android 如何使用juv-rtmp-client.jar向Red5服务器发布实时视频数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用juv-client-client.jar主要是尽快地完成毕业设计里面手机端向网页端发送实时视频的功能,由于实习和做毕业设计的时间冲突,因此完成毕业设计只花了1个多月时间。

(万恶的形式主义,论文格式改了我老久老久)因此代码上面会存在一些问题,并且也是单纯的实现了摄像头视频的实时传输,麦克风的实时语音没有实现。

自我感觉这个毕业设计没有多大价值,但是有参考意义,特把实现记录一下,用作纪念!

原理:

juv-client-client.jar提供了很多与Red5的交互操作,比如连接,流数据发布,方法互相调用等等。

在发布实时视频数据的之前,我们需要建立手机端和服务器端的RTMP连接。

使用类库里的NetConnection类:

关键代码如下:

[java]  view plain copy
  1. private void connectRed5() {  
  2.           
  3.         //key的值官方网站上可以申请到免费试用版本:http://www.smaxe.com/order.jsf#request_evaluation_key  
  4.         License.setKey("63140-D023C-D7420-00B15-91FC7");  
  5.         connection = new NetConnection();  
  6.           
  7.         //对连接进行配置  
  8.         connection.configuration().put(NetConnection.Configuration.INACTIVITY_TIMEOUT, -1);  
  9.         connection.configuration().put(NetConnection.Configuration.RECEIVE_BUFFER_SIZE, 256 * 1024);  
  10.         connection.configuration().put(NetConnection.Configuration.SEND_BUFFER_SIZE, 256 * 1024);  
  11.           
  12.         connection.client(new ClientHandler());  
  13.         connection.addEventListener(new NetConnectionListener());  
  14.         connection.connect(red5_url);  
  15.     }  
其中new ClientHandler类是继承Object,里面写的方法可以被服务器调用。

new NetConnectionListener可以继承NetConnection.ListenerAdapter或者实现Listener接口,用于显示处理建立RTMP连接时候的一些网络状况。

例如:

[java]  view plain copy
  1. private class ClientHandler extends Object {  
  2.           
  3.         public ClientHandler() {}  
  4.           
  5.         public void fun1() {}  
  6.           
  7.         public void fun2() {}  
  8.     }  
  9.       
  10. private class NetConnectionListener extends NetConnection.ListenerAdapter {  
  11.           
  12.     public NetConnectionListener() {}  
  13.           
  14.     @Override  
  15.     public void onAsyncError(final INetConnection source, final String message, final Exception e) {  
  16.     System.out.println("NetConnection#onAsyncError: " + message + " "+ e);  
  17.     }  
  18.   
  19.     @Override  
  20.     public void onIOError(final INetConnection source, final String message) {  
  21.     System.out.println("NetConnection#onIOError: " + message);  
  22.     }  
  23.   
  24.     @Override  
  25.     public void onNetStatus(final INetConnection source, final Map<String, Object> info) {  
  26.     System.out.println("NetConnection#onNetStatus: " + info);  
  27.     final Object code = info.get("code");  
  28.     if (NetConnection.CONNECT_SUCCESS.equals(code)) {}  
  29.     }  
  30. }  
以上就是建立连接的过程,判断是否建立了连接在
[java]  view plain copy
  1. System.out.println("NetConnection#onNetStatus: " + info);  
是会有消息打出来的。

建立RTMP连接以后我们就可以通过Android的Camera类进行视频的采集,然后进行实时发送。

这里我不得不说的是,实现Android端的视频采集比网页端的复杂,因为这个类库提供的摄像头类和麦克风类都是两个抽象类或者是接口,必须要自己实现它。而网页端却有封装好的摄像头和麦克风,调用简单。

我的方法是实现类库里的AbstractCamera抽象类,想到Android里面自己也提供了一个摄像头的Camera类,于是我想到了用面向对象的组合和多接口实现,于是我打算实现一个AndroidCamera类。

这里有个问题:为什么要实现AbstractCamera类?

因为这个类里面有一个protected的fireOnVideoData方法。可以给继承它的类使用,该方法的作用,我猜想是把一个个数据包封装成流数据。

继续实现AndroidCamera类,用类图表示我的实现方案:


可以看到我用Android里的Camera类、SurfaceView类、SurfaceHolder类组成了我自己的AndroidCamera类,并且需要实现SurfaceHolder.CallBack接口以及Camera的PreviewCallBack接口。

这么做的原因有两个:1、实现预览。2、预览的同时通过Camera的PreviewCallBack接口里的onPreviewFrame方法获取到实时帧数据,进而转码打包生成流数据。(注意我这里并没有进行视频的编码压缩,时间和能力有限)

直接上代码了:

[java]  view plain copy
  1. <pre name="code" class="java">    public class AndroidCamera extends AbstractCamera implements SurfaceHolder.Callback, Camera.PreviewCallback {  
  2.           
  3.         private SurfaceView surfaceView;  
  4.         private SurfaceHolder surfaceHolder;  
  5.         private Camera camera;  
  6.           
  7.         private int width;  
  8.         private int height;  
  9.           
  10.         private boolean init;  
  11.           
  12.         int blockWidth;  
  13.         int blockHeight;  
  14.         int timeBetweenFrames; // 1000 / frameRate  
  15.         int frameCounter;  
  16.         byte[] previous;  
  17.           
  18.         public AndroidCamera(Context context) {  
  19.                  
  20.             surfaceView = (SurfaceView)((Activity) context).findViewById(R.id.surfaceView);  
  21.                 //我是把Activity里的context传进入然后获取到SurfaceView,也可以之间传入SurfaceView进行实例  
  22.                 surfaceHolder = surfaceView.getHolder();  
  23.             surfaceHolder.addCallback(AndroidCamera.this);  
  24.             surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  25.               
  26.             width = 320;  
  27.             height = 240;  
  28.             init = false;  
  29.             Log.d("DEBUG""AndroidCamera()");  
  30.         }  
  31.           
  32.         private void startVideo() {  
  33.             Log.d("DEBUG""startVideo()");  
  34.               
  35.             netStream = new NetStream(connection);  
  36.             netStream.addEventListener(new NetStream.ListenerAdapter() {  
  37.                   
  38.                 @Override  
  39.                 public void onNetStatus(final INetStream source, final Map<String, Object> info){  
  40.                     System.out.println("Publisher#NetStream#onNetStatus: " + info);  
  41.                     Log.d("DEBUG""Publisher#NetStream#onNetStatus: " + info);  
  42.                       
  43.                     final Object code = info.get("code");  
  44.                       
  45.                     if (NetStream.PUBLISH_START.equals(code)) {  
  46.                         if (aCamera != null) {  
  47.                             netStream.attachCamera(aCamera, -1 /*snapshotMilliseconds*/);  
  48.                             Log.d("DEBUG""aCamera.start()");  
  49.                             aCamera.start();  
  50.                         } else {  
  51.                             Log.d("DEBUG""camera == null");  
  52.                         }  
  53.                     }      
  54.                 }  
  55.             });  
  56.             netStream.publish(VideoName, NetStream.RECORD);  
  57.         }  
  58.           
  59.         public void start() {  
  60.             camera.startPreview();  
  61.         }  
  62.   
  63.         @Override  
  64.         public void onPreviewFrame(byte[] arg0, Camera arg1) {  
  65.             // TODO Auto-generated method stub  
  66.             if (!active) return;  
  67.             if (!init) {  
  68.                 blockWidth = 32;  
  69.                 blockHeight = 32;  
  70.                 timeBetweenFrames = 100// 1000 / frameRate  
  71.                 frameCounter = 0;  
  72.                 previous = null;  
  73.                 init = true;  
  74.             }  
  75.             final long ctime = System.currentTimeMillis();  
  76.             byte[] current = RemoteUtil.decodeYUV420SP2RGB(arg0, width, height);  
  77.             try {  
  78.                 final byte[] packet = RemoteUtil.encode(current, previous, blockWidth, blockHeight, width, height);  
  79.                 Log.d("DEBUG", packet.toString());  
  80.   
  81.                 fireOnVideoData(new MediaDataByteArray(timeBetweenFrames, new ByteArray(packet)));  
  82.                 previous = current;  
  83.                 if (++frameCounter % 10 == 0) previous = null;  
  84.             }  
  85.             catch (Exception e) {  
  86.                 e.printStackTrace();  
  87.             }  
  88.             final int spent = (int) (System.currentTimeMillis() - ctime);  
  89.             try {  
  90.                 Thread.sleep(Math.max(0, timeBetweenFrames - spent));  
  91.             } catch (InterruptedException e) {  
  92.                 // TODO Auto-generated catch block  
  93.                 e.printStackTrace();  
  94.             }  
  95.         }  
  96.   
  97.         @Override  
  98.         public void surfaceChanged(SurfaceHolder holder, int format, int width,  
  99.                 int height) {  
  100.             // TODO Auto-generated method stub  
  101.             startVideo();  
  102.         }  
  103.   
  104.         @Override  
  105.         public void surfaceCreated(SurfaceHolder holder) {  
  106.             // TODO Auto-generated method stub  
  107.             camera = Camera.open();  
  108.             try {  
  109.                 camera.setPreviewDisplay(surfaceHolder);  
  110.                 camera.setPreviewCallback(this);  
  111.                 Camera.Parameters params = camera.getParameters();  
  112.                 params.setPreviewSize(width, height);  
  113.                 camera.setParameters(params);  
  114.             } catch (IOException e) {  
  115.                 // TODO Auto-generated catch block  
  116.                 e.printStackTrace();  
  117.                 camera.release();  
  118.                 camera = null;  
  119.             }  
  120.         }  
  121.   
  122.         @Override  
  123.         public void surfaceDestroyed(SurfaceHolder holder) {  
  124.             // TODO Auto-generated method stub  
  125.             if (camera != null) {  
  126.                 camera.stopPreview();  
  127.                 camera.release();  
  128.                 camera = null;  
  129.             }  
  130.         }  
  131.     } //AndroidCamera</pre><br>  
  132. <br>  
  133. <pre></pre>  
  134. <p></p>  
  135. <pre></pre>  
  136. 上面的实现原理是基于类库自带的ExDesktopPublisher.java实现的,因此有些我自己也无法看懂。(因为我不懂多媒体)  
  137. <p></p>  
  138. <p>值得说明的是在发布实时视频的时候是通过类库里的NetStream的publish方法进行发布的,在这之前需要先用attachCamera方法给他设置视频源(代码里有)。</p>  
  139. <p></p>  
  140. <pre name="code" class="java"><pre name="code" class="java"> RemoteUtil.decodeYUV420SP2RGB</pre>  
  141. <pre></pre>  
  142. <p></p>  
  143. <pre></pre>  
  144. 是对onPreviewFrame获取到的YUV420视频源数据进行转换,转到RGB的,不然显示也许会有问题。算法如下:  
  145. <p></p>  
  146. <p></p>  
  147. <pre name="code" class="java">public static byte[] decodeYUV420SP2RGB(byte[] yuv420sp, int width, int height) {  
  148.         final int frameSize = width * height;     
  149.           
  150.         byte[] rgbBuf = new byte[frameSize * 3];  
  151.           
  152.        // if (rgbBuf == null) throw new NullPointerException("buffer 'rgbBuf' is null");     
  153.         if (rgbBuf.length < frameSize * 3throw new IllegalArgumentException("buffer 'rgbBuf' size "  + rgbBuf.length + " < minimum " + frameSize * 3);     
  154.         
  155.         if (yuv420sp == nullthrow new NullPointerException("buffer 'yuv420sp' is null");     
  156.         
  157.         if (yuv420sp.length < frameSize * 3 / 2throw new IllegalArgumentException("buffer 'yuv420sp' size " + yuv420sp.length + " < minimum " + frameSize * 3 / 2);     
  158.              
  159.         int i = 0, y = 0;     
  160.         int uvp = 0, u = 0, v = 0;     
  161.         int y1192 = 0, r = 0, g = 0, b = 0;     
  162.              
  163.         for (int j = 0, yp = 0; j < height; j++) {     
  164.              uvp = frameSize + (j >> 1) * width;     
  165.              u = 0;     
  166.              v = 0;     
  167.              for (i = 0; i < width; i++, yp++) {     
  168.                  y = (0xff & ((int) yuv420sp[yp])) - 16;     
  169.                  if (y < 0) y = 0;     
  170.                  if ((i & 1) == 0) {     
  171.                      v = (0xff & yuv420sp[uvp++]) - 128;     
  172.                      u = (0xff & yuv420sp[uvp++]) - 128;     
  173.                  }     
  174.                      
  175.                  y1192 = 1192 * y;     
  176.                  r = (y1192 + 1634 * v);     
  177.                  g = (y1192 - 833 * v - 400 * u);     
  178.                  b = (y1192 + 2066 * u);     
  179.                      
  180.                  if (r < 0) r = 0else if (r > 262143) r = 262143;     
  181.                  if (g < 0) g = 0else if (g > 262143) g = 262143;     
  182.                  if (b < 0) b = 0else if (b > 262143) b = 262143;     
  183.                      
  184.                  rgbBuf[yp * 3] = (byte)(r >> 10);     
  185.                  rgbBuf[yp * 3 + 1] = (byte)(g >> 10);     
  186.                  rgbBuf[yp * 3 + 2] = (byte)(b >> 10);  
  187.              }     
  188.          }//for  
  189.         return rgbBuf;  
  190.      }// decodeYUV420Sp2RGB</pre><pre name="code" class="java"><pre name="code" class="java">RemoteUtil.encode</pre>  
  191. <pre></pre>  
  192. <p></p>  
  193. <pre></pre>  
  194. 的算法取之于ExDesktopPublisher.java,应该是对视频数据的RTMP封装。这里就不贴代码了,可以到sample的文件里拿来用。  
  195. <p></p>  
  196. <p>以上文字组织很乱,因为我是在答辩的前一个晚上才实现的,因此代码也很乱,很难组织清楚,不过原理就是这样。最后的确是实现了实时视频,然而可能由于转码算法问题,实时视频的颜色是有问题的。</p>  
  197. <p>为自己的大学生活里最后一次软件功能实现留给纪念吧!<br>  
  198. </p>  
  199. <p><br>  
  200. </p>  
  201.   
  202. </pre></pre>  

这篇关于android视频开发之一Android 如何使用juv-rtmp-client.jar向Red5服务器发布实时视频数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/625268

相关文章

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

Redis中的Lettuce使用详解

《Redis中的Lettuce使用详解》Lettuce是一个高级的、线程安全的Redis客户端,用于与Redis数据库交互,Lettuce是一个功能强大、使用方便的Redis客户端,适用于各种规模的J... 目录简介特点连接池连接池特点连接池管理连接池优势连接池配置参数监控常用监控工具通过JMX监控通过Pr

解决mysql插入数据锁等待超时报错:Lock wait timeout exceeded;try restarting transaction

《解决mysql插入数据锁等待超时报错:Lockwaittimeoutexceeded;tryrestartingtransaction》:本文主要介绍解决mysql插入数据锁等待超时报... 目录报错信息解决办法1、数据库中执行如下sql2、再到 INNODB_TRX 事务表中查看总结报错信息Lock

apache的commons-pool2原理与使用实践记录

《apache的commons-pool2原理与使用实践记录》ApacheCommonsPool2是一个高效的对象池化框架,通过复用昂贵资源(如数据库连接、线程、网络连接)优化系统性能,这篇文章主... 目录一、核心原理与组件二、使用步骤详解(以数据库连接池为例)三、高级配置与优化四、典型应用场景五、注意事

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创