parent
b2ec6c8e55
commit
c42c84a8a1
@ -0,0 +1,342 @@
|
|||||||
|
package net.ossrs.yasea;
|
||||||
|
|
||||||
|
import android.graphics.ImageFormat;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.media.AudioFormat;
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
|
||||||
|
import net.ossrs.yasea.rtmp.RtmpPublisher;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Leo Ma on 2016/7/25.
|
||||||
|
*/
|
||||||
|
public class SrsPublisher implements SurfaceHolder.Callback, Camera.PreviewCallback {
|
||||||
|
private static final String TAG = "SrsPublisher";
|
||||||
|
|
||||||
|
private AudioRecord mic;
|
||||||
|
private boolean aloop = false;
|
||||||
|
private Thread aworker;
|
||||||
|
|
||||||
|
private SurfaceView mCameraView;
|
||||||
|
private Camera mCamera;
|
||||||
|
|
||||||
|
private int videoFrameCount;
|
||||||
|
private long lastTimeMillis;
|
||||||
|
private int mPreviewRotation = 90;
|
||||||
|
private int mCamId = Camera.getNumberOfCameras() - 1;
|
||||||
|
private double mSamplingFps;
|
||||||
|
private byte[] mYuvPreviewFrame;
|
||||||
|
|
||||||
|
private SrsFlvMuxer mFlvMuxer;
|
||||||
|
private SrsMp4Muxer mMp4Muxer;
|
||||||
|
private SrsEncoder mEncoder = new SrsEncoder();
|
||||||
|
|
||||||
|
public SrsPublisher() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startEncode() {
|
||||||
|
if (!mEncoder.start()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startCamera();
|
||||||
|
|
||||||
|
aworker = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||||
|
startAudio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
aloop = true;
|
||||||
|
aworker.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopEncode() {
|
||||||
|
stopAudio();
|
||||||
|
stopCamera();
|
||||||
|
mEncoder.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startPublish(String rtmpUrl) {
|
||||||
|
if (mFlvMuxer != null) {
|
||||||
|
try {
|
||||||
|
mFlvMuxer.start(rtmpUrl);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mFlvMuxer.setVideoResolution(mEncoder.getOutputWidth(), mEncoder.getOutputHeight());
|
||||||
|
|
||||||
|
startEncode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopPublish() {
|
||||||
|
if (mFlvMuxer != null) {
|
||||||
|
stopEncode();
|
||||||
|
mFlvMuxer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startRecord(String recPath) {
|
||||||
|
if (mMp4Muxer != null) {
|
||||||
|
try {
|
||||||
|
mMp4Muxer.record(new File(recPath));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopRecord() {
|
||||||
|
if (mMp4Muxer != null) {
|
||||||
|
mMp4Muxer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pauseRecord() {
|
||||||
|
if (mMp4Muxer != null) {
|
||||||
|
mMp4Muxer.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resumeRecord() {
|
||||||
|
if (mMp4Muxer != null) {
|
||||||
|
mMp4Muxer.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swithToSoftEncoder() {
|
||||||
|
mEncoder.swithToSoftEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swithToHardEncoder() {
|
||||||
|
mEncoder.swithToHardEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSoftEncoder() {
|
||||||
|
return mEncoder.isSoftEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getmSamplingFps() {
|
||||||
|
return mSamplingFps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCamraId() {
|
||||||
|
return mCamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfCameras() {
|
||||||
|
return mCamera != null ? mCamera.getNumberOfCameras() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviewResolution(int width, int height) {
|
||||||
|
mEncoder.setPreviewResolution(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputResolution(int width, int height) {
|
||||||
|
mEncoder.setPortraitResolution(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScreenOrientation(int orientation) {
|
||||||
|
mEncoder.setScreenOrientation(orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviewRotation(int rotation) {
|
||||||
|
mPreviewRotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoHDMode() {
|
||||||
|
mEncoder.setVideoHDMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoSmoothMode() {
|
||||||
|
mEncoder.setVideoSmoothMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchCameraFace(int id) {
|
||||||
|
mCamId = id;
|
||||||
|
stopCamera();
|
||||||
|
mEncoder.swithCameraFace();
|
||||||
|
startCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startCamera() {
|
||||||
|
if (mCamera != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mCamId > (Camera.getNumberOfCameras() - 1) || mCamId < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCamera = Camera.open(mCamId);
|
||||||
|
|
||||||
|
Camera.Parameters params = mCamera.getParameters();
|
||||||
|
Camera.Size size = mCamera.new Size(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());
|
||||||
|
if (!params.getSupportedPreviewSizes().contains(size) || !params.getSupportedPictureSizes().contains(size)) {
|
||||||
|
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(),
|
||||||
|
new IllegalArgumentException(String.format("Unsupported resolution %dx%d", size.width, size.height)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mYuvPreviewFrame = new byte[mEncoder.getPreviewWidth() * mEncoder.getPreviewHeight() * 3 / 2];
|
||||||
|
|
||||||
|
/***** set parameters *****/
|
||||||
|
//params.set("orientation", "portrait");
|
||||||
|
//params.set("orientation", "landscape");
|
||||||
|
//params.setRotation(90);
|
||||||
|
params.setPictureSize(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());
|
||||||
|
params.setPreviewSize(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());
|
||||||
|
int[] range = findClosestFpsRange(SrsEncoder.VFPS, params.getSupportedPreviewFpsRange());
|
||||||
|
params.setPreviewFpsRange(range[0], range[1]);
|
||||||
|
params.setPreviewFormat(ImageFormat.NV21);
|
||||||
|
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
||||||
|
params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
|
||||||
|
params.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
|
||||||
|
if (!params.getSupportedFocusModes().isEmpty()) {
|
||||||
|
params.setFocusMode(params.getSupportedFocusModes().get(0));
|
||||||
|
}
|
||||||
|
mCamera.setParameters(params);
|
||||||
|
|
||||||
|
mCamera.setDisplayOrientation(mPreviewRotation);
|
||||||
|
|
||||||
|
mCamera.addCallbackBuffer(mYuvPreviewFrame);
|
||||||
|
mCamera.setPreviewCallbackWithBuffer(this);
|
||||||
|
try {
|
||||||
|
if (mCameraView != null){
|
||||||
|
mCamera.setPreviewDisplay(mCameraView.getHolder());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
mCamera.startPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] findClosestFpsRange(int expectedFps, List<int[]> fpsRanges) {
|
||||||
|
expectedFps *= 1000;
|
||||||
|
int[] closestRange = fpsRanges.get(0);
|
||||||
|
int measure = Math.abs(closestRange[0] - expectedFps) + Math.abs(closestRange[1] - expectedFps);
|
||||||
|
for (int[] range : fpsRanges) {
|
||||||
|
if (range[0] <= expectedFps && range[1] >= expectedFps) {
|
||||||
|
int curMeasure = Math.abs(range[0] - expectedFps) + Math.abs(range[1] - expectedFps);
|
||||||
|
if (curMeasure < measure) {
|
||||||
|
closestRange = range;
|
||||||
|
measure = curMeasure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopCamera() {
|
||||||
|
if (mCamera != null) {
|
||||||
|
// need to SET NULL CB before stop preview!!!
|
||||||
|
mCamera.setPreviewCallback(null);
|
||||||
|
mCamera.stopPreview();
|
||||||
|
mCamera.release();
|
||||||
|
mCamera = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGetYuvFrame(byte[] data) {
|
||||||
|
// Calculate YUV sampling FPS
|
||||||
|
if (videoFrameCount == 0) {
|
||||||
|
lastTimeMillis = System.nanoTime() / 1000000;
|
||||||
|
videoFrameCount++;
|
||||||
|
} else {
|
||||||
|
if (++videoFrameCount >= 48) {
|
||||||
|
long diffTimeMillis = System.nanoTime() / 1000000 - lastTimeMillis;
|
||||||
|
mSamplingFps = (double) videoFrameCount * 1000 / diffTimeMillis;
|
||||||
|
videoFrameCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mEncoder.onGetYuvFrame(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAudio() {
|
||||||
|
if (mic != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bufferSize = 2 * AudioRecord.getMinBufferSize(SrsEncoder.ASAMPLERATE, SrsEncoder.ACHANNEL, AudioFormat.ENCODING_PCM_16BIT);
|
||||||
|
mic = new AudioRecord(MediaRecorder.AudioSource.MIC, SrsEncoder.ASAMPLERATE, SrsEncoder.ACHANNEL, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
|
||||||
|
mic.startRecording();
|
||||||
|
|
||||||
|
byte pcmBuffer[] = new byte[4096];
|
||||||
|
while (aloop && !Thread.interrupted()) {
|
||||||
|
int size = mic.read(pcmBuffer, 0, pcmBuffer.length);
|
||||||
|
if (size <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mEncoder.onGetPcmFrame(pcmBuffer, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAudio() {
|
||||||
|
aloop = false;
|
||||||
|
if (aworker != null) {
|
||||||
|
aworker.interrupt();
|
||||||
|
try {
|
||||||
|
aworker.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
aworker.interrupt();
|
||||||
|
}
|
||||||
|
aworker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mic != null) {
|
||||||
|
mic.setRecordPositionUpdateListener(null);
|
||||||
|
mic.stop();
|
||||||
|
mic.release();
|
||||||
|
mic = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublishEventHandler(RtmpPublisher.EventHandler handler) {
|
||||||
|
mFlvMuxer = new SrsFlvMuxer(handler);
|
||||||
|
mEncoder.setFlvMuxer(mFlvMuxer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordEventHandler(SrsMp4Muxer.EventHandler handler) {
|
||||||
|
mMp4Muxer = new SrsMp4Muxer(handler);
|
||||||
|
mEncoder.setMp4Muxer(mMp4Muxer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSurfaceView(SurfaceView surfaceView) {
|
||||||
|
mCameraView = surfaceView;
|
||||||
|
mCameraView.getHolder().addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||||
|
onGetYuvFrame(data);
|
||||||
|
camera.addCallbackBuffer(mYuvPreviewFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder arg0) {
|
||||||
|
if (mCamera != null) {
|
||||||
|
try {
|
||||||
|
mCamera.setPreviewDisplay(mCameraView.getHolder());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder arg0) {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue