您的足迹:首页 > Android >七牛直播SDK Android试练 1

七牛直播SDK Android试练 1

现在直播挺火的,也有很多成熟的SDK

百度,阿里,腾讯等等都有自己的SDK

我今天演示的是七牛SDK,为什么选用七牛?

在很久以前云存储,图片处理的时候就看上了七牛,做的真不错,而且有免费额度,所以选择七牛。


介绍下直播的架构:




主要分为四部分:

  • 业务服务器
    负责协调直播类应用的业务逻辑,包括但不限于:
    • 创建直播房间
    • 返回直播房间播放地址列表
    • 关闭直播房间
  • LiveNet 实时流网络
    负责流媒体的分发、直播流的创建、查询等相关操作
  • 采集端
    负责采集和推送流媒体
  • 播放端
    负责拉取并播放流媒体


简单点说,就是 采集端(比如一个主播拿着手机进行直播)开始采集视频,然后实时传递给LiveNet 实时流网络,这个过程可称为 RTMP推流。

然后播放端(观看直播的吃瓜群众)连接LiveNet 实时流网络 进行播放视频流。

业务服务器呢,就是提供一些诸如登录注册,主播创建房间等等的操作。




1.配置直播空间

 登录七牛


选择直播云服务,新建直播空间


    

注意这里的域名要填写 自己购买的域名,并且这个域名还要备案完成。推荐购买域名和备案使用阿里云:https://wanwang.aliyun.com/domain/

下一步

这里可以选择直接重新建立一个存储空间,这个空间是用来存放直播视频切片的。


下一步

直播封面功能可以打开。 其他的可以暂时都不用填写。


下一步·创建直播空间


创建好之后,注意下红色部分的域名,需要进行CNAME,CNAME是干什么呢?这里我简要说一下 ,假如你购买的域名叫  abc.com  那么把 blog.abc.com 这个二级域名CNAME 成:www.baidu.com , 那么在浏览器 访问 blog.abc.com的时候,会转而访问到 百度,所以这里七牛让我们CNAME域名的意思就是说,直播的域名都可以使用我们自己的域名,但是其实功能是七牛帮我们做的直播功能,我们在访问我们的直播域名的时候,其实是转到七牛的服务器来处理。  那么CNAME怎么来操作呢?上面图片里面有个查看如何CNAME,其实就是在 我们的域名注册商(比如我们刚刚推荐使用阿里云购买域名,那么就去阿里云后台)那里进行配置。


我这里上一张我CNAME的示例图:


进入直播空间

如果CNAME没有完成的话,情况如下:


CNAME成功之后如下:              就可以开发了


2.Android 端推流开发

    所谓推流就是android手机采集视频上传到 LiveNet 实时流网络

        这是七牛的推流SDK文档: https://developer.qiniu.com/pili/sdk/the-sdk-android-push-flow照着上面写竟然不行,坑爹啊,问了客服竟然是过时了。推流端SDK地址是https://github.com/pili-engineering/PLDroidMediaStreaming 这上面的快速开发指南也是过时的。下载的demo虽然能用,但是对于新手来讲特别不友好,代码冗余,逻辑混乱。

        下面是整理的从0开始搭建的推流app

        首先是下载SDK,主要的就是jar包和so 库。下载的文件如下:

        

(1)首先是新建工程,然后在工程里导入jar 包

     ① 复制进libs文件夹。

    


    ②然后右击jar包,选择Add As Library,然后确定

      

        


(2)添加so库

        在 app/src/main 目录下创建 jniLibs 目录。然后复制进去

        


  (3)添加权限

    <uses-permission android:id="android.permission.INTERNET" />
    <uses-permission android:id="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:id="android.permission.RECORD_AUDIO" />
    <uses-permission android:id="android.permission.CAMERA" />
    <uses-permission android:id="android.permission.WAKE_LOCK" />
    <uses-feature android:id="android.hardware.camera.autofocus" />
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
    <uses-permission android:id="android.permission.ACCESS_NETWORK_STATE"/>

(4)添加happy-dns依赖

        在 app 目录下的 build.gradle ,并且按照如下修改:

        

     主要是添加了这2句:

compile 'com.qiniu:happy-dns:0.2.+'
compile 'com.qiniu.pili:pili-android-qos:0.8.16'


  (5)实现自己的Application     


public class StreamingApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        StreamingEnv.init(getApplicationContext());
    }
}


 记得在AndroidManifest文件里把application的name修改下:

     

 (6)在MainActivity里面 获取RTMP推流地址

     推流地址就是 指把手机采集的视频发送到那里的服务器地址

     这个时候需要服务器配合,因为这个推流地址是需要使用 服务端SDK生成的。

     生成推流地址的过程就相当于  主播创建房间,准备开直播

      这里我给出服务端PHP sdk生成 参考代码:

       

 七牛直播PHP sdk连接:https://github.com/pili-engineering/pili-sdk-php.v2 

这样我们就可以用类似于 这样的地址  http://www.a1anwang.com/getRTMP.php访问服务器来获取 推流地址(就跟登录注册一样)。

有些人可能会说:老子又不是搞服务器的,这么复杂

好消息就是,如果我们不会搭建服务器,那么我们可以借助 七牛的控制台来完成服务器的生成推流地址等工作

  如下:

   

点击直播流管理 里面的 添加流

添加好之后,


在流属性里面即可看到 推流地址,这个是和之前讲的使用PHP SDK生成的地址是一样的。

(7)获取到推流地址之后,开始推流

     上一步我们在MainActivity里面获取了推流地址,那么我们获取到推流地址之后 点击开始直播按钮,进入直播页面StreamingActivity

     如果我们不会搭建服务器而是使用的控制台生成推流地址的话,那么我们可以这样,在 assets文件夹里创建一个txt文件,把控制台的推流地址放进去,然后在推流页面StreamingActivity里面读取出来,为什么要用文件来存储这个定死的推流地址,我直接用一个string 固定不就行了吗?

     行,当然行,使用String 双引号里面需要注意字符转义,我是怕你们搞错了,使用文件不会有这个问题。如下:

     

      

而且这样有一个好处,因为推流地址是有 时效性的,所以经常会过期,修改的话,我们只需要直接修改这个文件即可,如果使用String变量的话,还得再次注意转义字符。

     

public class MUtils {
    public static String loadAssets(Context context, String fileName) {
        try {
            InputStreamReader inputReader = new InputStreamReader(
                    context.getResources().getAssets().open(fileName));
            BufferedReader bufReader = new BufferedReader(inputReader);
            String line = "";
            String Result = "";
            while ((line = bufReader.readLine()) != null)
                Result += line;
            return Result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}



    新建StreamingActivity ,完整的StreamingActivity代码如下:

package com.a1anwang.freelive;

import android.hardware.Camera;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.qiniu.android.dns.DnsManager;
import com.qiniu.android.dns.IResolver;
import com.qiniu.android.dns.NetworkInfo;
import com.qiniu.android.dns.http.DnspodFree;
import com.qiniu.android.dns.local.AndroidDnsServer;
import com.qiniu.android.dns.local.Resolver;
import com.qiniu.pili.droid.streaming.AVCodecType;
import com.qiniu.pili.droid.streaming.CameraStreamingSetting;
import com.qiniu.pili.droid.streaming.MediaStreamingManager;
import com.qiniu.pili.droid.streaming.MicrophoneStreamingSetting;
import com.qiniu.pili.droid.streaming.StreamingProfile;
import com.qiniu.pili.droid.streaming.StreamingState;
import com.qiniu.pili.droid.streaming.StreamingStateChangedListener;
import com.qiniu.pili.droid.streaming.widget.AspectFrameLayout;

import java.io.IOException;
import java.net.InetAddress;
import java.net.URISyntaxException;

/**
 * Created by Alan on 2017/3/1.
 */

public class StreamingActivity extends AppCompatActivity implements StreamingStateChangedListener  {
    private String TAG="StreamingActivity";

    StreamingProfile mProfile;
    private int mCurrentCamFacingIndex;
    CameraStreamingSetting mCameraStreamingSetting;
    MicrophoneStreamingSetting mMicrophoneStreamingSetting;
    protected MediaStreamingManager mMediaStreamingManager;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_streaming);

        String rtmpurl=MUtils.loadAssets(this,"rtmpurl.txt");

        mProfile = new StreamingProfile();
        try {
            mProfile.setPublishUrl(rtmpurl);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        StreamingProfile.AudioProfile aProfile = new StreamingProfile.AudioProfile(44100, 96 * 1024);
        StreamingProfile.VideoProfile vProfile = new StreamingProfile.VideoProfile(30, 1000 * 1024, 48);
        StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);

        mProfile.setVideoQuality(StreamingProfile.VIDEO_QUALITY_MEDIUM2)
                .setAudioQuality(StreamingProfile.AUDIO_QUALITY_MEDIUM2)
//                .setAVProfile(avProfile)
//                .setPreferredVideoEncodingSize(960, 544)
                .setEncodingSizeLevel(StreamingProfile.VIDEO_ENCODING_HEIGHT_480)
                .setEncoderRCMode(StreamingProfile.EncoderRCModes.BITRATE_PRIORITY)
                .setDnsManager(getMyDnsManager())
                .setAdaptiveBitrateEnable(true)
                .setFpsControllerEnable(true)
                .setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3))
//                .setEncodingOrientation(StreamingProfile.ENCODING_ORIENTATION.PORT)
                .setSendingBufferProfile(new StreamingProfile.SendingBufferProfile(0.2f, 0.8f, 3.0f, 20 * 1000));



        CameraStreamingSetting.CAMERA_FACING_ID cameraFacingId = chooseCameraFacingId();
        mCurrentCamFacingIndex = cameraFacingId.ordinal();
        mCameraStreamingSetting = new CameraStreamingSetting();
        mCameraStreamingSetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT)
                .setContinuousFocusModeEnabled(true)
                .setRecordingHint(false)
                .setCameraFacingId(cameraFacingId)
//                .setCameraSourceImproved(true)
                .setResetTouchFocusDelayInMs(3000)
//                .setFocusMode(CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_PICTURE)
                .setCameraPrvSizeLevel(CameraStreamingSetting.PREVIEW_SIZE_LEVEL.MEDIUM)
                .setCameraPrvSizeRatio(CameraStreamingSetting.PREVIEW_SIZE_RATIO.RATIO_16_9)
                .setBuiltInFaceBeautyEnabled(true)
                .setFaceBeautySetting(new CameraStreamingSetting.FaceBeautySetting(1.0f, 1.0f, 0.8f))
                .setVideoFilter(CameraStreamingSetting.VIDEO_FILTER_TYPE.VIDEO_FILTER_BEAUTY);


        mMicrophoneStreamingSetting = new MicrophoneStreamingSetting();
        mMicrophoneStreamingSetting.setBluetoothSCOEnabled(false);


        AspectFrameLayout afl = (AspectFrameLayout) findViewById(R.id.cameraPreview_afl);
        afl.setShowMode(AspectFrameLayout.SHOW_MODE.REAL);
        GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.cameraPreview_surfaceView);


//        WatermarkSetting watermarksetting = new WatermarkSetting(this);
//        watermarksetting.setResourceId(R.drawable.ic_launcher)
//                .setAlpha(100)
//                .setSize(WatermarkSetting.WATERMARK_SIZE.MEDIUM)
//                .setCustomPosition(0.5f, 0.5f);

        mMediaStreamingManager = new MediaStreamingManager(this, afl, glSurfaceView,
                AVCodecType.SW_VIDEO_WITH_SW_AUDIO_CODEC); // sw codec

//        mMediaStreamingManager.prepare(mCameraStreamingSetting, mMicrophoneStreamingSetting, watermarksetting, mProfile, new PreviewAppearance(0.0f, 0.0f, 0.5f, 0.5f, PreviewAppearance.ScaleType.FIT));
        //mMediaStreamingManager.prepare(mCameraStreamingSetting, mMicrophoneStreamingSetting,watermarksetting, mProfile);
        mMediaStreamingManager.prepare(mCameraStreamingSetting, mMicrophoneStreamingSetting, mProfile);

        mMediaStreamingManager.setStreamingStateListener(this);
//        mMediaStreamingManager.setSurfaceTextureCallback(this);
//        mMediaStreamingManager.setStreamingSessionListener(this);
//        mMediaStreamingManager.setNativeLoggingEnabled(false);
//        mMediaStreamingManager.setStreamStatusCallback(this);
//        mMediaStreamingManager.setAudioSourceCallback(this);
        // update the StreamingProfile
//        mProfile.setStream(new Stream(mJSONObject1));
//        mMediaStreamingManager.setStreamingProfile(mProfile);
    }
    @Override
    protected void onResume() {
        super.onResume();
        mMediaStreamingManager.resume();
    }
    @Override
    protected void onPause() {
        super.onPause();

        mMediaStreamingManager.pause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMediaStreamingManager.destroy();
    }

    private static DnsManager getMyDnsManager() {
        IResolver r0 = new DnspodFree();
        IResolver r1 = AndroidDnsServer.defaultResolver();
        IResolver r2 = null;
        try {
            r2 = new Resolver(InetAddress.getByName("119.29.29.29"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return new DnsManager(NetworkInfo.normal, new IResolver[]{r0, r1, r2});
    }
    private CameraStreamingSetting.CAMERA_FACING_ID chooseCameraFacingId() {
        if (CameraStreamingSetting.hasCameraFacing(CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_3RD)) {
            return CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_3RD;
        } else if (CameraStreamingSetting.hasCameraFacing(CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_FRONT)) {
            return CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_FRONT;
        } else {
            return CameraStreamingSetting.CAMERA_FACING_ID.CAMERA_FACING_BACK;
        }
    }

    @Override
    public void onStateChanged(StreamingState streamingState, Object o) {
        Log.e(TAG, "StreamingState  :" + streamingState  );
        switch (streamingState){
            case READY:

                ToastUtils.showToast(StreamingActivity.this,"ready",2000);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        if (mMediaStreamingManager != null) {
                            mMediaStreamingManager.startStreaming();
                        }
                    }
                }).start();
                break;


        }
    }

}


布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    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"
    >

    <com.qiniu.pili.droid.streaming.widget.AspectFrameLayout
        android:id="@+id/cameraPreview_afl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal" >

        <android.opengl.GLSurfaceView
            android:id="@+id/cameraPreview_surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center" />


    </com.qiniu.pili.droid.streaming.widget.AspectFrameLayout>
</RelativeLayout>
这个时候进入 StreamingActivity就已经在推流了, 播放端可以看到直播了, 那么如何验证呢?


我们可以使用能播放rtmp流的播放器来验证一下

我用的是PotPlayer(win7)(当然用手机上的支持rtmp的app也可以):

  

这个 播放链接填写什么呢?填写的是RTMP播放地址, 这个在正式环境中也是通过服务端SDK (PHP SDK)来获取的

依然地,我们可以在控制台得到:



这样,就可以使用播放器进行播放了, 我们注意到,还有HLS,FLV的播放形式,有兴趣的可以试下

播放画面如下




好,到了这里我们已经完成了基础版的推流APP了,直播的时候还有很多其他设置,比如美颜,镜像,水印等等等等。

当然啦,有兴趣的同学,还可以在我们自己的网站上实现直播功能如下:

这个原理就是在网页上使用了一个支持rtmp播放的播放器


这样是不是已经基本搭建出一个自己的直播平台了呢?


请期待下一篇




本博客所有文章如无特别注明均为原创。作者:AlanWang复制或转载请以超链接形式注明转自 AlanWang的博客-专注android和蓝牙BLE技术分享
原文地址《七牛直播SDK Android试练 1

相关推荐

发表评论

路人甲 表情
看不清楚?点图切换 Ctrl+Enter快速提交

网友评论(0)