@Component(modules = {ActivityModule.class})
public interface ActivityComponent {

    Activity getActivity();
} @Module
public class ActivityModule {

    private final Activity activity;
    public ActivityModule(Activity activity){
        this.activity = activity;
    }

    @Provides
    public Activity provideActivity(){
        return activity;
    }



} public class EvilsLiveStreamer {

    private final Context context;
    private final CameraHandlerThread mThread;
    @Inject
    CameraSupport cameraSupport;
    private EvilsLiveStreamerConfig streamerConfig;


    public EvilsLiveStreamer(Context context) {
        this.context = context;
        CameraComponent cameraComponent = CameraComponent.Initializer.init(context);
//
        cameraComponent.inject(this);

        //init default config
        EvilsLiveStreamerConfig.Builder builder = EvilsLiveStreamerConfig.Builder.newBuilder();
        setStreamConfig(builder.build());

        mThread = new CameraHandlerThread("camera thread");
    }

    /**
     * 设置预览界面
     * @param surfaceView view
     */
    public void setDisplayPreview(SurfaceView surfaceView) {
        cameraSupport.setDisplayPreview(surfaceView);
    }

    /**
     * 设置推流配置信息
     * @param config config
     */
    public void setStreamConfig(EvilsLiveStreamerConfig config) {
//        EvilsLiveStreamerConfig.Builder builder = EvilsLiveStreamerConfig.Builder.newBuilder();
        cameraSupport.setStreamConfig(config);
    }


    /**
     * 设置每一帧回调
     * @param listener listener
     */
    public void setOnPreviewFrameListener(OnPreviewFrameListener listener) {
        cameraSupport.setOnPreviewFrameListener(listener);
    }

    /**
     * 开始预览
     */
    public void startPreview() {
        mThread.openCamera(cameraSupport);
    }

    /**
     * 停止预览
     */
    public void stopPreview() {
        cameraSupport.close();
    }

    /**
     * 开始推流
     * @return 开启结果
     */
    public int startStream() {
        return cameraSupport.startPushStream();
    }

    /**
     * 停止推流
     */
    public void stopStream() {
        cameraSupport.stopPushStream();
    }
} public class EvilsLiveStreamerConfig {

    private static final EvilsLiveStreamerConfig DEFAULT_INSTANCE;

    static {
        DEFAULT_INSTANCE = new EvilsLiveStreamerConfig();
    }

    private String streamUrl;

    private Defines.EencodeMethod encodeMethod;

    private Defines.EvideoResolution videoresolution;

    private int imageFormat;
    private int previewFrameRate;
    private int encodeFrameRate;
    private Defines.EcameraFacing cameraFacing;


    private EvilsLiveStreamerConfig(Builder builder) {
        builder(builder);
    }

    private EvilsLiveStreamerConfig() {
        this.streamUrl = "";
        this.videoresolution = E640P;
        this.encodeMethod = Defines.EencodeMethod.SOFTWARE_ENCODE;
        this.imageFormat = ImageFormat.NV21;
        this.previewFrameRate = Defines.DEFAULT_FRAME_RATE;
        this.encodeFrameRate = Defines.DEFAULT_FRAME_RATE;
        this.cameraFacing = Defines.EcameraFacing.CAMERA_FACING_FRONT;
    }

    private void builder(Builder builder) {
        this.streamUrl = builder.streamUrl;
        this.encodeMethod = builder.encodeMethod;
        this.imageFormat = builder.imageFormat;
        this.videoresolution = builder.videoResolution;
        this.previewFrameRate = builder.previewFrameRate;
        this.encodeFrameRate = builder.encodeFrameRate;
        this.cameraFacing = builder.cameraFacing;
    }

    /**
     * 获取图像格式
     * @return
     */
    public int getImageFormat() {
        return imageFormat;
    }

    /**
     * 获取推流url
     * @return url
     */
    public String getStreamUrl() {
        return streamUrl;
    }

    /**
     * 获取视频编码方式
     * @return resolution
     */
    public Defines.EencodeMethod getEncodeMethod() {
        return encodeMethod;
    }


    /**
     * 获取视频分辨率
     * @return resolution
     */
    public Defines.EvideoResolution getVideoResolution() {
        return videoresolution;
    }

    /**
     * 获取预览帧率
     * @return
     */
    public int getPreviewFrameRate() {
        return previewFrameRate;
    }

    /**
     * 获取视频编码帧率
     * @return framerate
     */
    public int getEncodeFrameRate() {
        return encodeFrameRate;
    }

    /**
     * 获取摄像头状态
     * @return 前置/后置
     */
    public Defines.EcameraFacing getCameraFacing() {
        return cameraFacing;
    }

    public static final class Builder {
        private String streamUrl;

        private Defines.EencodeMethod encodeMethod;

        private Defines.EvideoResolution videoResolution;

        private int imageFormat;

        private int previewFrameRate;
        private int encodeFrameRate;
        public Defines.EcameraFacing cameraFacing;

        private Builder() {
        }

        /**
         * 设置推流url
         * @param streamUrl url
         */
        public void setStreamUrl(String streamUrl) {
            this.streamUrl = streamUrl;
        }

        /**
         * 设置视频编码方式
         * @param encodeMethod 软编码/硬编码
         */
        public void setEncodeMethod(Defines.EencodeMethod encodeMethod) {
            this.encodeMethod = encodeMethod;
        }

        /**
         * 设置图像格式
         * @param imageFormat imageFormat
         */
        public void setImageFormat(int imageFormat) {
            this.imageFormat = imageFormat;
        }

        public static Builder newBuilder() {
            return DEFAULT_INSTANCE.toBuilder();
        }

        /**
         * 设置视频预览帧率
         * @param previewFrameRate frameRate
         */
        public void setPreviewFrameRate(int previewFrameRate) {
            this.previewFrameRate = previewFrameRate;
        }

        /**
         * 设置视频编码帧率
         * @param encodeFrameRate frameRate
         */
        public void setEncodeFrameRate(int encodeFrameRate) {
            this.encodeFrameRate = encodeFrameRate;
        }

        /**
         * 设置预览摄像头
         * @param cameraFacing facing
         */
        public void setCameraFacing(Defines.EcameraFacing cameraFacing) {
            this.cameraFacing = cameraFacing;
        }

        /**
         * 设置分辨率
         * @param videoResolution 分辨率
         */
        public void setVideoResolution(Defines.EvideoResolution videoResolution) {
            this.videoResolution = videoResolution;
        }

        public EvilsLiveStreamerConfig build() {
            return new EvilsLiveStreamerConfig(this);
        }

        public Builder mergeFrom(EvilsLiveStreamerConfig config) {
            return null;
        }
    }

    private Builder toBuilder() {
//        return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this);
        Builder builder = new Builder();
        builder.streamUrl = DEFAULT_INSTANCE.streamUrl;
        builder.encodeMethod = DEFAULT_INSTANCE.encodeMethod;
        builder.videoResolution = DEFAULT_INSTANCE.videoresolution;
        builder.imageFormat = DEFAULT_INSTANCE.imageFormat;
        builder.previewFrameRate = DEFAULT_INSTANCE.previewFrameRate;
        builder.encodeFrameRate = DEFAULT_INSTANCE.encodeFrameRate;
        builder.cameraFacing = DEFAULT_INSTANCE.cameraFacing;
        return builder;
    }
} public class EvilsLiveJni {
    static {
        System.loadLibrary("native-lib");
    }

    /**
     * for test
     * @return hello
     */
    public static native String testJni();

    /**
     * 初始化
     */
    public static native void init();

    /**
     * 创建推流上下文
     * @param protocol
     * @return
     */
    public static native int createPushStream(int protocol);

    /**
     * 设置推流配置参数
     * @param index index
     * @param width width
     * @param height height
     * @param framerate framerate
     * @param bitrate bitrate
     * @param forcedI forcedI
     */
    public static native void setStreamConfig(int index, int width, int height, int framerate,
                                              int bitrate, boolean forcedI);

    /**
     * 启动推流
     * @param index index
     * @param url 推流服务器
     * @return 返回启流句柄
     */
    public static native int startPushStream(int index, byte[] url);
    /**
     * 发送一帧数据
     * @param data 一帧数据
     */
    public static native void sendStream(int index, byte[] data, int width, int height);

    /**
     * 停止推流
     * @param hanlder 推流句柄
     */
    public static native void stopPushStream(int hanlder);

    /**
     * 销毁
     */
    public static native void destory();
} public class AudioCapture {
    private static final String TAG = "AudioCapturer";

    private static final int DEFAULT_SOURCE = MediaRecorder.AudioSource.MIC;

    private static final int DEFAULT_SAMPLE_RATE = 44100;

    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;

    private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private static final int TIMER_INTERVAL = 120;

    private AudioRecord mAudioRecord;

    private int mMinBufferSize = 0;

    private Thread mCaptureThread;

    private boolean mIsCaptureStarted = false;

    private volatile boolean mIsLoopExit = false;

    private OnAudioFrameCapturedListener mAudioFrameCapturedListener;


    public interface OnAudioFrameCapturedListener {
        void onAudioFrameCaptured(byte[] audioData);
    }


    public boolean isCaptureStarted() {
        return mIsCaptureStarted;

    }


    public void setOnAudioFrameCapturedListener(OnAudioFrameCapturedListener listener) {
        mAudioFrameCapturedListener = listener;

    }


    public boolean startCapture() {
        return startCapture(DEFAULT_SOURCE, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG,
                DEFAULT_AUDIO_FORMAT);
    }

    private int getMinBufferSize(int sampleRate, int channelConfig, int audioFormat) {
        int numOfChannels, bitsPersample;
        if (channelConfig == AudioFormat.CHANNEL_IN_MONO) {
            numOfChannels = 1;
        } else {
            numOfChannels = 2;
        }
        if (AudioFormat.ENCODING_PCM_16BIT == audioFormat) {
            bitsPersample = 16;
        } else {
            bitsPersample = 8;
        }
        int periodInFrames = sampleRate * TIMER_INTERVAL / 1000;		//num of frames in a second is same as sample rate
        //refer to android/4.1.1/frameworks/av/media/libmedia/AudioRecord.cpp, AudioRecord::getMinFrameCount method
        //we times 2 for ping pong use of record buffer
        mMinBufferSize = periodInFrames * 2  * numOfChannels * bitsPersample / 8;
        if (mMinBufferSize < AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)) {
            // Check to make sure buffer size is not smaller than the smallest allowed one
            mMinBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
            // Set frame period and timer interval accordingly
//            periodInFrames = mMinBufferSize / ( 2 * bitsPersample * numOfChannels / 8 );
        }

        return mMinBufferSize;
    }

    public boolean startCapture(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) {

        if (mIsCaptureStarted) {
            Log.e(TAG, "hujd Capture already started !");
            return false;
        }

        mMinBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);

        if (mMinBufferSize == AudioRecord.ERROR_BAD_VALUE) {
            Log.e(TAG, "hujd Invalid parameter !");
            return false;
        }

        Log.e(TAG, "hujd getMinBufferSize = " + mMinBufferSize + " bytes !");

        mAudioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, mMinBufferSize);

        if (mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
            Log.e(TAG, "hujd AudioRecord initialize fail !");
            return false;
        }

        mAudioRecord.startRecording();


        mIsLoopExit = false;
        mCaptureThread = new Thread(new AudioCaptureRunnable());

        mCaptureThread.start();

        mIsCaptureStarted = true;

        Log.e(TAG, "hujd Start audio capture success !");
        return true;

    }


    public void stopCapture() {
        if (!mIsCaptureStarted) {
            return;
        }

        mIsLoopExit = true;

        try {
            mCaptureThread.interrupt();
            mCaptureThread.join(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
            mAudioRecord.stop();
        }

        mAudioRecord.release();
        mIsCaptureStarted = false;
        mAudioFrameCapturedListener = null;

        Log.d(TAG, "Stop audio capture success !");

    }


    private class AudioCaptureRunnable implements Runnable {
        @Override
        public void run() {
            while (!mIsLoopExit) {
                byte[] buffer = new byte[mMinBufferSize];

                int ret = mAudioRecord.read(buffer, 0, mMinBufferSize);

                if (ret == AudioRecord.ERROR_INVALID_OPERATION) {
                    Log.e(TAG, "Error ERROR_INVALID_OPERATION");
                } else if (ret == AudioRecord.ERROR_BAD_VALUE) {
                    Log.e(TAG, "Error ERROR_BAD_VALUE");
                } else {
                    if (mAudioFrameCapturedListener != null) {
                        mAudioFrameCapturedListener.onAudioFrameCaptured(buffer);
                    }
                    Log.d(TAG, "OK, Captured " + ret + " bytes !");
                }
                SystemClock.sleep(10);
            }

        }

    }
} public class AudioPlayer {
    
    private static final String TAG = "AudioPlayer";

    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
    private static final int DEFAULT_SAMPLE_RATE = 44100;
    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
    private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private static final int DEFAULT_PLAY_MODE = AudioTrack.MODE_STREAM;
            
    private boolean mIsPlayStarted = false;
    private int mMinBufferSize = 0;
    private AudioTrack mAudioTrack;  
    
    public boolean startPlayer() {
        return startPlayer(DEFAULT_STREAM_TYPE,DEFAULT_SAMPLE_RATE,DEFAULT_CHANNEL_CONFIG,DEFAULT_AUDIO_FORMAT);
    }
    
    public boolean startPlayer(int streamType, int sampleRateInHz, int channelConfig, int audioFormat) {
        
        if (mIsPlayStarted) {
            Log.e(TAG, "Player already started !");
            return false;
        }
        
        mMinBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);
        if (mMinBufferSize == AudioTrack.ERROR_BAD_VALUE) {
            Log.e(TAG, "Invalid parameter !");
            return false;
        }
        Log.d(TAG , "getMinBufferSize = "+mMinBufferSize+" bytes !");
        
        mAudioTrack = new AudioTrack(streamType,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize,DEFAULT_PLAY_MODE);
        if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
            Log.e(TAG, "AudioTrack initialize fail !");
            return false;
        }            
        
        mIsPlayStarted = true;
        
        Log.d(TAG, "Start audio player success !");
        
        return true;
    }
    
    public int getMinBufferSize() {
        return mMinBufferSize;
    }
    
    public void stopPlayer() {
        
        if (!mIsPlayStarted) {
            return;
        }
        
        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            mAudioTrack.stop();                        
        }
        
        mAudioTrack.release();
        mIsPlayStarted = false;
           
        Log.d(TAG, "Stop audio player success !");
    }
    
    public boolean play(byte[] audioData, int offsetInBytes, int sizeInBytes) {
        if (!mIsPlayStarted) {
            Log.e(TAG, "Player not started !");
            return false;
        }

        Log.e(TAG, "hujd onAudioFrameCaptured: " + sizeInBytes + " min:" + mMinBufferSize);

        if (sizeInBytes < mMinBufferSize) {
            Log.e(TAG, "audio data is not enough !");
            return false;
        }
        
        if (mAudioTrack.write(audioData,offsetInBytes,sizeInBytes) != sizeInBytes) {                
            Log.e(TAG, "Could not write all the samples to the audio device !");
        }                                   
                                                   
        mAudioTrack.play();
        
        Log.d(TAG , "OK, Played "+sizeInBytes+" bytes !");
        
        return true;
    }
} public interface CameraSupport {

    /**
     * 打开Camera
     * @return Camera实例
     */
    CameraSupport open();

    /**
     * 开始推流
     * @return err: 小于0 成功：0
     */
    int startPushStream();
    /**
     * 获取Camera 方向
     * @param cameraId 前置或后置
     */
    int getOrientation(int cameraId);

    /**
     * 设置预览View
     * @param surfaceView view
     */
    void setDisplayPreview(SurfaceView surfaceView);

    /**
     * 设置推流预览回调
     * @param listener listener
     */
    void setOnPreviewFrameListener(OnPreviewFrameListener listener);

    /**
     * 设置推流配置
     * @param config config
     */
    void setStreamConfig(EvilsLiveStreamerConfig config);

    /**
     * 停止推流
     * @return err：小于0 成功：0
     */
    void stopPushStream();

    /**
     * 关闭 camera
     */
    void close();
} public class Defines {
    public static final int DEFAULT_FRAME_RATE = 15;

    public enum EcameraFacing {
        CAMERA_FACING_FRONT(1),
        CAMERA_FACING_BACK(0),
        ;
        public final int index;

        EcameraFacing(int index) {
            this.index = index;
        }
    }
    public enum EencodeMethod {
        SOFTWARE_ENCODE,
        HARDWARE_ENCODE,
        ;
    }

    public enum EvideoResolution {
        E360P(480, 360),
        E640P(640, 480),
        E480P(858, 480),
        E540P(960, 540),
        E720P(1280, 720),
        ;
        private final int width;
        private final int height;

        public int getWidth() {
            return width;
        }

        public int getHeight() {
            return height;
        }

        EvideoResolution(int width, int height) {
            this.width = width;
            this.height = height;
        }
    }

} public class LogUtils {
    private static final int LEVEL = Log.DEBUG;

    private static boolean isDebug(int level) {
        return level >= LogUtils.LEVEL && AppUtils.isDebug();
    }

    private static void println(String tag, int level, String msg) {
        if (isDebug(level)) {
            Log.println(level, tag, msg);
        }
    }

    public static void d(String tag, String msg) {
        println(tag, Log.DEBUG, msg);
    }

    public static void i(String tag, String msg) {
        println(tag, Log.INFO, msg);
    }

    public static void w(String tag, String msg) {
        println(tag, Log.WARN, msg);
    }

    public static void e(String tag, String msg) {
        println(tag, Log.ERROR, msg);
    }

}
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.thinkkeep.evils.di.component.ActivityComponent;
import com.thinkkeep.evils.di.component.DaggerActivityComponent;
import com.thinkkeep.evils.di.modules.ActivityModule;
import com.thinkkeep.videolib.api.EvilsLiveStreamer;
import com.thinkkeep.videolib.api.EvilsLiveStreamerConfig;
import com.thinkkeep.videolib.jni.EvilsLiveJni;
import com.thinkkeep.videolib.model.audio.AudioCapture;
import com.thinkkeep.videolib.model.audio.AudioPlayer;
import com.thinkkeep.videolib.model.video.CameraSupport;
import com.thinkkeep.videolib.util.Defines;
import com.thinkkeep.videolib.util.LogUtils;
import javax.inject.Inject;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
package com.thinkkeep.evils;






public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener, SurfaceHolder.Callback {

    private static final int REQUEST_CODE = 1;
    @Inject
    CameraSupport cameraSupport;

    @Bind(R.id.surface_view)
    SurfaceView mPreviewView;

    private EvilsLiveStreamer streamer;

    private static final String TAG = "MainActivity";

    private int cameraId = 0;

    // 所需的全部权限
    static final String[] PERMISSIONS = new String[]{
            Manifest.permission.CAMERA,
    };

    private PermissionsChecker mPermissionsChecker;

    @Bind(R.id.toolbar) Toolbar mToolbar;
    @Bind(R.id.fab) FloatingActionButton mFab;
    @Bind(R.id.switch_camera) Button mSwitchBtn;
    @Bind(R.id.start) Button startBtn;
    @Bind(R.id.edit_str) EditText urlEdit;
    @Bind(R.id.audioBtn) Button audioBtn;

    private boolean isStartStream = true;
    private EvilsLiveStreamerConfig.Builder builder;
    private AudioCapture audioCapture;
    private AudioPlayer audioPlay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setSupportActionBar(mToolbar);

        mPermissionsChecker = new PermissionsChecker(this);

        streamer = new EvilsLiveStreamer(this);

//        ViewGroup.LayoutParams layoutParams = mPreviewView.getLayoutParams();
//        layoutParams.width = 640;
//        layoutParams.height = 480;
//        mPreviewView.setLayoutParams(layoutParams);
        SurfaceHolder holder = mPreviewView.getHolder();
        holder.addCallback(this);

        audioCapture = new AudioCapture();
        audioPlay = new AudioPlayer();

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .activityModule(new ActivityModule(this)).build();
        activityComponent.getActivity();

//        CameraComponent cameraComponent = DaggerCameraComponent.builder().activityComponent(activityComponent).
//                cameraModule(new com.thinkkeep.videolib.di.modules.CameraModule(this)).build();
//        cameraComponent.inject(this);
    }

    @OnClick(R.id.switch_camera)
    void onClickSwitchBtn() {
        cameraId = 1 - cameraId;
        Defines.EcameraFacing facing;