精华内容
下载资源
问答
  • 人脸识别开源SDK源码

    2015-07-01 17:54:54
    人脸识别开源SDK源码,自己下载看看吧
  • dianping-open-sdk 是大众点评网开源 SDK 样本集合。这些样本可以让你使用大众点评网的 API,包括 Java/Php/JavaScript/iOS。样本:dianping-android-samplesdianping-javascript-samplesdianping-java-...
  • 智游推送Android-push-开源SDK
  • PSn00bSDK是针对原始Sony PlayStation的100%免费和开源SDK项目,用于100%免费为控制台开发自制应用程序和游戏。 该SDK可用于免费软件,商业和开源自制程序项目。 该SDK主要由库(libpsn00b)和一些实用程序组成...
  • 开源SDK,用于将的OAuth2 API集成到您PHP应用程序中。 请注意,Medium的API仍处于早期阶段,该实现方法不是最终的。 重大变化将发生。 该SDK是非官方的。 您可以在找到Medium的API文档。 安装 composer require ...
  • nxdk:为原始Xbox开发的跨平台,开源SDK:* new * xdk
  • android制作室内或室外实景全景地图开源sdk和演示demo 1、支持手势拖动 2、支持点击进入下一个场景附加动画效果 3、支持手势缩放场景 4、数据可存放在本地或服务器
  • 有没有C#好用一点的SIP软电话开源SDK和源码 最好有完整文档或Demo的 麻烦推荐下,万分感谢
  • 如何通过开源SDK控制松灵机器人SCOUT底盘? 松灵课堂开课啦!为了更方便的解答大家在使用我们移动底盘进行开发调试的时候出现的问题,使二次研发更加得心应手,我们开设了松灵课堂,主要为小伙伴们讲解松灵机器人...

    如何通过开源SDK控制松灵机器人SCOUT底盘?

    松灵课堂开课啦!为了更方便的解答大家在使用我们移动底盘进行开发调试的时候出现的问题,使二次研发更加得心应手,我们开设了松灵课堂,主要为小伙伴们讲解松灵机器人开发过程中一些需要注意的问题,同时也会将具有代表性的用户问题(隐去商业秘密后)详解发布出来,欢迎各位小伙伴随时与我们交流。

    松灵课堂系列 SCOUT 专题分为三篇文章,第一个专题我们采用松灵最具代表性的SCOUT 2.0机器人移动平台,作为本系列的第一篇文章,我们先简单的教大家如何通过开源SDK控制SCOUT?接下来的两篇文章分别给大家讲解ros_package 解析和scout_ros代码分析教学。

    松灵机器人SCOUT2.0开源SDK控制教程

    前期准备

    准备

    ■ 硬件准备
    ● CANlight can通讯模块 X1
    ● Thinkpad E490 笔记本电脑 X1
    ​ ● AGILEX SCOUT 2.0 移动机器人底盘 X1
    ● AGILEX SCOUT 2.0 配套遥控器FS-i6s X1
    ● AGILEX SCOUT 2.0 顶部航空插座 X1
    在这里插入图片描述
    ■使用示例环境说明
    ● Ubuntu 16.04 LTS(此为测试版本,在Ubuntu 18.04 LTS测试过)
    ● Git

    该软件包提供了一个C++的接口,可与松灵机器人的移动平台进行通讯,用以将命令发送给机器人并获得机器人的最新状态。该SDK可在x86和ARM平台上使用。
    通常情况下,你只需要定义一个机器人的基类(例如ScoutBase),然后使用该对象编辑控制机器人。

    从内部讲,基类管理两个后台线程,一个用于处理机器人的CAN/UART信息状态,并相应的在机器人状态数据结构中更新状态,另一个维护一个20Hz循环并给机器人发送最新指令。用户可以在主线程中迭代执行任务,并检查机器人状态或设置控制指令。

    相关示例,请参考“src/apps”。

    硬件接口

    设置CAN转USB适配器

    1. 启用gs_usb内核模块(本指令需要搭配相应的硬件设备才可以使用,需要Linux内部版本>4.5)
    $ sudo modprobe gs_usb
    
    1. 设置can设备参数
     $ sudo ip link set can0 up type can bitrate 500000
    
    1. 如果在前面的步骤中没有发生错误,您可以使用以下指令查看can设备
    $ ifconfig -a
    
    1. 安装和使用can-utils来测试硬件
    $ sudo apt install can-utils
    
    1. 测试指令
    # receiving data from can0
    $ candump can0
    # send data to can0
    $ cansend can0 001#1122334455667788
    

    提供了 "./scripts"文件夹里的两个脚本以便于设置。您可以在首次安装时运行“ ./setup_can2usb.bash”,并在每次拔出和重新插入适配器时运行“ ./bringup_can2usb.bash”以启动设备。
    在这里插入图片描述

    设置UART

    通常您的UART2USB电缆应自动识别为“ / dev / ttyUSB0”或类似名称即可使用。如果在尝试打开端口是收到错误"… permission denied …" ,则需要授予用户账号访问权限:

    $ sudo usermod -a -G dialout $USER
    

    您需要重新登录才能使更改生效。

    构建SDK

    1. 安装编译工具
    $ sudo apt install build-essential cmake
    

    在这里插入图片描述
    如果您想要构建TUI监控工具,需安装libncurses

    $ sudo apt install libncurses5-dev
    
    1. 配置和构建
    $ cd scout_sdk 
    $ mkdir build
    $ cd build
    $ cmake ..
    $ make
    

    运行演示应用

    演示代码需要一个用于CAN总线模式的参数

    $ ./app_scout_demo can0
    

    使用RS232接口时,必须提供端口名称和波特率。

    $./app_scout_demo /dev/ttyUSB0 115200
    

    如果您已安装“ libncurses5-dev”并构建了“ app_scout_monitor”,则可以通过相似的方式来运行:

    $ ./app_scout_monitor can0
    

    或者

    $./app_scout_monitor /dev/ttyUSB0 115200
    

    在这里插入图片描述
    通过开源SDK,客户可以快速通过指令的形式与小车底盘进行控制,省去客户自主适配协议的过程,做到拿来即用的状态,省去客户开发的过程,缩短开发时间。本节内容基本结束,下一节我们主要讲解ros_package 解析。

    代表性问题

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 这是C ++中RPLIDAR产品的公共SDK,并在GPLv3许可下开源。 如果您使用的是ROS(机器人操作系统),请直接使用我们的开源ROS节点: : 。 如果您只是评估RPLIDAR,则可以使用Slamtec RoboStudio( )(当前仅支持...
  • AR应用开发必备开源SDK:ARToolkit

    千次阅读 2016-12-07 16:49:02
    AR应用开发必备开源SDK:ARToolkit  最近火爆全球的AR游戏《口袋妖怪GO》是是由任天堂、Pokemon公司和Niantic联合制作开发的。这一作品让近年来一直不温不火的AR技术又重回大众视野。而如果要快速开发一款AR手机...

    AR应用开发必备开源SDK:ARToolkit

     最近火爆全球的AR游戏《口袋妖怪GO》是是由任天堂、Pokemon公司和Niantic联合制作开发的。这一作品让近年来一直不温不火的AR技术又重回大众视野。而如果要快速开发一款AR手机应用,那么这款SDK是个不错的选择,它的名字叫ARToolkit。

      ARToolkit是一款开源的增强现实SDK,使用C/C++编写,可以帮助我们很容易的开发增强现实应用。它有着世界上使用最广泛的AR追踪库,遵循LGPL v3.0开源授权协议,提供跨平台支持(MacOSX、PC、Linux、Android、iOS),同时还为Unity提供ARToolKit插件。

    AR应用开发必备开源SDK:ARToolkit

      ARToolKit将会在秋季发布v6版本,将会有更多全新的特性。

      GitHub 地址:https://github.com/artoolkit/artoolkit5

      官方网站地址:https://artoolkit.org/download-artoolkit-sdk

      开发者们都知道,开发一款AR应用最难的部分之一就是要实时精确计算用户的视角,这样虚拟图像才能完全符合现实世界的对象。ARToolKit使用计算机视觉技术来计算实时相机位置和相对定位,使得形状和平面纹理表现一致,可以让开发人员叠加虚拟对象。ARToolKit当前支持经典的square marker,2D条形码,multimarker和自然特性追踪。并且在未来还将会支持以上任意的组合。

      除此之外,ARToolKit支持视频和光学穿透AR。视频穿透增强显示是在现实世界直播视频上叠加虚拟图像。光学穿透增强现实是直接在现实世界叠加的计算机图像。

    展开全文
  • 微信SDK 目前处于开发状态,目前实现了以下功能: 接收微信服务器推送信息,对推送信息类型进行识别 微信API客户端封装(用户管理、用户组管理、客服管理、自定义菜单管理、系统管理等) 微信跳转验证封装 微信卡卷...
  • android AR开源SDK

    2017-01-12 10:46:10
    AndAR : http://www.artoolworks.com/ BeyondAR:很容易使用 http://beyondar.com/platform DroidAR: http://code.google.com/p/droidar/ Mixare: http://www.mixare.org/ ...

    AndAR :
    http://www.artoolworks.com/

    BeyondAR:很容易使用
    http://beyondar.com/platform

    DroidAR:
    http://code.google.com/p/droidar/

    Mixare:

    http://www.mixare.org/

    http://beyondar.com/platform
    展开全文
  • Qiskit是一个开源SDK,可在电路,算法和应用模块级别与量子计算机一起使用。 Qiskit由相互协作以实现量子计算的元素组成。 这是一个简单的元软件包,用于完全安装Qiskit的元素。 安装 安装qiskit的最佳方法是使用...
  • 开源SDK实现Android视频直播

    万次阅读 2017-06-02 12:03:52
    目前市面上集视频采集、编码、封装和推流于一体的SDK已经有很多了,例如商业版的NodeMedia,但NodeMedia SDK按包名授权,未授权包名应用使用有版权提示信息。 我这里使用的是别人分享在github上的一个免费SDK, ...

    最近一段时间,视频直播可谓大火。在视频直播领域,有不同的商家提供各种的商业解决方案,包括软硬件设备,摄像机,编码器,流媒体服务器等。本文要讲解的是如何使用一系列免费工具,打造一套视频直播方案。

    视频直播流程

    视频直播的流程可以分为如下几步:
    采集 —>处理—>编码和封装—>推流到服务器—>服务器流分发—>播放器流播放
    这里写图片描述

    1.采集

    采集是整个视频推流过程中的第一个环节,它从系统的采集设备中获取原始视频数据,将其输出到下一个环节。视频的采集涉及两方面数据的采集:音频采集和图像采集,它们分别对应两种完全不同的输入源和数据格式。

    这里写图片描述

    • 音频采集
      音频数据既能与图像结合组合成视频数据,也能以纯音频的方式采集播放,后者在很多成熟的应用场景如在线电台和语音电台等起着非常重要的作用。音频的采集过程主要通过设备将环境中的模拟信号采集成 PCM 编码的原始数据,然后编码压缩成 MP3 等格式的数据分发出去。常见的音频压缩格式有:MP3,AAC,HE-AAC,Opus,FLAC,Vorbis (Ogg),Speex 和 AMR等。
      音频采集和编码主要面临的挑战在于:延时敏感、卡顿敏感、噪声消除(Denoise)、回声消除(AEC)、静音检测(VAD)和各种混音算法等。

    • 图像采集
      将图像采集的图片结果组合成一组连续播放的动画,即构成视频中可肉眼观看的内容。图像的采集过程主要由摄像头等设备拍摄成 YUV 编码的原始数据,然后经过编码压缩成 H.264 等格式的数据分发出去。常见的视频封装格式有:MP4、3GP、AVI、MKV、WMV、MPG、VOB、FLV、SWF、MOV、RMVB 和 WebM 等。
      图像由于其直观感受最强并且体积也比较大,构成了一个视频内容的主要部分。图像采集和编码面临的主要挑战在于:设备兼容性差、延时敏感、卡顿敏感以及各种对图像的处理操作如美颜和水印等。

    • 视频采集
      视频采集的采集源主要有摄像头采集、屏幕录制和从视频文件推流。

    2.处理

    视频或者音频完成采集之后得到原始数据,为了增强一些现场效果或者加上一些额外的效果,我们一般会在将其编码压缩前进行处理,比如打上时间戳或者公司 Logo 的水印,祛斑美颜和声音混淆等处理。在主播和观众连麦场景中,主播需要和某个或者多个观众进行对话,并将对话结果实时分享给其他所有观众,连麦的处理也有部分工作在推流端完成。

    这里写图片描述

    如上图所示,处理环节中分为音频和视频处理,音频处理中具体包含混音、降噪和声音特效等处理,视频处理中包含美颜、水印、以及各种自定义滤镜等处理。

    3.编码和封装

    (1)编码

    如果把整个流媒体比喻成一个物流系统,那么编解码就是其中配货和装货的过程,这个过程非常重要,它的速度和压缩比对物流系统的意义非常大,影响物流系统的整体速度和成本。同样,对流媒体传输来说,编码也非常重要,它的编码性能、编码速度和编码压缩比会直接影响整个流媒体传输的用户体验和传输成本。

    视频编码的意义
    原始视频数据存储空间大,一个 1080P 的 7 s 视频需要 817 MB
    原始视频数据传输占用带宽大,10 Mbps 的带宽传输上述 7 s 视频需要 11 分钟。而经过 H.264 编码压缩之后,视频大小只有 708 k ,10 Mbps 的带宽仅仅需要 500 ms ,可以满足实时传输的需求,所以从视频采集传感器采集来的原始视频势必要经过视频编码。

    基本原理
    为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?核心思想就是去除冗余信息:
    1)空间冗余:图像相邻像素之间有较强的相关性
    2)时间冗余:视频序列的相邻图像之间内容相似
    3)编码冗余:不同像素值出现的概率不同
    4)视觉冗余:人的视觉系统对某些细节不敏感
    5)知识冗余:规律性的结构可由先验知识和背景知识得到

    编码器的选择
    视频编码器经历了数十年的发展,已经从开始的只支持帧内编码演进到现如今的 H.265 和 VP9 为代表的新一代编码器,下面是一些常见的视频编码器:
    1)H.264/AVC
    2)HEVC/H.265
    3)VP8
    4)VP9
    5)FFmpeg
    注:音频编码器有Mp3, AAC等。

    (2)封装

    沿用前面的比喻,封装可以理解为采用哪种货车去运输,也就是媒体的容器。
    所谓容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准。容器使得不同多媒体内容同步播放变得很简单,而容器的另一个作用就是为多媒体内容提供索引,也就是说如果没有容器存在的话一部影片你只能从一开始看到最后,不能拖动进度条,而且如果你不自己去手动另外载入音频就没有声音。下面是几种常见的封装格式:
    1)AVI 格式(后缀为 .avi)
    2)DV-AVI 格式(后缀为 .avi)
    3)QuickTime File Format 格式(后缀为 .mov)
    4)MPEG 格式(文件后缀可以是 .mpg .mpeg .mpe .dat .vob .asf .3gp .mp4等)
    5)WMV 格式(后缀为.wmv .asf)
    6)Real Video 格式(后缀为 .rm .rmvb)
    7)Flash Video 格式(后缀为 .flv)
    8)Matroska 格式(后缀为 .mkv)
    9)MPEG2-TS 格式 (后缀为 .ts)
    目前,我们在流媒体传输,尤其是直播中主要采用的就是 FLV 和 MPEG2-TS 格式,分别用于 RTMP/HTTP-FLV 和 HLS 协议。

    4.推流到服务器

    推流是直播的第一公里,直播的推流对这个直播链路影响非常大,如果推流的网络不稳定,无论我们如何做优化,观众的体验都会很糟糕。所以也是我们排查问题的第一步,如何系统地解决这类问题需要我们对相关理论有基础的认识。
    推送协议主要有三种:

    • RTSP(Real Time Streaming Protocol):实时流传送协议,是用来控制声音或影像的多媒体串流协议, 由RealNetworks和Netscape共同提出的;
    • RTMP(Real Time MessagingProtocol):实时消息传送协议,是Adobe公司为Flash播放器和服务器之间音频、视频和数据传输 开发的开放协议;
    • HLS(HTTP Live Streaming):是苹果公司(Apple Inc.)实现的基于HTTP的流媒体传输协议;

    RTMP协议基于 TCP,是一种设计用来进行实时数据通信的网络协议,主要用来在 flash/AIR 平台和支持 RTMP 协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括 Adobe Media Server/Ultrant Media Server/red5 等。
    它有三种变种:
    - RTMP工作在TCP之上的明文协议,使用端口1935;
    - RTMPT封装在HTTP请求之中,可穿越防火墙;
    - RTMPS类似RTMPT,但使用的是HTTPS连接;

    RTMP 是目前主流的流媒体传输协议,广泛用于直播领域,可以说市面上绝大多数的直播产品都采用了这个协议。
    RTMP协议就像一个用来装数据包的容器,这些数据可以是AMF格式的数据,也可以是FLV中的视/音频数据。一个单一的连接可以通过不同的通道传输多路网络流。这些通道中的包都是按照固定大小的包传输的。
    这里写图片描述

    5.服务器流分发

    流媒体服务器的作用是负责直播流的发布和转播分发功能。
    流媒体服务器有诸多选择,如商业版的Wowza。但我选择的是Nginx,它是一款优秀的免费Web服务器,后面我会详细介绍如何搭建Nginx服务器。

    6.播放器流播放

    主要是实现直播节目在终端上的展现。因为我这里使用的传输协议是RTMP, 所以只要支持 RTMP 流协议的播放器都可以使用,譬如:

    • 电脑端:VLC等
    • 手机端:Vitamio以及ijkplayer等

    一般情况下我们把上面流程的前四步称为第一部分,即视频主播端的操作。视频采集处理后推流到流媒体服务器,第一部分功能完成。第二部分就是流媒体服务器,负责把从第一部分接收到的流进行处理并分发给观众。第三部分就是观众啦,只需要拥有支持流传输协议的播放器即可。
    这里写图片描述

    第一部分:采集推流SDK

    目前市面上集视频采集、编码、封装和推流于一体的SDK已经有很多了,例如商业版的NodeMedia,但NodeMedia SDK按包名授权,未授权包名应用使用有版权提示信息。
    我这里使用的是别人分享在github上的一个免费SDK,下载地址

    下面我就代码分析一下直播推流的过程吧:
    先看入口界面:
    这里写图片描述
    很简单,一个输入框让你填写服务器的推流地址,另外一个按钮开启推流。

    public class StartActivity extends Activity {
        public static final String RTMPURL_MESSAGE = "rtmppush.hx.com.rtmppush.rtmpurl";
        private Button _startRtmpPushButton = null;
        private EditText _rtmpUrlEditText = null;
    
        private View.OnClickListener _startRtmpPushOnClickedEvent = new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                Intent i = new Intent(StartActivity.this, MainActivity.class);
                String rtmpUrl = _rtmpUrlEditText.getText().toString();
                i.putExtra(StartActivity.RTMPURL_MESSAGE, rtmpUrl);
                StartActivity.this.startActivity(i);
            }
        };
    
        private void InitUI(){
            _rtmpUrlEditText = (EditText)findViewById(R.id.rtmpUrleditText);
            _startRtmpPushButton = (Button)findViewById(R.id.startRtmpButton);
            _rtmpUrlEditText.setText("rtmp://192.168.1.104:1935/live/12345");
            _startRtmpPushButton.setOnClickListener(_startRtmpPushOnClickedEvent);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_start);
            InitUI();
        }
    }

    主要的推流过程在MainActivity里面,同样,先看界面:
    这里写图片描述

    布局文件:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/cameraRelative"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
    
        <SurfaceView
            android:id="@+id/surfaceViewEx"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <Button
            android:id="@+id/SwitchCamerabutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/surfaceViewEx"
            android:text="@string/SwitchCamera" />
    </RelativeLayout>

    其实就是用一个SurfaceView显示摄像头拍摄画面,并提供了一个按钮切换前置和后置摄像头。从入口函数看起:

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    
            Intent intent = getIntent();
            _rtmpUrl = intent.getStringExtra(StartActivity.RTMPURL_MESSAGE);
    
            InitAll();
    
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            _wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
        }

    首先设置全屏显示,常亮,竖屏,获取服务器的推流url,再初始化所有东西。

        private void InitAll() {
            WindowManager wm = this.getWindowManager();
    
            int width = wm.getDefaultDisplay().getWidth();
            int height = wm.getDefaultDisplay().getHeight();
            int iNewWidth = (int) (height * 3.0 / 4.0);
    
            RelativeLayout rCameraLayout = (RelativeLayout) findViewById(R.id.cameraRelative);
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.MATCH_PARENT);
            int iPos = width - iNewWidth;
            layoutParams.setMargins(iPos, 0, 0, 0);
    
            _mSurfaceView = (SurfaceView) this.findViewById(R.id.surfaceViewEx);
            _mSurfaceView.getHolder().setFixedSize(HEIGHT_DEF, WIDTH_DEF);
            _mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            _mSurfaceView.getHolder().setKeepScreenOn(true);
            _mSurfaceView.getHolder().addCallback(new SurceCallBack());
            _mSurfaceView.setLayoutParams(layoutParams);
    
            InitAudioRecord();
    
            _SwitchCameraBtn = (Button) findViewById(R.id.SwitchCamerabutton);
            _SwitchCameraBtn.setOnClickListener(_switchCameraOnClickedEvent);
    
            RtmpStartMessage();//开始推流
        }

    首先设置屏幕比例3:4显示,给SurfaceView设置一些参数并添加回调,再初始化AudioRecord,最后执行开始推流。音频在这里初始化了,那么相机在哪里初始化呢?其实在SurfaceView的回调函数里。

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                _iDegrees = getDisplayOritation(getDispalyRotation(), 0);
                if (_mCamera != null) {
                    InitCamera(); //初始化相机
                    return;
                }
                //华为i7前后共用摄像头
                if (Camera.getNumberOfCameras() == 1) {
                    _bIsFront = false;
                    _mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                } else {
                    _mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
                }
                InitCamera();
            }
    
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
        }

    相机的初始化就在这里啦:

        public void InitCamera() {
            Camera.Parameters p = _mCamera.getParameters();
    
            Size prevewSize = p.getPreviewSize();
            showlog("Original Width:" + prevewSize.width + ", height:" + prevewSize.height);
    
            List<Size> PreviewSizeList = p.getSupportedPreviewSizes();
            List<Integer> PreviewFormats = p.getSupportedPreviewFormats();
            showlog("Listing all supported preview sizes");
            for (Camera.Size size : PreviewSizeList) {
                showlog("  w: " + size.width + ", h: " + size.height);
            }
    
            showlog("Listing all supported preview formats");
            Integer iNV21Flag = 0;
            Integer iYV12Flag = 0;
            for (Integer yuvFormat : PreviewFormats) {
                showlog("preview formats:" + yuvFormat);
                if (yuvFormat == android.graphics.ImageFormat.YV12) {
                    iYV12Flag = android.graphics.ImageFormat.YV12;
                }
                if (yuvFormat == android.graphics.ImageFormat.NV21) {
                    iNV21Flag = android.graphics.ImageFormat.NV21;
                }
            }
    
            if (iNV21Flag != 0) {
                _iCameraCodecType = iNV21Flag;
            } else if (iYV12Flag != 0) {
                _iCameraCodecType = iYV12Flag;
            }
            p.setPreviewSize(HEIGHT_DEF, WIDTH_DEF);
            p.setPreviewFormat(_iCameraCodecType);
            p.setPreviewFrameRate(FRAMERATE_DEF);
    
            showlog("_iDegrees="+_iDegrees);
            _mCamera.setDisplayOrientation(_iDegrees);
            p.setRotation(_iDegrees);
            _mCamera.setPreviewCallback(_previewCallback);
            _mCamera.setParameters(p);
            try {
                _mCamera.setPreviewDisplay(_mSurfaceView.getHolder());
            } catch (Exception e) {
                return;
            }
            _mCamera.cancelAutoFocus();//只有加上了这一句,才会自动对焦。
            _mCamera.startPreview();
        }

    还记得之前初始化完成之后开始推流函数吗?

        private void RtmpStartMessage() {
            Message msg = new Message();
            msg.what = ID_RTMP_PUSH_START;
            Bundle b = new Bundle();
            b.putInt("ret", 0);
            msg.setData(b);
            mHandler.sendMessage(msg);
        }

    Handler处理:

        public Handler mHandler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                Bundle b = msg.getData();
                int ret;
                switch (msg.what) {
                    case ID_RTMP_PUSH_START: {
                        Start();
                        break;
                    }
                }
            }
        };

    真正的推流实现原来在这里:

        private void Start() {
            if (DEBUG_ENABLE) {
                File saveDir = Environment.getExternalStorageDirectory();
                String strFilename = saveDir + "/aaa.h264";
                try {
                    if (!new File(strFilename).exists()) {
                        new File(strFilename).createNewFile();
                    }
                    _outputStream = new DataOutputStream(new FileOutputStream(strFilename));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //_rtmpSessionMgr.Start("rtmp://192.168.0.110/live/12345678");
            _rtmpSessionMgr = new RtmpSessionManager();
            _rtmpSessionMgr.Start(_rtmpUrl); //------point 1
    
            int iFormat = _iCameraCodecType;
            _swEncH264 = new SWVideoEncoder(WIDTH_DEF, HEIGHT_DEF, FRAMERATE_DEF, BITRATE_DEF);
            _swEncH264.start(iFormat); //------point 2
    
            _bStartFlag = true;
    
            _h264EncoderThread = new Thread(_h264Runnable);
            _h264EncoderThread.setPriority(Thread.MAX_PRIORITY);
            _h264EncoderThread.start(); //------point 3
    
            _AudioRecorder.startRecording();
            _AacEncoderThread = new Thread(_aacEncoderRunnable);
            _AacEncoderThread.setPriority(Thread.MAX_PRIORITY);
            _AacEncoderThread.start(); //------point 4
        }

    里面主要的函数有四个,我分别标出来了,现在我们逐一看一下。首先是point 1,这已经走到SDK里面了

        public int Start(String rtmpUrl){
            int iRet = 0;
    
            _rtmpUrl = rtmpUrl;
            _rtmpSession = new RtmpSession();
    
            _bStartFlag = true;
            _h264EncoderThread.setPriority(Thread.MAX_PRIORITY);
            _h264EncoderThread.start();
    
            return iRet;
        }

    其实就是启动了一个线程,这个线程稍微有点复杂

        private Thread _h264EncoderThread = new Thread(new Runnable() {
    
            private Boolean WaitforReConnect(){
                for(int i=0; i < 500; i++){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(_h264EncoderThread.interrupted() || (!_bStartFlag)){
                        return false;
                    }
                }
                return true;
            }
            @Override
            public void run() {
                while (!_h264EncoderThread.interrupted() && (_bStartFlag)) {
                    if(_rtmpHandle == 0) {
                        _rtmpHandle = _rtmpSession.RtmpConnect(_rtmpUrl);
                        if(_rtmpHandle == 0){
                            if(!WaitforReConnect()){
                                break;
                            }
                            continue;
                        }
                    }else{
                        if(_rtmpSession.RtmpIsConnect(_rtmpHandle) == 0){
                            _rtmpHandle = _rtmpSession.RtmpConnect(_rtmpUrl);
                            if(_rtmpHandle == 0){
                                if(!WaitforReConnect()){
                                    break;
                                }
                                continue;
                            }
                        }
                    }
    
                    if((_videoDataQueue.size() == 0) && (_audioDataQueue.size()==0)){
                        try {
                            Thread.sleep(30);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                    //Log.i(TAG, "VideoQueue length="+_videoDataQueue.size()+", AudioQueue length="+_audioDataQueue.size());
                    for(int i = 0; i < 100; i++){
                        byte[] audioData = GetAndReleaseAudioQueue();
                        if(audioData == null){
                            break;
                        }
                        //Log.i(TAG, "###RtmpSendAudioData:"+audioData.length);
                        _rtmpSession.RtmpSendAudioData(_rtmpHandle, audioData, audioData.length);
                    }
    
                    byte[] videoData = GetAndReleaseVideoQueue();
                    if(videoData != null){
                        //Log.i(TAG, "$$$RtmpSendVideoData:"+videoData.length);
                        _rtmpSession.RtmpSendVideoData(_rtmpHandle, videoData, videoData.length);
                    }
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                _videoDataQueueLock.lock();
                _videoDataQueue.clear();
                _videoDataQueueLock.unlock();
                _audioDataQueueLock.lock();
                _audioDataQueue.clear();
                _audioDataQueueLock.unlock();
    
                if((_rtmpHandle != 0) && (_rtmpSession != null)){
                    _rtmpSession.RtmpDisconnect(_rtmpHandle);
                }
                _rtmpHandle  = 0;
                _rtmpSession = null;
            }
        });

    看18行,主要就是一个while循环,每隔一段时间去_audioDataQueue和_videoDataQueue两个缓冲数组中取数据发送给服务器,发送方法_rtmpSession.RtmpSendAudioData和_rtmpSession.RtmpSendVideoData都是Native方法,通过jni调用so库文件的内容,每隔一段时间,这个时间是多少呢?看第4行,原来是5秒钟,也就是说我们的视频数据会在缓冲中存放5秒才被取出来发给服务器,所有直播会有5秒的延时,我们可以修改这块来控制直播延时。
    上面说了我们会从_audioDataQueue和_videoDataQueue两个Buffer里面取数据,那么数据是何时放进去的呢?看上面的point 2,3,4。首先是point 2,同样走进了SDK:

       public boolean start(int iFormateType){
            int iType = OpenH264Encoder.YUV420_TYPE;
    
            if(iFormateType == android.graphics.ImageFormat.YV12){
                iType = OpenH264Encoder.YUV12_TYPE;
            }else{
                iType = OpenH264Encoder.YUV420_TYPE;
            }
            _OpenH264Encoder = new OpenH264Encoder();
            _iHandle = _OpenH264Encoder.InitEncode(_iWidth, _iHeight, _iBitRate, _iFrameRate, iType);
            if(_iHandle == 0){
                return false;
            }
    
            _iFormatType = iFormateType;
            return true;
        }

    其实这是初始化编码器,具体的初始化过程也在so文件,jni调用。point 3,4其实就是开启两个线程,那我们看看线程中具体实现吧。

       private Thread _h264EncoderThread = null;
        private Runnable _h264Runnable = new Runnable() {
            @Override
            public void run() {
                while (!_h264EncoderThread.interrupted() && _bStartFlag) {
                    int iSize = _YUVQueue.size();
                    if (iSize > 0) {
                        _yuvQueueLock.lock();
                        byte[] yuvData = _YUVQueue.poll();
                        if (iSize > 9) {
                            Log.i(LOG_TAG, "###YUV Queue len=" + _YUVQueue.size() + ", YUV length=" + yuvData.length);
                        }
    
                        _yuvQueueLock.unlock();
                        if (yuvData == null) {
                            continue;
                        }
    
                        if (_bIsFront) {
                            _yuvEdit = _swEncH264.YUV420pRotate270(yuvData, HEIGHT_DEF, WIDTH_DEF);
                        } else {
                            _yuvEdit = _swEncH264.YUV420pRotate90(yuvData, HEIGHT_DEF, WIDTH_DEF);
                        }
                        byte[] h264Data = _swEncH264.EncoderH264(_yuvEdit);
                        if (h264Data != null) {
                            _rtmpSessionMgr.InsertVideoData(h264Data);
                            if (DEBUG_ENABLE) {
                                try {
                                    _outputStream.write(h264Data);
                                    int iH264Len = h264Data.length;
                                    //Log.i(LOG_TAG, "Encode H264 len="+iH264Len);
                                } catch (IOException e1) {
                                    e1.printStackTrace();
                                }
                            }
                        }
                    }
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                _YUVQueue.clear();
            }
        };

    也是一个循环线程,第9行,从_YUVQueue中取出摄像头获取的数据,然后进行视频旋转,第24行,对数据进行编码,然后执行26行,InsertVideoData:

        public void InsertVideoData(byte[] videoData){
            if(!_bStartFlag){
                return;
            }
            _videoDataQueueLock.lock();
            if(_videoDataQueue.size() > 50){
                _videoDataQueue.clear();
            }
            _videoDataQueue.offer(videoData);
            _videoDataQueueLock.unlock();
        }

    果然就是插入之前提到的_videoDataQueue的Buffer。这里插入的是视频数据,那么音频数据呢?在另外一个线程,内容大致相同

    private Runnable _aacEncoderRunnable = new Runnable() {
            @Override
            public void run() {
                DataOutputStream outputStream = null;
                if (DEBUG_ENABLE) {
                    File saveDir = Environment.getExternalStorageDirectory();
                    String strFilename = saveDir + "/aaa.aac";
                    try {
                        if (!new File(strFilename).exists()) {
                            new File(strFilename).createNewFile();
                        }
                        outputStream = new DataOutputStream(new FileOutputStream(strFilename));
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
    
                long lSleepTime = SAMPLE_RATE_DEF * 16 * 2 / _RecorderBuffer.length;
    
                while (!_AacEncoderThread.interrupted() && _bStartFlag) {
                    int iPCMLen = _AudioRecorder.read(_RecorderBuffer, 0, _RecorderBuffer.length); // Fill buffer
                    if ((iPCMLen != _AudioRecorder.ERROR_BAD_VALUE) && (iPCMLen != 0)) {
                        if (_fdkaacHandle != 0) {
                            byte[] aacBuffer = _fdkaacEnc.FdkAacEncode(_fdkaacHandle, _RecorderBuffer);
                            if (aacBuffer != null) {
                                long lLen = aacBuffer.length;
    
                                _rtmpSessionMgr.InsertAudioData(aacBuffer);
                                //Log.i(LOG_TAG, "fdk aac length="+lLen+" from pcm="+iPCMLen);
                                if (DEBUG_ENABLE) {
                                    try {
                                        outputStream.write(aacBuffer);
                                    } catch (IOException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    } else {
                        Log.i(LOG_TAG, "######fail to get PCM data");
                    }
                    try {
                        Thread.sleep(lSleepTime / 10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Log.i(LOG_TAG, "AAC Encoder Thread ended ......");
            }
        };
        private Thread _AacEncoderThread = null;

    这就是通过循环将音频数据插入_audioDataQueue这个Buffer。
    以上就是视频采集和推流的代码分析,Demo中并没有对视频进行任何处理,只是摄像头采集,编码后推流到服务器端。

    第二部分:Nginx服务器搭建

    流媒体服务器有诸多选择,如商业版的Wowza。但我选择的是免费的Nginx(nginx-rtmp-module)。Nginx本身是一个非常出色的HTTP服务器,它通过nginx的模块nginx-rtmp-module可以搭建一个功能相对比较完善的流媒体服务器。这个流媒体服务器可以支持RTMP和HLS。
    Nginx配合SDK做流媒体服务器的原理是: Nginx通过rtmp模块提供rtmp服务, SDK推送一个rtmp流到Nginx, 然后客户端通过访问Nginx来收看实时视频流。 HLS也是差不多的原理,只是最终客户端是通过HTTP协议来访问的,但是SDK推送流仍然是rtmp的。
    下面是一款已经集成rtmp模块的windows版本的Nginx。下载后,即可直接使用
    下载链接:https://github.com/illuspas/nginx-rtmp-win32

    1、rtmp端口配置

    配置文件在/conf/nginx.conf
    RTMP监听 1935 端口,启用live 和hls 两个application

    这里写图片描述

    所以你的流媒体服务器url可以写成:rtmp://(服务器IP地址):1935/live/xxxrtmp://(服务器IP地址):1935/hls/xxx
    例如:我们上面写的 rtmp://192.168.1.104:1935/live/12345

    HTTP监听 8080 端口,

    • :8080/stat 查看stream状态
    • :8080/index.html 为一个直播播放与直播发布测试器
    • :8080/vod.html为一个支持RTMP和HLS点播的测试器

    2、启动nginx服务

    双击nginx.exe文件或者在dos窗口下运行nginx.exe,即可启动nginx服务:
    这里写图片描述

    1)启动任务管理器,可以看到nginx.exe进程

    这里写图片描述

    2)打开网页输入http://localhot:8080,出现如下画面:
    这里写图片描述
    显示以上界面说明启动成功。

    第三部分:直播流的播放

    主播界面:
    这里写图片描述

    上面说过了只要支持RTMP流传输协议的播放器都可以收看到我们的直播。下面举两个例子吧:
    (1)window端播放器VLC
    这里写图片描述

    这里写图片描述

    (2)Android端播放器ijkplayer
    ijkplayer的使用请参考Android ijkplayer的使用解析

        private void initPlayer() {
            player = new PlayerManager(this);
            player.setFullScreenOnly(true);
            player.setScaleType(PlayerManager.SCALETYPE_FILLPARENT);
            player.playInFullScreen(true);
            player.setPlayerStateListener(this);
            player.play("rtmp://192.168.1.104:1935/live/12345");
        }

    这里写图片描述

    附:Demo下载地址


    另附网上一个开源的SDK

    项目地址
    GitHub:

    https://github.com/AnyRTC/AnyRTC-RTMP

    OSChina:

    https://git.oschina.net/dynctm/AnyRTC-RTMP

    项目特点:

    1,商业级开源代码,高效稳定
    2,超小内存占有率,移动直播针对性极致优化,代码冗余率极低
    3,打破平台壁垒,真正的跨平台,一套核心代码支持Android、iOS、Windows等
    4,超过200+Android手机、iOS全平台适配,硬件编解码可保证99%的可用性
    5,接口极简,推流:2个 拉流:2个
    6,底层库C++核心库代码风格采用:Google code style
    7,极简内核,无需再去深扒复杂的FFMpeg代码
    8,实用主义,那些什么坑什么优化等概念请搜索相关文章
    9,OpenH264软件编码,FFMpeg软件解码,FAAC/FAAD软件编解码,适配不同系统的硬件编解码统统包含
    10,支持SRS、Nginx-RTMP等标准RTMP服务;同时支持各大CDN厂商的接入

    rtmp直播地址:
    可以直接用电视台的rtmp直播地址:
    一般做rtmp编程的时候,需要用到rtmp视频源地址,通常可以自己搭建Red5等服务器到自己的机器配置好后,编写客户端,但是这样有点麻烦;

    为了方便,可以直接用电视台的rtmp直播地址:

    rtmp://live.hkstv.hk.lxdns.com/live/hks 测试可用;
    rtmp://www.planeta-online.tv:1936/live/channel_4

    香港卫视 rtmp://live.hkstv.hk.lxdns.com/live/hks
    香港卫视 http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8
    香港卫视 http://124.160.184.137:80/live/5 … a46720.m3u8?type=tv

    香港卫视精品台 http://221.120.177.59/hls/i3d7ragr.m3u8
    香港卫视精品台 rtmp://221.120.177.59/livestream/ucagm8kk

    更多地址详见:http://blog.csdn.net/newnewfeng/article/details/50456181

    展开全文
  • 别人都是免费版到商业版,我们是先做商业版在做靠谱的免费版,初心真的是想把数据能力平民化,同时配合数据采集SDK开源和支持多活分布式的ETL调度工具开源,让更多的人可以把数据用起来。在筹划的时候,公司也没有...
  • 通过此开源Web客户端SDK,您可以轻松地将Spark REST API集成到您的应用程序中。 Spark API当前处于beta版:。 先决条件 在上注册的应用程序。 有关更多信息,请参见。 (可选)身份验证API调用(来宾,访问和刷新...
  • Android facebook VK 登录与分享 开源SDK

    千次阅读 2017-05-05 18:49:56
    自己封好的SDK 直接调用 就可以登录 包含facebook VK 登录分享 首先导入SDK 在项目gradle 添加FB,VK的引用 如果你不需要修改源码 直接导入arr文件 比较方便 repositories { mavenCentral() } dependencies { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,634
精华内容 45,453
关键字:

开源sdk