精华内容
下载资源
问答
  • 美狐美颜sdk源码,基于人脸识别和图像渲染技术,为直播、短视频、自拍机等多种音视频业务场景提供美化方案,包含实时美颜、人脸美型、智能美妆、动态贴纸等功能。多平台适配,接入迅速、支持功能拆分组合,支持离线...
  • 原标题:Android美颜SDK能否占据现有的市场随着华为手机品牌的崛起,Android系统开始受到大众的疯狂信任,同样的Android美颜SDK也随之被更多平台看重。那么按照当下的发展情况看,Android端的美颜SDK能否占据现在的...

    原标题:Android美颜SDK能否占据现有的市场

    随着华为手机品牌的崛起,Android系统开始受到大众的疯狂信任,同样的Android美颜SDK也随之被更多平台看重。那么按照当下的发展情况看,Android端的美颜SDK能否占据现在的市场呢?我们一起来通过文章分析下。

    044c588fce8869043890d18691a73797.pngAndroid美颜SDK

    1.Android系统本身是否存在优势

    如果是根据当前的情况来看,Android系统的使用群体人数比起iOS系统是在逐渐增多的,这说明越来越多的用户开始承认Android系统,当然,作为美颜SDK的服务商来讲,是需要时刻关注用户群体发展动向,以准确地抓住用户需求。

    2.对比iOS系统有什么优势

    美颜SDK是接入到APP中才能使用的,而一般情况下这些APP运营者后期都是会上架应用商店的,如果是涉及到上架方面问题的话,其实很多运营者更倾向于Android系统,因为对于APP的市场监管来看,iOS系统是比较封闭的,上架审核的流程非常复杂,并且还存在一定的风险。所以考虑到APP上架方面的问题,Android美颜SDK接入后使用且上架的优势更明显一些。

    e61386775eb8110119e132f28f8577e8.pngAndroid美颜SDK

    3.价格上是否存在优势

    由于Android和iOS的开发环境和一些语言环境都不相同,所以有很多服务商在开发美颜SDK的时候,需要根据系统不同来进行开发。相比较来看,iOS开发起来更加复杂一些,虽然是比较封闭和单一的系统,但是开发难度上比Android要高的多,所以在价格方面iOS也略高出来不少。不过也有服务商在费用收取的时候,是将两端的产品都包含在一起收费的,其实这样对于一些小规模APP运营者和试运营的APP来说是性价比较高的,至于最终如何选择还是根据实际情况来定的。

    6a8ebfaab1b2bf19e754e912475701b1.pngAndroid美颜SDK

    俗话说“萝卜青菜各有所爱”,美颜SDK也是如此。有的运营者就只想先用Android美颜SDK,实现Android端APP的美颜效果,这并不是不可以实现的。而且随着用户对Android系统的不断了解、认知和喜爱,Android美颜SDK的市场份额也在逐渐加大。

    声明:以上内容均为作者本人原创,转载需注明作者及原文链接,欢迎转载分享。返回搜狐,查看更多

    责任编辑:

    展开全文
  • 开源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

    展开全文
  • Android 视频 美颜SDK对比

    千次阅读 2019-05-15 17:05:57
    Android 视频 美颜SDK对比 开启美颜直播 45分钟测试结果 手机型号 Galaxy S7 MeiZu Pro7 Plus M8 SE 华为 STF-AL00 OPPO R9s CZ YQ601 红米Note 4X 哎呀 发热中等 正常 正常 测试中 测试中 测试中 Y ...

    美颜SDK
    1. 蓝色AR
      优点:文档规范 技术支持及时 价格适中
      缺点:问题较多 包集成偏大

    2. 涂图
      特点:前YY团队成员,10W
      优点:
      缺点:

    3. 商汤
      特点:全身塑型头部肩部腰部臀部腿部塑型以及增高功能。用户可单独使用每种功能
      优点:速度快功能种类多效果稳定性强整体效果逼真自然、算法可自动调节以及拥有多种处理模式
      缺点:价格贵120W?
      客户:
      范例:商汤与映客的合作为例,根据映客直播总监姜春生的介绍,通过商汤的视觉识别解决方案,映客的调帧时长延长了85%,识别时间缩短了31%。用户使用非 ios 系统的情况下,商汤的方案让手机CPU消耗可降低25.6%

    4. 旷视
      特点:泛安防手机等移动端应用

    5. 依图
      特点:AI+医疗

    6. 云从
      特点:金融、银行客户

    7. 拓幻科技
      特点:
      优点:
      缺点:有延时

    8. 相芯:faceunitity
      特点:
      优点:
      缺点:20W

    9. KanKanAi

    10. faceu
      特点:前YY团队成员

    11. 美摄

    12. 无他相机

    13. kiwi

    14. 腾讯优图

    15. 视觉伟业

    16. 海康威视

    17. 汉王科技

    18. 科大讯飞

    美颜的本质
    1. 识别脸部特征

    人脸关键特征点示意图检测抓取,涉及到算法

    • HOG 算法
    • LBF 特征算法(开源OpenCV的算法就是LBF)
    Android 视频 美颜SDK对比

    开启美颜直播 45分钟测试结果

    手机型号Galaxy S7MeiZu Pro7 PlusM8 SE华为 STF-AL00OPPO R9sCZ YQ601红米Note 4X
    哎呀发热中等正常正常测试中正常正常Y
    展开全文
  • 开源IOS 客户端直播SDK自带美颜功能

    千次阅读 2016-08-25 15:30:08
    本文介绍一个:IOS 客户端直播的SDK,代码完全开源。 直播时代:让IOS普通开发者一天内做出一个RTMP直播客户端,并且带有美艳直播功能。(文章最下面有github源码地址) 包含一下功能: 1, 提供IOS苹果手机的...

    当前视频直播非常火爆,手机端的视频直播也非常火爆,PGC、UGC的视频直播门槛都降低了很多。

    本文介绍一个:IOS 客户端直播的SDK,代码完全开源。

    直播时代:让IOS普通开发者一天内做出一个RTMP直播客户端,并且带有美艳直播功能。(文章最下面有github源码地址)

    包含一下功能:

    1, 提供IOS苹果手机的RTMP推流;

    填写RTMP服务地址,直接就可以进行推流。

    2,美颜直播

    美不美都能装的直播,IOS OPENGL美艳加速,手机完全不发烫。

    3,前后摄像头随时切换

    4,提供RTMP连接状态的回调

    一,首先简单扫盲

    当前直播都是用RTMP协议,推流到RTMP服务器

    然后RTMP服务器提供RTMP视频服务;

    当然通常RTMP服务器也提供HLS,把视音频流切片成ts流,然后用户可以直接播放xxx.m3u8的流媒体

     

    二,SDK使用简介

    1,初始化

    [LiveVideoCoreSDK sharedinstance]:全局独立的SDK实例,你不用管理对象的申请和释放;

    初始化RTMP连接和视频参数,非常简单:

    [[LiveVideoCoreSDK sharedinstance] LiveInit:RtmpUrl Preview:self.view VideSize:LIVE_VIEDO_SIZE_CIF BitRate:LIVE_BITRATE_500Kbps FrameRate:LIVE_FRAMERATE_20];

    参数:

    RtmpUrl: rtmp服务器地址参数,如rtmp://192.168.30.191/live/123456

    Preview: IOS本地预览显示的UIVIEW对象,现在都喜欢全屏直播模式,这里直接用self.view

    VideSize: 视频分辨率。这里提供了几种视频分辨率,CIF(320*640),D1(540*960),720P(720*1280)

    BitRate: 视频码率。有500kbps对应CIF,800kbps对应D1, 2Mbps对应720p,怎么配置就看你手机当前的带宽情况了。

    FrameRate: 视频帧率。这个决定视频流畅程度,帧率越高越流畅。这里提供:15帧/s,20帧/s,25帧/s,20帧/s

    2,连接RTMP视频服务器

    [[LiveVideoCoreSDK sharedinstance] connect];

    调用这个接口后,开始打开iphone本地手机预览,并且开始连接RTMP服务器。

    如果网络没有问题,RTMP服务器没有问题,直播就开始了,简单吧!

    3,设置滤镜:提供美艳效果

    - (void)setFilter:(LIVE_FILTER_TYPE) type;

    当前提供了美白效果,设置一个LIVE_FILTER_BEAUTY这个参数,看看你有多美白。

    4,设置前后摄像头

    - (void)setCameraFront:(Boolean)bCameraFrontFlag;

    前后摄像头随时动态切换,想怎么拍就怎么拍。

    5,设置RTMP连接状态回调

    [LiveVideoCoreSDK sharedinstance].delete = self;

    在你的viewcontroller中实现协议:LIVEVCSessionDelegate,如下,这样你可以随时知道RTMP连接的状态了。

    //rtmp status delegate:

    - (void) LiveConnectionStatusChanged: (LIVE_VCSessionState) sessionState{

        dispatch_async(dispatch_get_main_queue(), ^{

            switch (sessionState) {

                case LIVE_VCSessionStatePreviewStarted:

                    _RtmpStatusLabel.text = @"RTMP状态: 预览未连接";

                    break;

                case LIVE_VCSessionStateStarting:

                    _RtmpStatusLabel.text = @"RTMP状态: 连接中...";

                    break;

                case LIVE_VCSessionStateStarted:

                    _RtmpStatusLabel.text = @"RTMP状态: 已连接";

                    break;

                case LIVE_VCSessionStateEnded:

                    _RtmpStatusLabel.text = @"RTMP状态: 未连接";

                    break;

                case LIVE_VCSessionStateError:

                    _RtmpStatusLabel.text = @"RTMP状态: 错误";

                    break;

                default:

                    break;

            }

        });

    }

    6,直播结束,断开rtmp连接

    [[LiveVideoCoreSDK sharedinstance] disconnect];简单吧,不解释了。

     

    GitHub地址: https://github.com/runner365/LiveVideoCoreSDK

    最后,如果觉得代码对你有帮助,请在github中帮助“star”,加个小星星。

    如果有问题,可以在我的问题区提问,欢迎交流

    展开全文
  • 直播SDK如何通过开源直播系统源码加入GPU自定义美颜 了解情况 查了SDK暴露出来的接口,跟系统AVFoundation的回调方法一致,直播SDK提供了Block回调,返回封装好的CMSampleBufferRef数据,然后在Block里把...
  • 现如今,美颜SDK已经成为图片与视频的必备功能,大部分互联网应用都会使用美颜SDK功能。随着短视频开发、直播系统行业的发展,用户对视频拍照不仅仅只满足美白、磨皮、瘦脸等功能了,而是在此基础上增加了一些特效,...
  • 目录 一、相芯基础美颜介绍 ...由于想给录制加上美颜的功能,调研了几种美颜SDK,最后选用了相芯基础美颜。 一、相芯基础美颜介绍 1、有哪些功能? 基础美颜,只有:滤镜、美白红润、磨皮、锐化 几.
  • 本文介绍一个:IOS 客户端直播的SDK,代码完全开源。 直播时代:让IOS普通开发者一天内做出一个RTMP直播客户端,并且带有美艳直播功能。(文章最下面有github源码地址) 包含一下功能: 1, 提供IOS苹果手机的RTMP推...
  • LFRtmp iOS SDK ##一。功能特性1:开源全部代码,未内置任何静态库或者框架2:纯OC实现RTMP推流端协议内容3:AAC音频编码,iphone6s以上采用48k音频采样,iphone6s以下设备采用44k音频采样4:H.264视频编码,iOS8...
  • 一个全开源的纯OC实现的RTMP推流SDK支持AAC、H264、美颜滤镜、AMF编解码
  • 前端开源库-js2sdk

    2019-08-29 18:56:22
    前端开源库-js2sdkjs2sdk,javascript sdk生成器
  • 搭建直播平台中的美颜效果开源实现,从AI到美颜全流程讲解 美颜和短视频 美颜相关APP可以说是现在手机上的必备的软件,例如抖音,快手,拍出的“照骗”和视频不加美颜效果,估计没有人敢传到网上。很多人一直好奇...
  • 短视频美颜SDK接入,占用超低空间,十秒大型场景仅100KB+ 精准人脸识别,动态捕捉最优人脸画面 无限炫酷特效,支持Android、IOS系统。 为了使短视频更加可爱,我们可以给视频人物增加猫耳朵、帽子等特效,如何让相.....
  • 除此之外,里面还包括了人脸识别SDK和戴口罩的人脸识别,里面包含了大量的预训练模型。 开源地址:https://github.com/JDAI-CV/FaceX-Zoo 环境要求 python:3.7.1+ pytorch:1.1.0+ torchvision:0
  • 采集:视频采集的主要采集源:摄像头、屏幕录制、视频文件推流 处理:视频采集后得到原始数据,为了增强一些现场效果,需要在编码前进行处理(logo、美颜、变声) 编码:编码性能、编码速率和编码压缩比直接影响整个...
  • 映客直播资深音视频工程师叶峰峰在LiveVideoStackCon 2018大会的演讲中详细介绍了INKE自研连麦整体设计思路、如何基于WebRTC搭建互动直播SDK以及针对用户体验进行优化。本文由LiveVideoStack整理而成。文 / 叶峰峰...
  • 美颜是在线直播源码很重要的一部分功能,为了保证美颜质量并降低开发时间,在线直播源码会接入第三方美颜SDK,实现系统的美颜功能,接下来就介绍一下,在线直播源码是怎么接入美颜SDK的。 SDK接入流程 1.获取...
  • 当前视频直播非常火爆,手机端的视频直播也非常...本文介绍一个:IOS 客户端直播的SDK,代码完全开源。 直播时代:让IOS普通开发者一天内做出一个RTMP直播客户端,并且带有美艳直播功能。(文章最下面有github源码地址)
  • 秀场直播中,主播可能会在直播前调试美颜滤镜效果,可以同步开启测速。 功能实现: 新增 StartConnectivityTest、StopConnectivityTest 接口,用于 UDP 连通性测试;新增 StartSpeedTest、...
  • 本文介绍一个:IOS 客户端直播的SDK,代码完全开源。 直播时代:让IOS普通开发者一天内做出一个RTMP直播客户端,并且带有美艳直播功能。(文章最下面有github源码地址) 包含一下功能: 1, 提供IOS苹果手机的RTMP推流;...
  • 美颜直播调(20181224)

    千次阅读 2018-12-24 16:26:14
    美颜直播调研(20181224)直播美颜SDK(非开源)1.1 Face++ (旷视科技)美颜直播SDK开源美颜直播1. [VisioninSDK](https://github.com/rexbu/VisioninSDK)2. [Agora]...
  • 实时美颜 滤镜 内置主流滤镜,支持扩展更多滤镜 协议 RTMP协议,支持主流服务器 编码与设置 支持H264及AAC编码;支持智能硬件编码;支持自定义分辨率、码率、帧率 内置伴音 主播可以高质量卡拉OK及喊麦主持 可定制...
  • 前不久,字节跳动的技术团队在Github上开源了一个视频动画特效SDK,可以通过制作Alpha通道分离的视频素材,再在客户端上通过OpenGL ES重新实现Alpha通道和RGB通道的混合,从而实现在端上播放带透明通道的视频。...
  • Android——直播推流SDK

    万次阅读 热门讨论 2018-02-27 19:00:32
    概述: ...现在把我们项目中的直播SDK开源出来,我们是境外直播平台,百万用户,经过半年迭代,SDK已经相当稳定,大家可以放心使用。https://github.com/WangShuo1143368701/WSLiveDemo 这个sdk是...
  • https://github.com/pili-engineering/PLDroidShortVideo
  • 美颜API调研

    千次阅读 2018-12-24 14:56:55
    美颜和滤镜API调研(2081224)1. 美颜和滤镜 API及其效果1.... 开源美颜库 1. 美颜和滤镜 API及其效果 1. Face++ (旷视科技) 美颜API 对图片进行美颜和美白。 测试效果: 2. 开源美颜库 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 868
精华内容 347
关键字:

美颜开源sdk