package com.frank.live; import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.media.AudioFormat; import android.os.Build; import android.os.Handler; import android.os.Message; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.TextureView; import android.view.View; import android.widget.CompoundButton; import android.widget.Toast; import android.widget.ToggleButton; import com.frank.live.Push.LivePusher; import com.frank.live.camera2.Camera2Helper; import com.frank.live.listener.LiveStateChangeListener; import com.frank.live.param.AudioParam; import com.frank.live.param.VideoParam; /** * h264与rtmp实时推流直播 * Created by frank on 2018/1/28. */ public class RtmpLiveActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, LiveStateChangeListener { private final static String TAG = RtmpLiveActivity.class.getSimpleName(); private final static int CODE_CAMERA_RECORD = 0x0001; private final static String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}; private final static String LIVE_URL = "rtmp://192.168.1.3/live/stream"; private final static int MSG_ERROR = 100; private TextureView textureView; private LivePusher livePusher; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_ERROR) { String errMsg = (String) msg.obj; if (!TextUtils.isEmpty(errMsg)) { Toast.makeText(RtmpLiveActivity.this, errMsg, Toast.LENGTH_SHORT).show(); } } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rtmp_live); initView(); requirePermission(); initPusher(); } private void initView() { findViewById(R.id.btn_swap).setOnClickListener(this); ((ToggleButton) findViewById(R.id.btn_live)).setOnCheckedChangeListener(this); textureView = findViewById(R.id.surface_camera); } private void initPusher() { int width = 640;//分辨率设置很重要 int height = 480; int videoBitRate = 400;//kb/s jason-->480kb int videoFrameRate = 25;//fps VideoParam videoParam = new VideoParam(width, height, Integer.valueOf(Camera2Helper.CAMERA_ID_BACK), videoBitRate, videoFrameRate); int sampleRate = 44100;//采样率:Hz int channelConfig = AudioFormat.CHANNEL_IN_STEREO;//立体声道 int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//pcm16位 int numChannels = 2;//声道数 AudioParam audioParam = new AudioParam(sampleRate, channelConfig, audioFormat, numChannels); livePusher = new LivePusher(textureView, videoParam, audioParam, this); } @Override public void onClick(View v) { if (v.getId() == R.id.btn_swap) { livePusher.switchCamera(); } } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { livePusher.startPush(LIVE_URL, this); } else { livePusher.stopPush(); } } @Override public void onError(String msg) { Log.e(TAG, "errMsg=" + msg); mHandler.obtainMessage(MSG_ERROR, msg).sendToTarget(); } @TargetApi(Build.VERSION_CODES.M) private void requirePermission() { requestPermissions(permissions, CODE_CAMERA_RECORD); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (permissions.length > 0 && grantResults.length == permissions.length) { for (int i = 0; i < permissions.length; i++) { Log.i(TAG, permissions[i] + ":grantResult=" + grantResults[i]); } } } @Override protected void onDestroy() { super.onDestroy(); livePusher.release(); } }