/*
* CameraPublishActivity.java
* CameraPublishActivity
*
* Github: https://github.com/daniulive/SmarterStreaming
*
* Created by DaniuLive on 2015/09/20.
* Copyright © 2014~2016 DaniuLive. All rights reserved.
*/
package com.daniulive.smartpublisher;
import com.daniulive.smartpublisher.SmartPublisherJni.WATERMARK;
import com.eventhandle.SmartEventCallback;
import com.voiceengine.NTAudioRecord; //for audio capture..
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.hardware.Camera.AutoFocusCallback;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@SuppressWarnings("deprecation")
public class CameraPublishActivity extends Activity implements Callback, PreviewCallback
{
private static String TAG = "SmartPublisher";
NTAudioRecord audioRecord_ = null; //for audio capture
private TextView textCurURL = null;
private SmartPublisherJni libPublisher = null;
/* 推送类型选择
* 0: 音视频
* 1: 纯音频
* 2: 纯视频
* */
private Spinner pushTypeSelector;
private int pushType = 0;
/* 水印类型选择
* 0: 图片水印
* 1: 全部水印
* 2: 文字水印
* 3: 不加水印
* */
private Spinner watermarkSelctor;
private int watemarkType = 0;
/* 推流分辨率选择
* 0: 640*480
* 1: 320*240
* 2: 176*144
* 3: 1280*720
* */
private Spinner resolutionSelector;
/* 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 Spinner recorderSelector;
private Button btnRecoderMgr;
private Button btnNoiseSuppression;
private Button btnAGC;
private Button btnSpeex;
private Button btnMute;
private Button btnMirror;
private Spinner swVideoEncoderSpeedSelector;
private Button btnHWencoder;
private ImageView imgSwitchCamera;
private Button btnInputPushUrl;
private Button btnStartStop;
private Button btnStartPush;
private Button btnStartRecorder;
private SurfaceView mSurfaceView = null;
private SurfaceHolder mSurfaceHolder = null;
private Camera mCamera = null;
private AutoFocusCallback myAutoFocusCallback = null;
private boolean mPreviewRunning = false;
private boolean isStart = false;
private boolean isPushing = false;
private boolean isRecording = false;
final private String logoPath = "/sdcard/daniulivelogo.png";
private boolean isWritelogoFileSuccess = false;
private String publishURL;
final private String baseURL = "rtmp://player.daniulive.com:1935/hls/stream";
private String inputPushURL ="";
private String printText = "URL:";
private String txt = "当前状态";
private static final int FRONT = 1; //前置摄像头标记
private static final int BACK = 2; //后置摄像头标记
private int currentCameraType = BACK; //当前打开的摄像头标记
private static final int PORTRAIT = 1; //竖屏
private static final int LANDSCAPE = 2; //横屏
private int currentOrigentation = PORTRAIT;
private int curCameraIndex = -1;
private int videoWidth = 640;
private int videoHight = 480;
private int frameCount = 0;
private String recDir = "/sdcard/daniulive/rec"; //for recorder path
private boolean is_need_local_recorder = false; // do not enable recorder in default
private boolean is_noise_suppression = true;
private boolean is_agc = false;
private boolean is_speex = false;
private boolean is_mute = false;
private boolean is_mirror = false;
private int sw_video_encoder_speed = 6;
private boolean is_hardware_encoder = false;
private Context myContext;
static {
System.loadLibrary("SmartPublisher");
}
private byte[] ReadAssetFileDataToByte(InputStream in) throws IOException
{
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
int c = 0;
while ( (c = in.read()) != -1 )
{
bytestream.write(c);
}
byte bytedata[] = bytestream.toByteArray();
bytestream.close();
return bytedata;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
Log.i(TAG, "onCreate..");
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); //屏幕常亮
setContentView(R.layout.activity_main);
myContext = this.getApplicationContext();
try {
InputStream logo_input_stream = getClass().getResourceAsStream(
"/assets/logo.png");
byte[] logo_data = ReadAssetFileDataToByte(logo_input_stream);
if (logo_data != null)
{
try
{
FileOutputStream out = new FileOutputStream(logoPath);
out.write(logo_data);
out.close();
isWritelogoFileSuccess = true;
} catch (Exception e)
{
e.printStackTrace();
Log.e(TAG, "write logo file to /sdcard/ failed");
}
}
}
catch (Exception e)
{
e.printStackTrace();
Log.e(TAG, "write logo file to /sdcard/ failed");
}
//push type, audio/video/audio&video
pushTypeSelector = (Spinner)findViewById(R.id.pushTypeSelctor);
final String []types = new String[]{"音视频", "纯音频", "纯视频"};
ArrayAdapter<String> adapterType = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, types);
adapterType.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
pushTypeSelector.setAdapter(adapterType);
pushTypeSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if(isStart || isPushing || isRecording )
{
Log.e(TAG, "Could not switch push type during publishing..");
return;
}
pushType = position;
Log.i(TAG, "[推送类型]Currently choosing: " + types[position] + ", pushType: " + pushType);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
//end
//水印
watermarkSelctor = (Spinner)findViewById(R.id.watermarkSelctor);
final String []watermarks = new String[]{"图片水印", "全部水印", "文字水印", "不加水印"};
ArrayAdapter<String> adapterWatermark = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, watermarks);
adapterWatermark.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
watermarkSelctor.setAdapter(adapterWatermark);
watermarkSelctor.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if(isStart || isPushing || isRecording )
{
Log.e(TAG, "Could not switch water type during publishing..");
return;
}
watemarkType = position;
Log.i(TAG, "[水印类型]Currently choosing: " + watermarks[position] + ", watemarkType: " + watemarkType);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
//end
resolutionSelector = (Spinner)findViewById(R.id.resolutionSelctor);
final String []resolutionSel = new String[]{"高分辨率", "中分辨率", "低分辨率", "超高分辨率"};
ArrayAdapter<String> adapterResolution = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, resolutionSel);
adapterResolution.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
resolutionSelector.setAdapter(adapterResolution);
resolutionSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if(isStart|| isPushing || isRecording)
{
Log.e(TAG, "Could not switch resolution during publishing..");
return;
}
Log.i(TAG, "[推送分辨率]Currently choosing: " + resolutionSel[position]);
SwitchResolution(position);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
swVideoEncoderProfileSelector = (Spinner)findViewById(R.id.swVideoEncoderProfileSelector);
final String []profileSel = new String[]{"BaseLineProfile", "MainProfile", "HighProfile"};
ArrayAdapter<String> adapterProfile = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, profileSel);
adapterProfile.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
swVideoEncoderProfileSelector.setAdapter(adapterProfile);
swVideoEncoderProfileSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if(isStart|| isPushing || isRecording)
{
Log.e(TAG, "Could not switch video profile during publishing..");
return;
}
Log.i(TAG, "[VideoProfile]Currently choosing: " + profileSel[position]);
sw_video_encoder_profile = position + 1;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
//Recorder related settings
recorderSelector = (Spinner)findViewById(R.id.recoder_selctor);
final String []recoderSel = new String[]{"本地不录像", "本地录像"};
ArrayAdapter<String> adapterRecoder = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, recoderSel);
adapterRecoder.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
recorderSelector.setAdapter(adapterRecoder);
recorderSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
Log.i(TAG, "Currently choosing: " + recoderSel[position]);
if ( 1 == position )
{
is_need_local_recorder = true;
}
else
{
is_need_local_recorder = false;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
btnRecoderMgr = (Button)findViewById(R.id.button_recoder_manage);
btnRecoderMgr.setOnClickListener(new ButtonRecorderMangerListener());
//end
btnNoiseSuppression = (Button)findViewById(R.id.button_noise_suppression);
btnNoiseSuppression.setOnClickListener(new ButtonNoiseSuppressionListener());
btnAGC = (Button)findViewById(R.id.button_agc);
btnAGC.setOnClickListener(new ButtonAGCListener());
btnSpeex = (Button)findViewById(R.id.button_speex);
btnSpeex.setOnClickListener(new ButtonSpeexListener());
btnMute = (Button)findViewById(R.id.button_mute);
btnMute.setOnClickListener(new ButtonMuteListener());
btnMirror = (Button)findViewById(R.id.button_mirror);
btnMirror.setOnClickListener(new ButtonMirrorListener());
swVideoEncoderSpeedSelector = (Spinner)findViewById(R.id.sw_video_encoder_speed_selctor);
final String [] video_encoder_speed_Sel = new String[]{"6", "5", "4", "3", "2", "1"};
ArrayAdapter<String> adapterVideoEncoderSpeed = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, video_encoder_speed_Sel);
adapterVideoEncoderSpeed.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
swVideoEncoderSpeedSelector.setAdapter(adapterVideoEncoderSpeed);
swVideoEncoderSpeedSelector.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id)
{
Log.i(TAG, "Currently speed choosing: " + video_encoder_speed_Sel[position]);
sw_video_encoder_speed = 6 - position;
Log.i(TAG, "Choose speed=" + sw_video_encoder_speed);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
btnHWencoder = (Button)findViewById(R.id.button_hwencoder);
btnHWencoder.setOnClickListener(new ButtonHardwareEncoderListener());
textCurURL = (TextView)findViewById(R.id.txtCurURL);
textCurURL.setText(printText);
btnInputPushUrl =(Button)findViewById(R.id.button_input_push_url);
btnInputPushUrl.setOnClickListener(new ButtonInputPushUrlListener());
btnStartStop = (Button)findViewById(R.id.button_start_stop);
btnStartStop.setOnClickListener(new ButtonStartListener());
btnStartPush = (Button)findViewById(R.id.button_start_push);
btnStartPush.setOnClickListener(new ButtonStartPushListener());
btnStartRecorder = (Button)findViewById(R.id.button_start_recorder);
btnStartRecorder.setOnClickListener(new ButtonStartRecorderListener());
imgSwitchCamera = (ImageView)findViewById(R.id.button_switchCamera);
imgSwitchCamera.setOnClickListener(new SwitchCameraListener());
mSurfaceView = (SurfaceView) this.findViewById(R.id.surface);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//自动聚焦变量回调
myAutoFocusCallback = new AutoFocusCallback()
{
public void onAutoFocus(boolean success, Camera camera) {
if(success)//success表示对焦成功
{
Log.i(TAG, "onAutoFocus succeed...");
}
else
{
Log.i(TAG, "onAutoFocus failed...");
}
}
};
libPublisher = new SmartPublisherJni();
}
class SwitchCameraListener implements OnClickListener
{
public void onClick(View v)
{
Log.i(TAG, "Switch camera..");
try {
switchCamera();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
void SwitchResolution(int position)
{
Log.i(TAG, "Current Resolution position: " + position);
switch(position) {
case 0:
videoWidth = 640;
videoHight = 480;
break;
case 1:
videoWidth = 320;
videoHight = 240;
break;
case 2:
videoWidth = 176;
videoHight = 144;
break;
case 3:
videoWidth = 1280;
videoHight = 720;
break;
default:
videoWidth = 640;
videoHight = 480;
}
mCamera.stopPreview();
initCamera(mSurfaceHolder);
}
void CheckInitAudioRecorder()
{
if ( audioRecord_ == null )
{
audioRecord_ = new NTAudioRecord(this, 1);
}
if( audioRecord_ != null )
{
Log.i(TAG, "onCreate, call executeAudioRecordMethod..");
// auido_ret: 0 ok, other failed
int auido_ret= audioRecord_.executeAudioRecordMethod();
Log.i(TAG, "onCreate, call executeAudioRecordMethod.. auido_ret=" + auido_ret);
}
}
//Configure recorder related function.
void ConfigRecorderFuntion(boolean isNeedLocalRecorder)
{
if ( libPublisher != null )
{
if ( isNeedLocalRecorder )
{
if ( recDir != null && !recDir.isEmpty() )
{
int ret = libPublisher.SmartPublisherCreateFileDirectory(recDir);
if ( 0 == ret )
{
if ( 0 != libPublisher.SmartPublisherSetRecorderDirectory(recDir) )
{
Log.e(TAG, "Set recoder dir failed , path:" + recDir);
return;
}
if ( 0 != libPublisher.SmartPublisherSetRecorder(1) )
{
Log.e(TAG, "SmartPublisherSetRecoder failed.");
return;
}
if ( 0 != libPublisher.SmartPublisherSetRecorderFileMaxSize(200) )
{
Log.e(TAG, "SmartPublisherSetRecoderFileMaxSize failed.");
return;
}
}
else
{
Log.e(TAG, "Create recoder dir failed, path:" + recDir);
}
}
}
else
{
if ( 0 != libPublisher.SmartPublisherSetRecorder(0) )
{
Log.e(TAG, "SmartPublisherSetRecoder failed.");
return;
}
}
}
}
class ButtonRecorderMangerListener implements OnClickListener
{
public void onClick(View v)
{
if (mCamera != null )
{
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
Intent intent = new Intent();
intent.setClass(CameraPublishActivity.this, RecorderManager.class);
intent.putExtra("RecoderDir", recDir);
startActivity(intent);
}
}
class ButtonNoiseSuppressionListener implements OnClickListener
{
public void onClick(View v)
{
is_noise_suppression = !is_noise_suppression;
if ( is_noise_suppression )
btnNoiseSuppression.setText("停用噪音抑制");
else
btnNoiseSuppression.setText("启用噪音抑制");
}
}
class ButtonAGCListener implements OnClickListener
{
public void onClick(View v)
{
is_agc = !is_agc;
if ( is_agc )
btnAGC.setText("停用AGC");
else
btnAGC.setText("启用AGC");
}
}
class ButtonSpeexListener implements OnClickListener
{
public void onClick(View v)
{
is_speex = !is_speex;
if ( is_speex )
btnSpeex.setText("不使用Speex");
else
btnSpeex.setText("使用Speex");
}
}
class ButtonMuteListener implements OnClickListener
{
public void onClick(View v)
{
is_mute = !is_mute;
if ( is_mute )
btnMute.setText("取消静音");
else
btnMute.setText("静音");
if ( libPublisher != null )
libPublisher.SmartPublisherSetMute(is_mute?1:0);
}
}
class ButtonMirrorListener implements OnClickListener
{
public void onClick(View v)
{
is_mirror = !is_mirror;
if ( is_mirror )
btnMirror.setText("关镜像");
else
btnMirror.setText("开镜像");
if ( libPublisher != null )
libPublisher.SmartPublisherSetMirror(is_mirror?1:0);
}
}
class ButtonHardwareEncoderListener implements OnClickListener
{
public void onClick(View v)
{
is_hardware_encoder = !is_hardware_encoder;
if ( is_hardware_encoder )
btnHWencoder.setText("当前硬解码");
else
btnHWencoder.setText("当前软解码");
}
}
class EventHande implements SmartEventCallback
{
@Override
public void onCallback(int code, long param1, long param2, String param3, String param4, Object param5){
switch (code) {
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
txt = "开始。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
txt = "连接中。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
txt = "连接失败。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
txt = "连接成功。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
txt = "连接断开。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
txt = "关闭。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
Log.i(TAG, "开始一个新的录像文件 : " + param3);
txt = "开始一个新的录像文件。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
Log.i(TAG, "已生成一个录像文件 : " + param3);
txt = "已生成一个录像文件。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
Log.i(TAG, "发送时延: " + param1 + " 帧数:" + param2);
txt = "收到发送时延..";
break;
}
String str = "当前回调状态:" + txt;
Log.i(TAG, str);
}
}
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.i(TAG, "Input publish url:" + url);
}
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() {
public void onClick(DialogInterface dialog, int which) {
String fullPushUrl = inputUrlTxt.getText().toString();
SaveInputUrl(fullPushUrl);
}
});
builderUrl.show();
}
class ButtonInputPushUrlListener implements OnClickListener
{
public void onClick(View v)
{
PopInputUrlDialog();
}
}
class ButtonStartListener implements OnClickListener
{
public void onClick(View v)
{
if (isPushing || isRecording )
{
return;
}
if (isStart)
{
stop();
btnRecoderMgr.setEnabled(true);
btnHWencoder.setEnabled(true);
btnNoiseSuppression.setEnabled(true);
btnAGC.setEnabled(true);
btnSpeex.setEnabled(true);
return;
}
isStart = true;
btnStartStop.setText(" 停止推流 ");
Log.i(TAG, "onClick start..");
if(libPublisher!=null)
{
if ( inputPushURL != null && inputPushURL.length() > 1 )
{
publishURL = inputPushURL;
Log.i(TAG, "start, input publish url:" + publishURL);
}
else
{
publishURL = baseURL + String.valueOf((int)( System.currentTimeMillis() % 1000000));
Log.i(TAG, "start, generate random url:" + publishURL);
}
printText = "URL:" + publishURL;
Log.i(TAG, printText);
textCurURL = (TextView)findViewById(R.id.txtCurURL);
textCurURL.setText(printText);
ConfigRecorderFuntion(is_need_local_recorder);
Log.i(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;
}
libPublisher.SmartPublisherInit(myContext, audio_opt, video_opt, videoWidth, videoHight);
if(is_hardware_encoder)
{
int hwHWKbps = setHardwareEncoderKbps(videoWidth, videoHight);
Log.i(TAG, "hwHWKbps: " + hwHWKbps);
int isSupportHWEncoder = libPublisher.SetSmartPublisherVideoHWEncoder(hwHWKbps);
if(isSupportHWEncoder == 0)
{
Log.i(TAG, "Great, it supports hardware encoder!");
}
}
libPublisher.SetSmartPublisherEventCallback(new EventHande());
//如果想和时间显示在同一行,请去掉'\n'
String watermarkText = "大牛直播(daniulive)\n\n";
String path =logoPath;
if(watemarkType == 0)
{
if ( isWritelogoFileSuccess )
libPublisher.SmartPublisherSetPictureWatermark(path, WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160, 160, 10, 10);
}
else if(watemarkType == 1)
{
if ( isWritelogoFileSuccess )
libPublisher.SmartPublisherSetPictureWatermark(path, WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160, 160, 10, 10);
libPublisher.SmartPublisherSetTextWatermark(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(watermarkText, 1, WATERMARK.WATERMARK_FONTSIZE_BIG, WATERMARK.WATERMARK_POSITION_BOTTOMRIGHT, 10, 10);
//libPublisher.SmartPublisherSetTextWatermarkFontFileName("/system/fonts/DroidSansFallback.ttf");
}
else
{
Log.i(TAG, "no watermark settings..");
}
//end
if ( !is_speex )
{
// set AAC encoder
libPublisher.SmartPublisherSetAudioCodecType(1);
}
else
{
// set Speex encoder
libPublisher.SmartPublisherSetAudioCodecType(2);
libPublisher.SmartPublisherSetSpeexEncoderQuality(8);
}
libPublisher.SmartPublisherSetNoiseSuppression(is_noise_suppression?1:0);
libPublisher.SmartPublisherSetAGC(is_agc?1:0);
//libPublisher.SmartPublisherSetClippingMode(0);
libPublisher.SmartPublisherSetSWVideoEncoderProfile(sw_video_encoder_profile);
libPublisher.SmartPublisherSetSWVideoEncoderSpeed(sw_video_encoder_speed);
//libPublisher.SetRtmpPublishingType(0);
//libPublisher.SmartPublisherSetGopInterval(40);
//libPublisher.SmartPublisherSetFPS(15);
//libPublisher.SmartPublisherSetSWVideoBitRate(600, 1200);
// IF not set url or url is empty, it will not publish stream
// if ( libPublisher.SmartPublisherSetURL("") != 0 )
if ( libPublisher.SmartPublisherSetURL(publishURL) != 0 )
{
Log.e(TAG, "Failed to set publish stream URL..");
}
int isStarted = libPublisher.SmartPublisherStart();
if(isStarted != 0)
{
Log.e(TAG, "Failed to publish stream..");
}
else
{
btnRecoderMgr.setEnabled(false);
btnHWencoder.setEnabled(false);
btnNoiseSuppression.setEnabled(false);
btnAGC.setEnabled(false);
btnSpeex.setEnabled(false);
}
}
if(pushType == 0 || pushType ==1)
{
CheckInitAudioRecorder(); //enable pure video publisher..
}
}
};
class ButtonStopListener implements OnClickListener
{
public void onClick(View v)
{
//onDestroy();
}
};
private void ConfigControlEnable(boolean isEnable)
{
btnRecoderMgr.setEnabled(isEnable);
btnHWencoder.setEnabled(isEnable);
btnNoiseSuppression.setEnabled(isEnable);
btnAGC.setEnabled(isEnable);
btnSpeex.setEnabled(isEnable);
}
private void InitAndSetConfig()
{
Log.i(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;
}
libPublisher.SmartPublisherInit(myContext, audio_opt, video_opt,
videoWidth, videoHight);
if (is_hardware_encoder)
{
int hwHWKbps = setHardwareEncoderKbps(videoWidth, videoHight);
Log.i(TAG, "hwHWKbps: " + hwHWKbps);
int isSupportHWEncoder = libPublisher
.SetSmartPublisherVideoHWEncoder(hwHWKbps);
if (isSupportHWEncoder == 0) {
Log.i(TAG, "Great, it supports hardware encoder!");
}
}
libPublisher.SetSmartPublisherEventCallback(new EventHande());
// 如果想和时间显示在同一行,请去掉'\n'
String watermarkText = "大牛直播(daniulive)\n\n";
String path = logoPath;
if (watemarkType == 0)
{
if (isWritelogoFileSuccess)
libPublisher.SmartPublisherSetPictureWatermark(path,
WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160,
160, 10, 10);
}
else if (watemarkType == 1)
{
if (isWritelogoFileSuccess)
libPublisher.SmartPublisherSetPictureWatermark(path,
WATERMARK.WATERMARK_POSITION_TOPRIGHT, 160,
160, 10, 10);
libPublisher.SmartPublisherSetTextWatermark(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(watermarkText, 1,
WATERMARK.WATERMARK_FONTSIZE_BIG,
WATERMARK.WATERMARK_POSITION_BOTTOMRIGHT, 10, 10);
// libPublisher.SmartPublisherSetTextWatermarkFontFileName("/system/fonts/DroidSansFallback.ttf");
} else
{
Log.i(TAG, "no watermark settings..");
}
// end
if (!is_speex)
{
// set AAC encoder
libPublisher.SmartPublisherSetAudioCodecType(1);
}
else
{
// set Speex encoder
libPublisher.SmartPublisherSetAudioCodecType(2);
libPublisher.SmartPublisherSetSpeexEncoderQuality(8);
}
libPublisher.SmartPublisherSetNoiseSuppression(is_noise_suppression ? 1
: 0);
libPublisher.SmartPublisherSetAGC(is_agc ? 1 : 0);
// libPublisher.SmartPublisherSetClippingMode(0);
libPublisher.SmartPublisherSetSWVideoEncoderProfile(sw_video_encoder_profile);
libPublisher.SmartPublisherSetSWVideoEncoderSpeed(sw_video_encoder_speed);
// libPublisher.SetRtmpPublishingType(0);
// libPublisher.SmartPublisherSetGopInterval(40);
// libPublisher.SmartPublisherSetFPS(15);
// libPublisher.SmartPublisherSetSWVideoBitRate(600, 1200);
}
class ButtonStartPushListener implements OnClickListener
{
public void onClick(View v)
{
if ( isStart )
{
return;
}
if ( isPushing )
{
stopPush();
if ( !isRecording )
{
ConfigControlEnable(true);
}
btnStartPush.setText(" 推送");
isPushing = false;
return;
}
Log.i(TAG, "onClick start push..");
if( libPublisher == null )
return;
isPushing = true;
if ( !isRecording )
{
InitAndSetConfig();
}
if ( inputPushURL != null && inputPushURL.length() > 1 )
{
publishURL = inputPushURL;
Log.i(TAG, "start, input publish url:" + publishURL);
}
else
{
publishURL = baseURL + String.valueOf((int)( System.currentTimeMillis() % 1000000));
Log.i(TAG, "start, generate random url:" + publishURL);
}
printText = "URL:" + publishURL;
Log.i(TAG, printText);
if ( libPublisher.SmartPublisherSetURL(publishURL) != 0 )
{
Log.e(TAG, "Failed to set publish stream URL..");
}
int startRet = libPublisher.SmartPublisherStartPublisher();
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);
}
textCurURL = (TextView)findViewById(R.id.txtCurURL);
textCurURL.setText(printText);
btnStartPush.setText(" 停止推送 ");
}
};
class ButtonStartRecorderListener implements OnClickListener
{
public void onClick(View v)
{
if ( isStart )
{
return;
}
if ( isRecording )
{
stopRecorder();
if ( !isPushing )
{
ConfigControlEnable(true);
}
btnStartRecorder.setText(" 录像");
isRecording = false;
return;
}
Log.i(TAG, "onClick start recorder..");
if( libPublisher == null )
return;
isRecording = true;
if ( !isPushing )
{
InitAndSetConfig();
}
ConfigRecorderFuntion(true);
int startRet = libPublisher.SmartPublisherStartRecorder();
if( startRet != 0 )
{
isRecording = false;
Log.e(TAG, "Failed to start recorder.");
return;
}
if ( !isPushing )
{
if( pushType == 0 || pushType ==1 )
{
CheckInitAudioRecorder(); //enable pure video publisher..
}
}
if ( !isPushing )
{
ConfigControlEnable(false);
}
btnStartRecorder.setText(" 停止录像");
}
};
private void stop()
{
Log.i(TAG, "onClick stop..");
StopPublish();
isStart = false;
btnStartStop.setText(" 开始推流 ");
}
private void stopPush()
{
if ( !isRecording )
{
if( audioRecord_ != null )
{
Log.i(TAG, "stopPush, call audioRecord_.StopRecording..");
audioRecord_.StopRecording();
audioRecord_ = null;
}
}
if ( libPublisher != null )
{
libPublisher.SmartPublisherStopPublisher();
}
}
private void stopRecorder()
{
if ( !isPushing )
{
if( audioRecord_ != null )
{
Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording..");
audioRecord_.StopRecording();
audioRecord_ = null;
}
}
if ( libPublisher != null )
{
libPublisher.SmartPublisherStopRecorder();
}
}
@Override
protected void onDestroy()
{
Log.i(TAG, "activity destory!");
if ( isStart )
{
isStart = false;
StopPublish();
Log.i(TAG, "onDestroy StopPublish");
}
if ( isPushing || isRecording )
{
if( audioRecord_ != null )
{
Log.i(TAG, "surfaceDestroyed, call StopRecording..");
audioRecord_.StopRecording();
audioRecord_ = null;
}
stopPush();
stopRecorder();
isPushing = false;
isRecording = false;
}
super.onDestroy();
finish();
System.exit(0);
}
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.i(TAG, "Camera index:" + i + " support min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
Log.i(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.i(TAG, "Camera found appropriate fps, min fps:" + range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
+ " ,max fps:" + range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
}
if ( findRange != null )
{
parameters.setPreviewFpsRange(findRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], findRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
/*it will call when surfaceChanged*/
private void initCamera(SurfaceHolder holder)
{
Log.i(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;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated..");
try {
int CammeraIndex=findBackCamera();
Log.i(TAG, "BackCamera: " + CammeraIndex);
if(CammeraIndex==-1){
CammeraIndex=findFrontCamera();
currentCameraType = FRONT;
imgSwitchCamera.setEnabled(false);
if(CammeraIndex == -1)
{
Log.i(TAG, "NO camera!!");
return;
}
}
else
{
currentCameraType = BACK;
}
if ( mCamera == null )
{
mCamera = openCamera(currentCameraType);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged..");
initCamera(holder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.i(TAG, "Surface Destroyed");
}
public void onConfigurationChanged(Configuration newConfig) {
try {
super.onConfigurationChanged(newConfig);
Log.i(TAG, "onConfigurationChanged, start:" + isStart);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
if(!isStart && !isPushing && !isRecording) {
currentOrigentation = LANDSCAPE;
}
} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
if(!isStart && !isPushing && !isRecording ) {
currentOrigentation = PORTRAIT;
}
}
} catch (Exception ex) {
}
}
@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) {
Parameters params = camera.getParameters();
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(data, data.length, currentCameraType, currentOrigentation);
}
camera.addCallbackBuffer(data);
}
}
@SuppressLint("NewApi")
private Camera openCamera(int type){
int frontIndex =-1;
int backIndex = -1;
int cameraCount = Camera.getNumberOfCameras();
Log.i(TAG, "cameraCount: " + cameraCount);
CameraInfo info = new CameraInfo();
for(int cameraIndex = 0; cameraIndex<cameraCount; cameraIndex++){
Camera.getCameraInfo(cameraIndex, info);
if(info.facing == CameraInfo.CAMERA_FACING_FRONT){
frontIndex = cameraIndex;
}else if(info.facing == 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);
}
private void StopPublish()
{
if(audioRecord_ != null)
{
Log.i(TAG, "surfaceDestroyed, call StopRecording..");
audioRecord_.StopRecording();
audioRecord_ = null;
}
if ( libPublisher != null )
{
libPublisher.SmartPublisherStop();
}
}
//Check if it has front camera
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;
}
//Check if it has back camera
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 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.i(TAG, "curDegree: "+ result);
camera.setDisplayOrientation (result);
}
private int setHardwareEncoderKbps(int width, int height)
{
int hwEncoderKpbs = 0;
switch(width) {
case 176:
hwEncoderKpbs = 300;
break;
case 320:
hwEncoderKpbs = 500;
break;
case 640:
hwEncoderKpbs = 1000;
break;
case 1280:
hwEncoderKpbs = 1700;
break;
default:
hwEncoderKpbs = 1000;
}
return hwEncoderKpbs;
}
}