安卓 视频直播二:推流端代码

版权声明:有需要的请联系QQ1634475153,欢迎技术交流 https://blog.csdn.net/jinmie0193/article/details/83386824

想要从底层一步步写起比较麻烦,需要了解一点图像处理的知识,为了快速开发,我选择通过第三方的SDK,这里简单说一下第三方SDK,其中有腾讯,阿里,百度云,网易,金山云,抖音,大牛都支持不过各有利弊。

(1)腾讯云ILVB实名认证后需要人工审核5个工作日,反正至今没有看到SDK;

(2)阿里云提供多媒体云服务,但是至今尚未提供移动直播SDK;

(3)百度云接口还很粗糙,连移动直播必选的美颜功能都不支持,首先淘汰;

(4)网易云相比腾讯和阿里好一点,不过大部分功能受限制,文档比较老,而且会有推销电话

(5)金山云支持自定义音频数据处理,可以把自己的噪声抑制代码挂载进去;

(6)抖音偏重的是视频处理,而不是直播

(7)大牛相比其他几个算是最好用的,接口全面,文档清晰。

注:能完全运行起来的是金山云、百度云,大牛提供的SDK,而且除大牛外其他都需要账号注册。

SDK筛选方案:http://www.cctime.com/html/2016-6-6/1179900.htm

大牛Git:https://github.com/daniulive/SmarterStreaming

一、大牛相关文档

1. 说明文档

移动端SDK(V2)调用说明(更新于2018/10/21)

 Windows SDK说明(以C#为例)

windows/android/iOS播放器SDK(V2)Unity3D调用说明

2.Demo下载

 [Windows demo测试程序] Windows推送、播放、合成、导播、连麦Demo(64位)本地下载(更新于2018/10/16)

 [Android SDK demo工程代码] android推送、播放、转发、一对一互动、后台推摄像头/屏幕Demo(V2接口,建议采用)(Android Studio工程)(更新于2018/10/18)

 [iOS SDK demo工程代码] iOS推送、播放、转发、录屏SDK(V2)本地下载(更新于2018/10/24)

二、android 编码

1.系统要求

  • SDK 支持 Android 4.4 及以上版本;
  • 支持的 CPU 架构:armv7, arm64。

2.   准备工作

  • 确保 SmartPublisherJniV2.java 放到 com.daniulive.smartpublisher 包名下(可下载Demo,在其中找到);
  • Smartavengine.jar 加入到工程;
  • 拷贝 SmartPublisherV2\app\src\main\jniLibs\armeabi-v7a 和SmartPublisherV2\app\src\main\jniLibs\arm64-v8a 下 libSmartPublisher.so 到工程;

添加相关权限:

<uses-permission  android :name=" "android.permission.WRITE_EXTERNAL_STORAGE" ></ uses-permission>
<uses-permission  android :name=" "android.permission.INTERNET" ></ uses-permission>
<uses-permission  android :name=" "android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission  android :name=" "android.permission.MODIFY_AUDIO_SETTINGS" />
  • Load 库:
static {
    System.loadLibrary("SmartPublisher");
}
  • build.gradle 配置 32/64 位库:
splits {
    abi {
        enable  true
        reset()

        // Specifies a list of ABIs that Gradle should create APKs for
        //include "armeabi"
        include , 'armeabi-v7a',  'arm64-v8a' //select ABIs to build APKs for
        // Specify that we do not want to also generate a universal APK that includes all ABIs
        universalApk  true
    }
}
  • 如需集成到自己系统测试,请用大牛直播 SDK 的 app name(不然集成提示 license failed),正式授权版按照授权 app name 正常使用即可:
  • 如何改 app-name:
    strings.xml 做以下修改:
    <string name="app_name">SmartPublisherSDKDemo</string>

3.SDK接口详解

移动端SDK(V2)调用说明(更新于2018/10/21)

4.代码

XML

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.liang.beautifulphoto.Daniulive.CameraPushActivity">

    <SurfaceView
        android:id="@+id/cpush_sf_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/cpush_btn_switch"
        android:layout_width="55dp"
        android:layout_height="55dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintVertical_bias="0.03"
        android:layout_marginRight="15dp"
        android:background="@drawable/switch_facing_button_list"/>

    <Button
        android:id="@+id/cpush_btn_push"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="推流"
        android:textColor="@color/white"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintVertical_bias="0.8"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintHorizontal_bias="0.05"
        android:background="#50000000" />

    <Button
        android:id="@+id/cpush_btn_url"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="输入推流URL"
        android:textColor="@color/white"
        app:layout_constraintLeft_toRightOf="@id/cpush_btn_push"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintTop_toTopOf="@id/cpush_btn_push"
        android:layout_marginRight="10dp"
        android:layout_marginLeft="10dp"
        android:background="#50000000"/>

    <TextView
        android:id="@+id/cpush_tv_url"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="URL:rtmp://player.daniulive.com:1935/hls/stream"
        android:textColor="@color/white"
        android:textAllCaps="false"
        android:layout_marginTop="10dp"
        app:layout_constraintLeft_toLeftOf="@id/cpush_btn_push"
        app:layout_constraintTop_toBottomOf="@id/cpush_btn_push"/>
</android.support.constraint.ConstraintLayout>

activity:

package com.liang.beautifulphoto.Daniulive;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.daniulive.smartpublisher.SmartPublisherJniV2;
import com.daniulive.smartpublisher.SmartPublisherJniV2.WATERMARK;
import com.eventhandle.NTSmartEventCallbackV2;
import com.eventhandle.NTSmartEventID;
import com.liang.beautifulphoto.R;
import com.voiceengine.NTAudioRecordV2;
import com.voiceengine.NTAudioRecordV2Callback;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import superClass.superActivity;
import superInterface.myInterface;

public class CameraPushActivity extends superActivity implements myInterface, Callback, Camera.PreviewCallback {

    private static String TAG = "LIANG-CameraPushActivity";

    /* 推流分辨率选择
     * 0: 640*480
     * 1: 320*240
     * 2: 176*144
     * 3: 1280*720
     * */
    private int videoWidth = 1280;
    private int videoHight = 720;

    /* 推送类型选择
     * 0: 音视频
     * 1: 纯音频
     * 2: 纯视频
     * */
    //private Spinner pushTypeSelector;
    private int pushType = 0;

    /* 水印类型选择
     * 0: 图片水印
     * 1: 全部水印
     * 2: 文字水印
     * 3: 不加水印
     * */
    //private Spinner watermarkSelctor;
    private int watemarkType = 3;

    /* video软编码profile设置
     * 1: baseline profile
     * 2: main profile
     * 3: high profile
     * */
    //private Spinner swVideoEncoderProfileSelector;
    private int sw_video_encoder_profile = 1;    //default with baseline profile

    //编码速度
    private int sw_video_encoder_speed = 6;

    private Button   switchFaceButton;
    private Button   pushButton;
    private Button   urlButon;
    private TextView curUrlTView;

    private SurfaceView   mSurfaceView   = null;
    private SurfaceHolder mSurfaceHolder = null;

    private Camera                   mCamera             = null;
    private Camera.AutoFocusCallback myAutoFocusCallback = null;

    //Daniu
    private SmartPublisherJniV2 libPublisher = null;
    NTAudioRecordV2         audioRecord_         = null;
    NTAudioRecordV2Callback audioRecordCallback_ = null;

    //标志
    private static final int     FRONT                   = 1;               //前置摄像头标记
    private static final int     BACK                    = 2;                 //后置摄像头标记
    private              int     currentCameraType       = BACK;             //当前打开的摄像头标记
    private              int     curCameraIndex          = -1;                   //当前摄像头
    private              boolean mPreviewRunning         = false;         //正在录制标志
    private static final int     PORTRAIT                = 1;    //竖屏
    private static final int     LANDSCAPE               = 2;    //横屏 home键在右边的情况
    private static final int     LANDSCAPE_LEFT_HOME_KEY = 3; // 横屏 home键在左边的情况
    private              int     currentOrigentation     = PORTRAIT;
    private              boolean is_speex                = true;                  //启用语音引擎
    private              boolean is_noise_suppression    = true;    //降噪处理
    private              boolean is_agc                  = false;                   //AGC自动发电量控制

    private boolean isStart     = false;
    private boolean isPushing   = false;
    private boolean isRecording = false;

    private boolean is_hardware_encoder = false;

    //字符串
    private String publishURL;
    final private String baseURL      = "rtmp://player.daniulive.com:1935/hls/stream";
    private       String inputPushURL = "";
    private       String printText    = "URL:";
    private       String curState     = "当前状态";

    //水印
    final private String  logoPath               = "/sdcard/daniulivelogo.png";
    private       boolean isWritelogoFileSuccess = false;

    private long publisherHandle = 0;
    private int  frameCount      = 0;

    private Context myContext;

    //Load 库
    static {
        System.loadLibrary("SmartPublisher");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //屏幕常亮
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_camera_push);

        myContext = this.getApplicationContext();
        getPermission();
        initView();
        initEvent();

    }

    @Override
    public void initView() {
        switchFaceButton = (Button) findViewById(R.id.cpush_btn_switch);
        pushButton = (Button) findViewById(R.id.cpush_btn_push);
        urlButon = (Button) findViewById(R.id.cpush_btn_url);
        curUrlTView = (TextView) findViewById(R.id.cpush_tv_url);

        mSurfaceView = (SurfaceView) findViewById(R.id.cpush_sf_preview);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        //自动聚焦变量回调
        myAutoFocusCallback = new Camera.AutoFocusCallback() {
            public void onAutoFocus(boolean success, Camera camera) {
                if (success)//success表示对焦成功
                {
                    Log.e(TAG, "onAutoFocus succeed...");
                } else {
                    Log.e(TAG, "onAutoFocus failed...");
                }
            }
        };

        libPublisher = new SmartPublisherJniV2();
    }

    @Override
    public void initEvent() {
        switchFaceButton.setOnClickListener(new SwitchCameraListener());
        pushButton.setOnClickListener(new ButtonStartPushListener());
        urlButon.setOnClickListener(new ButtonInputPushUrlListener());
    }

    /**
     * 动态权限,安卓6.0后申请动态权限
     */
    private void getPermission() {
        if (Build.VERSION.SDK_INT >= 23) {
            //申请运行时权限
            List<String> permissionList = new ArrayList<>();
            if (ContextCompat.checkSelfPermission(CameraPushActivity.this, Manifest.permission.RECORD_AUDIO)
                    != PackageManager.PERMISSION_GRANTED) {
                //录制声音的权限
                permissionList.add(Manifest.permission.RECORD_AUDIO);
            }
            if (ContextCompat.checkSelfPermission(CameraPushActivity.this, Manifest.permission.CAMERA)
                    != PackageManager.PERMISSION_GRANTED) {
                //使用摄像头的权限
                permissionList.add(Manifest.permission.CAMERA);
            }
            if (ContextCompat.checkSelfPermission(CameraPushActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                //外部存储器的权限
                permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }

            if (!permissionList.isEmpty()) {
                String[] permisssion = permissionList.toArray(new String[permissionList.size()]);
                ActivityCompat.requestPermissions(this, permisssion, 1);//申请
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int result : grantResults) {
                        if (result != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this, "必须同意所有权限才可使用本程序", Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                } else {
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        frameCount++;
        if (frameCount % 3000 == 0) {
            Log.i("OnPre", "gc+");
            System.gc();
            Log.i("OnPre", "gc-");
        }

        if (data == null) {
            Camera.Parameters params = camera.getParameters();
            Camera.Size size = params.getPreviewSize();
            int bufferSize = (((size.width | 0x1f) + 1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;
            camera.addCallbackBuffer(new byte[bufferSize]);
        } else {
            if (isStart || isPushing || isRecording) {
                libPublisher.SmartPublisherOnCaptureVideoData(publisherHandle, data, data.length, currentCameraType, currentOrigentation);
            }

            camera.addCallbackBuffer(data);
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        Log.e(TAG, "surfaceCreated: ");
        try {

            int CammeraIndex = findBackCamera();
            Log.e(TAG, "BackCamera: " + CammeraIndex);

            if (CammeraIndex == -1) {
                CammeraIndex = findFrontCamera();
                currentCameraType = FRONT;
                switchFaceButton.setEnabled(false);
                if (CammeraIndex == -1) {
                    Log.e(TAG, "NO camera!!");
                    return;
                }
            } else {
                currentCameraType = BACK;
            }

            if (mCamera == null) {
                mCamera = openCamera(currentCameraType);
            }

        } catch (Exception e) {
            //e.printStackTrace();
            Log.e(TAG, "surfaceCreated: Exception");
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        Log.e(TAG, "surfaceChanged: ");
        initCamera(surfaceHolder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }

    /*****************自定义方法******************/
    /*it will call when surfaceChanged*/
    private void initCamera(SurfaceHolder holder) {
        Log.e(TAG, "initCamera..");

        if (mPreviewRunning) {
            mCamera.stopPreview();
        }

        Camera.Parameters parameters;
        try {
            parameters = mCamera.getParameters();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }

        parameters.setPreviewSize(videoWidth, videoHight);
        parameters.setPictureFormat(PixelFormat.JPEG);
        parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);

        SetCameraFPS(parameters);

        setCameraDisplayOrientation(this, curCameraIndex, mCamera);

        mCamera.setParameters(parameters);

        int bufferSize = (((videoWidth | 0xf) + 1) * videoHight * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat())) / 8;

        mCamera.addCallbackBuffer(new byte[bufferSize]);

        mCamera.setPreviewCallbackWithBuffer(this);
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (Exception ex) {
            // TODO Auto-generated catch block
            if (null != mCamera) {
                mCamera.release();
                mCamera = null;
            }
            ex.printStackTrace();
        }
        mCamera.startPreview();
        mCamera.autoFocus(myAutoFocusCallback);
        mPreviewRunning = true;
    }

    //检查它是否有后置摄像头
    private int findBackCamera() {
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();

        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                return camIdx;
            }
        }
        return -1;
    }

    //检查它是否有前摄像头
    private int findFrontCamera() {
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();

        for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                return camIdx;
            }
        }
        return -1;
    }

    //打开摄像头
    @SuppressLint("NewApi")
    private Camera openCamera(int type) {
        int frontIndex = -1;
        int backIndex = -1;
        int cameraCount = Camera.getNumberOfCameras();
        Log.e(TAG, "cameraCount: " + cameraCount);

        Camera.CameraInfo info = new Camera.CameraInfo();
        for (int cameraIndex = 0; cameraIndex < cameraCount; cameraIndex++) {
            Camera.getCameraInfo(cameraIndex, info);

            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                frontIndex = cameraIndex;
            } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                backIndex = cameraIndex;
            }
        }

        currentCameraType = type;
        if (type == FRONT && frontIndex != -1) {
            curCameraIndex = frontIndex;
            return Camera.open(frontIndex);
        } else if (type == BACK && backIndex != -1) {
            curCameraIndex = backIndex;
            return Camera.open(backIndex);
        }
        return null;
    }

    //切换摄像头
    private void switchCamera() throws IOException {
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();
        if (currentCameraType == FRONT) {
            mCamera = openCamera(BACK);
        } else if (currentCameraType == BACK) {
            mCamera = openCamera(FRONT);
        }

        initCamera(mSurfaceHolder);
    }

    //设置FPS
    private void SetCameraFPS(Camera.Parameters parameters) {
        if (parameters == null) {
            return;
        }

        int[] findRange = null;

        int defFPS = 20 * 1000;

        List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
        if (fpsList != null && fpsList.size() > 0) {
            for (int i = 0; i < fpsList.size(); ++i) {
                int[] range = fpsList.get(i);
                if (range != null
                        && Camera.Parameters.PREVIEW_FPS_MIN_INDEX < range.length
                        && Camera.Parameters.PREVIEW_FPS_MAX_INDEX < range.length) {
                    Log.e(TAG, "Camera index:" + i + " support min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);

                    Log.e(TAG, "Camera index:" + i + " support max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);

                    if (findRange == null) {
                        if (defFPS <= range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
                            findRange = range;

                            Log.e(TAG, "Camera found appropriate fps, min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
                                    + " ,max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
                        }
                    }
                }
            }
        }
    }

    //设置方向
    private void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }

        Log.e(TAG, "curDegree: " + result);

        camera.setDisplayOrientation(result);
    }

    private void stopPush() {
        if (!isRecording) {
            if (audioRecord_ != null) {
                Log.e(TAG, "stopPush, call audioRecord_.StopRecording..");


                audioRecord_.Stop();

                if (audioRecordCallback_ != null) {
                    audioRecord_.RemoveCallback(audioRecordCallback_);
                    audioRecordCallback_ = null;
                }

                audioRecord_ = null;

                //audioRecord_.StopRecording();
                //audioRecord_ = null;
            }
        }

        if (libPublisher != null) {
            libPublisher.SmartPublisherStopPublisher(publisherHandle);
        }

        if (!isRecording) {
            if (publisherHandle != 0) {
                if (libPublisher != null) {
                    libPublisher.SmartPublisherClose(publisherHandle);
                    publisherHandle = 0;
                }
            }
        }

    }

    private void ConfigControlEnable(boolean isEnable) {
    }

    //这里硬编码码率是按照25帧来计算的
    private int setHardwareEncoderKbps(int width, int height) {
        int hwEncoderKpbs = 0;

        int area = width * height;

        if (area < (200 * 180)) {
            hwEncoderKpbs = 300;
        } else if (area < (400 * 320)) {
            hwEncoderKpbs = 600;
        } else if (area < (640 * 500)) {
            hwEncoderKpbs = 1200;
        } else if (area < (960 * 600)) {
            hwEncoderKpbs = 1500;
        } else if (area < (1300 * 720)) {
            hwEncoderKpbs = 2000;
        } else if (area < (2000 * 1080)) {
            hwEncoderKpbs = 3000;
        } else {
            hwEncoderKpbs = 4000;
        }

        return hwEncoderKpbs;
    }

    private void InitAndSetConfig() {
        Log.e(TAG, "videoWidth: " + videoWidth + " videoHight: " + videoHight
                + " pushType:" + pushType);

        int audio_opt = 1;
        int video_opt = 1;

        if (pushType == 1) {
            video_opt = 0;
        } else if (pushType == 2) {
            audio_opt = 0;
        }

        publisherHandle = libPublisher.SmartPublisherOpen(myContext, audio_opt, video_opt,
                videoWidth, videoHight);

        if (publisherHandle == 0) {
            Log.e(TAG, "sdk open failed!");
            return;
        }

        Log.e(TAG, "publisherHandle=" + publisherHandle);

        if (is_hardware_encoder) {
            int hwHWKbps = setHardwareEncoderKbps(videoWidth, videoHight);

            Log.e(TAG, "hwHWKbps: " + hwHWKbps);

            int isSupportHWEncoder = libPublisher
                    .SetSmartPublisherVideoHWEncoder(publisherHandle, hwHWKbps);

            if (isSupportHWEncoder == 0) {
                Log.e(TAG, "Great, it supports hardware encoder!");
            }
        }

        libPublisher.SetSmartPublisherEventCallbackV2(publisherHandle, new EventHandeV2());

        // 如果想和时间显示在同一行,请去掉'\n'
        String watermarkText = "大牛直播(daniulive)\n\n";


        String path = logoPath;

        if (watemarkType == 0) {
            if (isWritelogoFileSuccess) {
                libPublisher.SmartPublisherSetPictureWatermark(publisherHandle, path,
                        WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160,
                        160, 10, 10);
            }

        } else if (watemarkType == 1) {
            if (isWritelogoFileSuccess) {
                libPublisher.SmartPublisherSetPictureWatermark(publisherHandle, path,
                        WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160,
                        160, 10, 10);
            }

            libPublisher.SmartPublisherSetTextWatermark(publisherHandle, watermarkText, 1,
                    WATERMARK.WATERMARK_FONTSIZE_BIG,
                    WATERMARK.WATERMARK_POSITION_BOTTOMRIGHT, 10, 10);

            // libPublisher.SmartPublisherSetTextWatermarkFontFileName("/system/fonts/DroidSansFallback.ttf");

            // libPublisher.SmartPublisherSetTextWatermarkFontFileName("/sdcard/DroidSansFallback.ttf");
        } else if (watemarkType == 2) {
            libPublisher.SmartPublisherSetTextWatermark(publisherHandle, watermarkText, 1,
                    WATERMARK.WATERMARK_FONTSIZE_BIG,
                    WATERMARK.WATERMARK_POSITION_BOTTOMRIGHT, 10, 10);

            // libPublisher.SmartPublisherSetTextWatermarkFontFileName("/system/fonts/DroidSansFallback.ttf");
        } else {
            Log.e(TAG, "no watermark settings..");
        }
        // end

        if (!is_speex) {
            // set AAC encoder
            libPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 1);
        } else {
            // set Speex encoder
            libPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 2);
            libPublisher.SmartPublisherSetSpeexEncoderQuality(publisherHandle, 8);
        }

        libPublisher.SmartPublisherSetNoiseSuppression(publisherHandle, is_noise_suppression ? 1
                : 0);

        libPublisher.SmartPublisherSetAGC(publisherHandle, is_agc ? 1 : 0);

        // libPublisher.SmartPublisherSetClippingMode(publisherHandle, 0);

        libPublisher.SmartPublisherSetSWVideoEncoderProfile(publisherHandle, sw_video_encoder_profile);

        libPublisher.SmartPublisherSetSWVideoEncoderSpeed(publisherHandle, sw_video_encoder_speed);

        // libPublisher.SetRtmpPublishingType(publisherHandle, 0);

        // libPublisher.SmartPublisherSetGopInterval(publisherHandle, 40);

        // libPublisher.SmartPublisherSetFPS(publisherHandle, 15);

        // libPublisher.SmartPublisherSetSWVideoBitRate(publisherHandle, 600, 1200);

        libPublisher.SmartPublisherSaveImageFlag(publisherHandle, 1);

    }

    void CheckInitAudioRecorder() {
        if (audioRecord_ == null) {
            //audioRecord_ = new NTAudioRecord(this, 1);

            audioRecord_ = new NTAudioRecordV2(this);
        }

        if (audioRecord_ != null) {
            Log.e(TAG, "CheckInitAudioRecorder call audioRecord_.start()+++...");

            audioRecordCallback_ = new NTAudioRecordV2CallbackImpl();

            audioRecord_.AddCallback(audioRecordCallback_);

            audioRecord_.Start();

            Log.e(TAG, "CheckInitAudioRecorder call audioRecord_.start()---...");


            //Log.e(TAG, "onCreate, call executeAudioRecordMethod..");
            // auido_ret: 0 ok, other failed
            //int auido_ret= audioRecord_.executeAudioRecordMethod();
            //Log.e(TAG, "onCreate, call executeAudioRecordMethod.. auido_ret=" + auido_ret);
        }
    }

    private void PopInputUrlDialog() {
        final EditText inputUrlTxt = new EditText(this);
        inputUrlTxt.setFocusable(true);
        inputUrlTxt.setText(baseURL + String.valueOf((int) (System.currentTimeMillis() % 1000000)));

        AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
        builderUrl.setTitle("如 rtmp://player.daniulive.com:1935/hls/stream123456").setView(inputUrlTxt).setNegativeButton(
                "取消", null);

        builderUrl.setPositiveButton("确认", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                String fullPushUrl = inputUrlTxt.getText().toString();
                SaveInputUrl(fullPushUrl);
            }
        });

        builderUrl.show();
    }

    private void SaveInputUrl(String url) {
        inputPushURL = "";

        if (url == null) {
            return;
        }

        // rtmp://
        if (url.length() < 8) {
            Log.e(TAG, "Input publish url error:" + url);
            return;
        }

        if (!url.startsWith("rtmp://")) {
            Log.e(TAG, "Input publish url error:" + url);
            return;
        }

        inputPushURL = url;

        Log.e(TAG, "Input publish url:" + url);
    }

    /***************内部类*********************/
    //切换摄像头
    class SwitchCameraListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            Log.e(TAG, "Switch camera..");
            try {
                switchCamera();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    //推流
    class ButtonStartPushListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            if (isStart) {
                return;
            }

            if (isPushing) {
                stopPush();

                if (!isRecording) {
                    ConfigControlEnable(true);
                }

                pushButton.setText(" 推送");

                isPushing = false;

                return;
            }

            Log.e(TAG, "onClick start push..");

            if (libPublisher == null) {
                return;
            }

            isPushing = true;

            if (!isRecording) {
                InitAndSetConfig();
            }

            if (inputPushURL != null && inputPushURL.length() > 1) {
                publishURL = inputPushURL;
                Log.e(TAG, "start, input publish url:" + publishURL);
            } else {
                publishURL = baseURL + String.valueOf((int) (System.currentTimeMillis() % 1000000));
                Log.e(TAG, "start, generate random url:" + publishURL);
            }

            printText = "URL:" + publishURL;
            Log.e(TAG, printText);

            if (libPublisher.SmartPublisherSetURL(publisherHandle, publishURL) != 0) {
                Log.e(TAG, "Failed to set publish stream URL..");
            }

            int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);
            if (startRet != 0) {
                isPushing = false;

                Log.e(TAG, "Failed to start push stream..");
                return;
            }

            if (!isRecording) {
                if (pushType == 0 || pushType == 1) {
                    CheckInitAudioRecorder();    //enable pure video publisher..
                }
            }

            if (!isRecording) {
                ConfigControlEnable(false);
            }

            curUrlTView = (TextView) findViewById(R.id.cpush_tv_url);
            curUrlTView.setText(printText);

            pushButton.setText(" 停止推送 ");

        }

    }

    //状态
    class EventHandeV2 implements NTSmartEventCallbackV2 {
        @Override
        public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

            Log.e(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

            switch (id) {
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
                    curState = "开始。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
                    curState = "连接中。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
                    curState = "连接失败。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
                    curState = "连接成功。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
                    curState = "连接断开。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
                    curState = "关闭。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
                    Log.e(TAG, "开始一个新的录像文件 : " + param3);
                    curState = "开始一个新的录像文件。。";
                    break;
                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
                    Log.e(TAG, "已生成一个录像文件 : " + param3);
                    curState = "已生成一个录像文件。。";
                    break;

                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
                    Log.e(TAG, "发送时延: " + param1 + " 帧数:" + param2);
                    curState = "收到发送时延..";
                    break;

                case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
                    Log.e(TAG, "快照: " + param1 + " 路径:" + param3);

                    if (param1 == 0) {
                        curState = "截取快照成功。.";
                    } else {
                        curState = "截取快照失败。.";
                    }
                    break;
                default:
            }

            String str = "当前回调状态:" + curState;

            Log.e(TAG, str);

        }
    }

    class NTAudioRecordV2CallbackImpl implements NTAudioRecordV2Callback {
        @Override
        public void onNTAudioRecordV2Frame(ByteBuffer data, int size, int sampleRate, int channel, int per_channel_sample_number) {
    		 /*
    		 Log.e(TAG, "onNTAudioRecordV2Frame size=" + size + " sampleRate=" + sampleRate + " channel=" + channel
    				 + " per_channel_sample_number=" + per_channel_sample_number);

    		 */

            if (publisherHandle != 0) {
                libPublisher.SmartPublisherOnPCMData(publisherHandle, data, size, sampleRate, channel, per_channel_sample_number);
            }
        }
    }

    //推流URL
    class ButtonInputPushUrlListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            PopInputUrlDialog();
        }
    }

}

猜你喜欢

转载自blog.csdn.net/jinmie0193/article/details/83386824