package com.example.plugina; import android.content.Context; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.hardware.Camera; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.bingo.apkloader.base.ApkContext; import com.bingo.apkloader.base.ApkWindowBuild; import com.github.faucamp.simplertmp.RtmpHandler; import net.ossrs.yasea.SrsCameraView; import net.ossrs.yasea.SrsEncodeHandler; import net.ossrs.yasea.SrsPublisher; import net.ossrs.yasea.SrsRecordHandler; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.SocketException; public class ViewPush implements RtmpHandler.RtmpListener, SrsRecordHandler.SrsRecordListener, SrsEncodeHandler.SrsEncodeListener, SurfaceHolder.Callback, View.OnTouchListener, View.OnClickListener { private static final String TAG = "ViewPush"; private ApkWindowBuild apkWindowBuild; private ApkContext apkContext; private WindowManager windowManager; private WindowManager.LayoutParams windowParams; private String pushUrl; private String apiBaseUrl; private String userId; private String token; private String sourceType; private String group; private String owner; private String orientation; private boolean destroyed = false; private int mWidth = 640; private int mHeight = 480; private static final long DOUBLE_BACK_TOUCH_INTERVAL = 2000; // 两次按下返回键的时间间隔,单位为毫秒 private long mLastBackPressTime = 0; private SrsPublisher mPublisher; private SrsCameraView mCameraView; private TextView txtOnlineCount; private View contentView = null; private Handler mainLooperHandler = new Handler(Looper.getMainLooper()); public ViewPush(ApkContext apkContext, ApkWindowBuild apkWindowBuild,String pushUrl, String apiBaseUrl, String userId, String token, String sourceType, String group, String owner, String orientation) { this.pushUrl = pushUrl; this.apiBaseUrl = apiBaseUrl; this.userId = userId; this.token = token; this.sourceType = sourceType; this.group = group; this.owner = owner; this.orientation = orientation; this.apkContext = apkContext; this.apkWindowBuild = apkWindowBuild; this.contentView = this.apkWindowBuild.Inflate(R.layout.view_push); this.init(); } public void show(){ mCameraView = this.contentView.findViewById(R.id.glsurfaceview_camera); mCameraView.setInitPreviewOrientation("horizontal".equals(this.orientation) ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT); mCameraView.setOnTouchListener(this); mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler(){ @Override public void onCameraParameters(Camera.Parameters params) { } }); mPublisher = new SrsPublisher(mCameraView); mPublisher.setEncodeHandler(new SrsEncodeHandler(this)); mPublisher.setRtmpHandler(new RtmpHandler(this)); mPublisher.setRecordHandler(new SrsRecordHandler(this)); mPublisher.setPreviewResolution(mWidth, mHeight); mPublisher.setOutputResolution(mHeight, mWidth); // 这里要和preview反过来 mPublisher.setVideoHDMode(); this.contentView.findViewById(R.id.ivClose).setOnClickListener(this); this.contentView.findViewById(R.id.btnSwitch).setOnClickListener(this); this.contentView.findViewById(R.id.btnEnableAudio).setOnClickListener(this); this.contentView.findViewById(R.id.btnCapture).setOnClickListener(this); this.contentView.findViewById(R.id.ivFullscreen).setOnClickListener(this); handler.post(httpRequestRunnable); mainLooperHandler.post(new Runnable() { @Override public void run() { // startPush(); } }); if (windowManager == null) { windowManager = (WindowManager) this.apkContext.getContext().getSystemService(Context.WINDOW_SERVICE); } if (windowParams == null) { int LAYOUT_FLAG; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE; } // 初始化LayoutParams windowParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; }else{ windowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } windowParams.gravity = Gravity.START | Gravity.TOP; if ("horizontal".equals(this.orientation)){ int maxScreenWidth = windowManager.getDefaultDisplay().getWidth(); windowParams.width = (int)(maxScreenWidth * 0.5); windowParams.x = windowParams.width; }else{ int maxScreenHeight = windowManager.getDefaultDisplay().getHeight(); windowParams.height = (int)(maxScreenHeight * 0.5); windowParams.y = windowParams.height; } Log.d(TAG, "-->show this.apkWindowBuild.AddView"); this.apkWindowBuild.AddView(this.contentView, windowParams); } private void init() { txtOnlineCount = this.contentView.findViewById(R.id.txtOnlineCount); } private void startPush() { mPublisher.startCamera(); mPublisher.startPublish(this.pushUrl); } ////////////////////////////////////////////get和set/////////////////////////////////////////////// public static String getTAG() { return TAG; } public ApkWindowBuild getApkWindowBuild() { return apkWindowBuild; } public void setApkViewBuild(ApkWindowBuild apkWindowBuild) { this.apkWindowBuild = apkWindowBuild; } public String getPushUrl() { return pushUrl; } public void setPushUrl(String pushUrl) { this.pushUrl = pushUrl; } public String getApiBaseUrl() { return apiBaseUrl; } public void setApiBaseUrl(String apiBaseUrl) { this.apiBaseUrl = apiBaseUrl; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getSourceType() { return sourceType; } public void setSourceType(String sourceType) { this.sourceType = sourceType; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } private void close(){ this.destroyed = true; mPublisher.stopPublish(); mPublisher.stopRecord(); // if (Main.liveBroadcastId != null && !Main.liveBroadcastId.isEmpty()){ // new Thread(new Runnable() { // @Override // public void run() { // HttpUtil.shutdownLiveBroadcast(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId); // } // }).start(); // } this.apkWindowBuild.removeView(this.contentView); } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override public void onClick(View v) { if (v.getId() == R.id.ivClose){ long currentTime = System.currentTimeMillis(); if (currentTime - mLastBackPressTime > DOUBLE_BACK_TOUCH_INTERVAL) { mLastBackPressTime = currentTime; Toast.makeText(this.contentView.getContext(), "再按一次退出直播", Toast.LENGTH_SHORT).show(); } else { this.close(); } }else if (v.getId() == R.id.btnSwitch){ mPublisher.switchCameraFace((mPublisher.getCameraId() + 1) % Camera.getNumberOfCameras()); }else if(v.getId() == R.id.btnEnableAudio){ if (v.getTag() == null){ mPublisher.setSendVideoOnly(true); ((ImageView)v).setImageResource(R.drawable.microphone_off); v.setTag(1); }else{ mPublisher.setSendVideoOnly(false); ((ImageView)v).setImageResource(R.drawable.microphone_on); v.setTag(null); } }else if(v.getId() == R.id.btnCapture){ takePicture(); }else if(v.getId() == R.id.ivFullscreen){ Object tag = v.getTag(); if (tag == null || tag.toString() == "full_screen"){ this.updateContentViewFullscreen(true); ((ImageView)v).setImageResource(R.drawable.normal_screen); v.setTag("normal_screen"); }else{ this.updateContentViewFullscreen(false); ((ImageView)v).setImageResource(R.drawable.full_screen); v.setTag("full_screen"); } } } @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if (event.getPointerCount() > 1) { if (action == MotionEvent.ACTION_MOVE) { mCameraView.setZoom(event); } } return true; } /** * 设置是否为全屏 * @param full */ private void updateContentViewFullscreen(boolean full){ if (full){ if ("horizontal".equals(this.orientation)){ this.windowParams.width = WindowManager.LayoutParams.MATCH_PARENT; }else{ this.windowParams.height = WindowManager.LayoutParams.MATCH_PARENT; } }else{ if ("horizontal".equals(this.orientation)){ int maxScreenWidth = windowManager.getDefaultDisplay().getWidth(); this.windowParams.width = (int)(maxScreenWidth * 0.5); this.windowParams.x = windowParams.width; }else{ int maxScreenHeight = windowManager.getDefaultDisplay().getHeight(); this.windowParams.height = (int)(maxScreenHeight * 0.5); this.windowParams.y = windowParams.height; } } //// this.apkWindowBuild.updateViewLayout(this.contentView, this.windowParams); } // 截图方法 private void takePicture() { if(this.mCameraView.getCamera() != null) { this.mCameraView.getCamera().takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // data就是图片的字节数组 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); // 保存图片 saveBitmap(bitmap); // 重新开始预览 camera.startPreview(); } }); } } private void saveBitmap(Bitmap bitmap) { String fileName = "IMG_" + System.currentTimeMillis() + ".jpg"; File file = new File(this.contentView.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName); try (FileOutputStream fos = new FileOutputStream(file)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); } catch (IOException e) { Log.e(TAG, e.getMessage()); e.printStackTrace(); } } private Handler handler = new Handler(); private Runnable httpRequestRunnable = new Runnable() { @Override public void run() { if (ViewPush.this.destroyed){ Log.d(TAG, "exit request task"); return; } updateTextViewContent(); // 每隔3秒发起一次HTTP请求 handler.postDelayed(this, 3000); } }; private void updateTextViewContent() { new Thread(new Runnable() { @Override public void run() { try { // if (Main.liveBroadcastId.isEmpty()){ // return; // } final long count = 0; //HttpUtil.fetchOnlineCount(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId, "push", ""); // 在UI线程更新TextView内容 ViewPush.this.contentView.post(new Runnable() { @Override public void run() { ViewPush.this.txtOnlineCount.setText("在线观看人数:" + count); } }); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }).start(); } private String getCreateJsonString() { String formatStr = "{\"sourceType\":\"%s\", \"objectId\":\"%s\", \"config\":\"{}\", \"owner\":\"%s\", \"group\":\"%s\", \"status\":%d}"; return String.format(formatStr, this.sourceType, this.userId, this.owner, this.group.trim().isEmpty() ? "72-0" : this.group.trim(), 10); } @Override public void onRtmpConnecting(String s) { Toast.makeText(ViewPush.this.contentView.getContext(), "连接中", Toast.LENGTH_SHORT).show(); } @Override public void onRtmpConnected(String s) { mainLooperHandler.post(new Runnable() { @Override public void run() { // // 空的则代表需要创建直播 // new Thread(new Runnable() { // @Override // public void run() { // if (ViewPush.this.destroyed){ // return; // } // if (Main.liveBroadcastId.isEmpty()) { // Main.liveBroadcastId = HttpUtil.createLiveBroadcase(ViewPush.this.apiBaseUrl, ViewPush.this.token, ViewPush.this.getCreateJsonString()); // }else{ // HttpUtil.openLiveBroadcast(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId); // } // } // }).start(); Toast.makeText(ViewPush.this.contentView.getContext(), "连接成功", Toast.LENGTH_SHORT).show(); } }); } @Override public void onRtmpVideoStreaming() { } @Override public void onRtmpAudioStreaming() { } @Override public void onRtmpStopped() { } @Override public void onRtmpDisconnected() { } @Override public void onRtmpVideoFpsChanged(double v) { } @Override public void onRtmpVideoBitrateChanged(double v) { } @Override public void onRtmpAudioBitrateChanged(double v) { } @Override public void onRtmpSocketException(SocketException e) { } @Override public void onRtmpIOException(IOException e) { } @Override public void onRtmpIllegalArgumentException(IllegalArgumentException e) { } @Override public void onRtmpIllegalStateException(IllegalStateException e) { } @Override public void onNetworkWeak() { } @Override public void onNetworkResume() { } @Override public void onEncodeIllegalArgumentException(IllegalArgumentException e) { } @Override public void onRecordPause() { } @Override public void onRecordResume() { } @Override public void onRecordStarted(String s) { } @Override public void onRecordFinished(String s) { } @Override public void onRecordIllegalArgumentException(IllegalArgumentException e) { } @Override public void onRecordIOException(IOException e) { } }