ViewPush.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. package com.example.plugina;
  2. import android.content.Context;
  3. import android.content.res.Configuration;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.PixelFormat;
  7. import android.hardware.Camera;
  8. import android.os.Build;
  9. import android.os.Environment;
  10. import android.os.Handler;
  11. import android.os.Looper;
  12. import android.util.Log;
  13. import android.view.Gravity;
  14. import android.view.MotionEvent;
  15. import android.view.SurfaceHolder;
  16. import android.view.View;
  17. import android.view.WindowManager;
  18. import android.widget.ImageView;
  19. import android.widget.TextView;
  20. import android.widget.Toast;
  21. import com.bingo.apkloader.base.ApkContext;
  22. import com.bingo.apkloader.base.ApkWindowBuild;
  23. import com.github.faucamp.simplertmp.RtmpHandler;
  24. import net.ossrs.yasea.SrsCameraView;
  25. import net.ossrs.yasea.SrsEncodeHandler;
  26. import net.ossrs.yasea.SrsPublisher;
  27. import net.ossrs.yasea.SrsRecordHandler;
  28. import java.io.File;
  29. import java.io.FileOutputStream;
  30. import java.io.IOException;
  31. import java.net.SocketException;
  32. public class ViewPush implements
  33. RtmpHandler.RtmpListener, SrsRecordHandler.SrsRecordListener, SrsEncodeHandler.SrsEncodeListener,
  34. SurfaceHolder.Callback, View.OnTouchListener, View.OnClickListener {
  35. private static final String TAG = "ViewPush";
  36. private ApkWindowBuild apkWindowBuild;
  37. private ApkContext apkContext;
  38. private WindowManager windowManager;
  39. private WindowManager.LayoutParams windowParams;
  40. private String pushUrl;
  41. private String apiBaseUrl;
  42. private String userId;
  43. private String token;
  44. private String sourceType;
  45. private String group;
  46. private String owner;
  47. private String orientation;
  48. private boolean destroyed = false;
  49. private int mWidth = 640;
  50. private int mHeight = 480;
  51. private static final long DOUBLE_BACK_TOUCH_INTERVAL = 2000; // 两次按下返回键的时间间隔,单位为毫秒
  52. private long mLastBackPressTime = 0;
  53. private SrsPublisher mPublisher;
  54. private SrsCameraView mCameraView;
  55. private TextView txtOnlineCount;
  56. private View contentView = null;
  57. private Handler mainLooperHandler = new Handler(Looper.getMainLooper());
  58. public ViewPush(ApkContext apkContext, ApkWindowBuild apkWindowBuild,String pushUrl,
  59. String apiBaseUrl,
  60. String userId,
  61. String token,
  62. String sourceType,
  63. String group,
  64. String owner,
  65. String orientation) {
  66. this.pushUrl = pushUrl;
  67. this.apiBaseUrl = apiBaseUrl;
  68. this.userId = userId;
  69. this.token = token;
  70. this.sourceType = sourceType;
  71. this.group = group;
  72. this.owner = owner;
  73. this.orientation = orientation;
  74. this.apkContext = apkContext;
  75. this.apkWindowBuild = apkWindowBuild;
  76. this.contentView = this.apkWindowBuild.Inflate(R.layout.view_push);
  77. this.init();
  78. }
  79. public void show(){
  80. mCameraView = this.contentView.findViewById(R.id.glsurfaceview_camera);
  81. mCameraView.setInitPreviewOrientation("horizontal".equals(this.orientation) ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT);
  82. mCameraView.setOnTouchListener(this);
  83. mCameraView.setCameraCallbacksHandler(new SrsCameraView.CameraCallbacksHandler(){
  84. @Override
  85. public void onCameraParameters(Camera.Parameters params) {
  86. }
  87. });
  88. mPublisher = new SrsPublisher(mCameraView);
  89. mPublisher.setEncodeHandler(new SrsEncodeHandler(this));
  90. mPublisher.setRtmpHandler(new RtmpHandler(this));
  91. mPublisher.setRecordHandler(new SrsRecordHandler(this));
  92. mPublisher.setPreviewResolution(mWidth, mHeight);
  93. mPublisher.setOutputResolution(mHeight, mWidth); // 这里要和preview反过来
  94. mPublisher.setVideoHDMode();
  95. this.contentView.findViewById(R.id.ivClose).setOnClickListener(this);
  96. this.contentView.findViewById(R.id.btnSwitch).setOnClickListener(this);
  97. this.contentView.findViewById(R.id.btnEnableAudio).setOnClickListener(this);
  98. this.contentView.findViewById(R.id.btnCapture).setOnClickListener(this);
  99. this.contentView.findViewById(R.id.ivFullscreen).setOnClickListener(this);
  100. handler.post(httpRequestRunnable);
  101. mainLooperHandler.post(new Runnable() {
  102. @Override
  103. public void run() {
  104. // startPush();
  105. }
  106. });
  107. if (windowManager == null) {
  108. windowManager = (WindowManager) this.apkContext.getContext().getSystemService(Context.WINDOW_SERVICE);
  109. }
  110. if (windowParams == null) {
  111. int LAYOUT_FLAG;
  112. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  113. LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
  114. } else {
  115. LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
  116. }
  117. // 初始化LayoutParams
  118. windowParams = new WindowManager.LayoutParams(
  119. WindowManager.LayoutParams.MATCH_PARENT,
  120. WindowManager.LayoutParams.MATCH_PARENT,
  121. WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
  122. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  123. PixelFormat.TRANSLUCENT
  124. );
  125. }
  126. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
  127. windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
  128. }else{
  129. windowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
  130. }
  131. windowParams.gravity = Gravity.START | Gravity.TOP;
  132. if ("horizontal".equals(this.orientation)){
  133. int maxScreenWidth = windowManager.getDefaultDisplay().getWidth();
  134. windowParams.width = (int)(maxScreenWidth * 0.5);
  135. windowParams.x = windowParams.width;
  136. }else{
  137. int maxScreenHeight = windowManager.getDefaultDisplay().getHeight();
  138. windowParams.height = (int)(maxScreenHeight * 0.5);
  139. windowParams.y = windowParams.height;
  140. }
  141. Log.d(TAG, "-->show this.apkWindowBuild.AddView");
  142. this.apkWindowBuild.AddView(this.contentView, windowParams);
  143. }
  144. private void init() {
  145. txtOnlineCount = this.contentView.findViewById(R.id.txtOnlineCount);
  146. }
  147. private void startPush() {
  148. mPublisher.startCamera();
  149. mPublisher.startPublish(this.pushUrl);
  150. }
  151. ////////////////////////////////////////////get和set///////////////////////////////////////////////
  152. public static String getTAG() {
  153. return TAG;
  154. }
  155. public ApkWindowBuild getApkWindowBuild() {
  156. return apkWindowBuild;
  157. }
  158. public void setApkViewBuild(ApkWindowBuild apkWindowBuild) {
  159. this.apkWindowBuild = apkWindowBuild;
  160. }
  161. public String getPushUrl() {
  162. return pushUrl;
  163. }
  164. public void setPushUrl(String pushUrl) {
  165. this.pushUrl = pushUrl;
  166. }
  167. public String getApiBaseUrl() {
  168. return apiBaseUrl;
  169. }
  170. public void setApiBaseUrl(String apiBaseUrl) {
  171. this.apiBaseUrl = apiBaseUrl;
  172. }
  173. public String getUserId() {
  174. return userId;
  175. }
  176. public void setUserId(String userId) {
  177. this.userId = userId;
  178. }
  179. public String getToken() {
  180. return token;
  181. }
  182. public void setToken(String token) {
  183. this.token = token;
  184. }
  185. public String getSourceType() {
  186. return sourceType;
  187. }
  188. public void setSourceType(String sourceType) {
  189. this.sourceType = sourceType;
  190. }
  191. public String getGroup() {
  192. return group;
  193. }
  194. public void setGroup(String group) {
  195. this.group = group;
  196. }
  197. public String getOwner() {
  198. return owner;
  199. }
  200. public void setOwner(String owner) {
  201. this.owner = owner;
  202. }
  203. private void close(){
  204. this.destroyed = true;
  205. mPublisher.stopPublish();
  206. mPublisher.stopRecord();
  207. // if (Main.liveBroadcastId != null && !Main.liveBroadcastId.isEmpty()){
  208. // new Thread(new Runnable() {
  209. // @Override
  210. // public void run() {
  211. // HttpUtil.shutdownLiveBroadcast(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId);
  212. // }
  213. // }).start();
  214. // }
  215. this.apkWindowBuild.removeView(this.contentView);
  216. }
  217. @Override
  218. public void surfaceCreated(SurfaceHolder holder) {
  219. }
  220. @Override
  221. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  222. }
  223. @Override
  224. public void surfaceDestroyed(SurfaceHolder holder) {
  225. }
  226. @Override
  227. public void onClick(View v) {
  228. if (v.getId() == R.id.ivClose){
  229. long currentTime = System.currentTimeMillis();
  230. if (currentTime - mLastBackPressTime > DOUBLE_BACK_TOUCH_INTERVAL) {
  231. mLastBackPressTime = currentTime;
  232. Toast.makeText(this.contentView.getContext(), "再按一次退出直播", Toast.LENGTH_SHORT).show();
  233. } else {
  234. this.close();
  235. }
  236. }else if (v.getId() == R.id.btnSwitch){
  237. mPublisher.switchCameraFace((mPublisher.getCameraId() + 1) % Camera.getNumberOfCameras());
  238. }else if(v.getId() == R.id.btnEnableAudio){
  239. if (v.getTag() == null){
  240. mPublisher.setSendVideoOnly(true);
  241. ((ImageView)v).setImageResource(R.drawable.microphone_off);
  242. v.setTag(1);
  243. }else{
  244. mPublisher.setSendVideoOnly(false);
  245. ((ImageView)v).setImageResource(R.drawable.microphone_on);
  246. v.setTag(null);
  247. }
  248. }else if(v.getId() == R.id.btnCapture){
  249. takePicture();
  250. }else if(v.getId() == R.id.ivFullscreen){
  251. Object tag = v.getTag();
  252. if (tag == null || tag.toString() == "full_screen"){
  253. this.updateContentViewFullscreen(true);
  254. ((ImageView)v).setImageResource(R.drawable.normal_screen);
  255. v.setTag("normal_screen");
  256. }else{
  257. this.updateContentViewFullscreen(false);
  258. ((ImageView)v).setImageResource(R.drawable.full_screen);
  259. v.setTag("full_screen");
  260. }
  261. }
  262. }
  263. @Override
  264. public boolean onTouch(View v, MotionEvent event) {
  265. int action = event.getAction();
  266. if (event.getPointerCount() > 1) {
  267. if (action == MotionEvent.ACTION_MOVE) {
  268. mCameraView.setZoom(event);
  269. }
  270. }
  271. return true;
  272. }
  273. /**
  274. * 设置是否为全屏
  275. * @param full
  276. */
  277. private void updateContentViewFullscreen(boolean full){
  278. if (full){
  279. if ("horizontal".equals(this.orientation)){
  280. this.windowParams.width = WindowManager.LayoutParams.MATCH_PARENT;
  281. }else{
  282. this.windowParams.height = WindowManager.LayoutParams.MATCH_PARENT;
  283. }
  284. }else{
  285. if ("horizontal".equals(this.orientation)){
  286. int maxScreenWidth = windowManager.getDefaultDisplay().getWidth();
  287. this.windowParams.width = (int)(maxScreenWidth * 0.5);
  288. this.windowParams.x = windowParams.width;
  289. }else{
  290. int maxScreenHeight = windowManager.getDefaultDisplay().getHeight();
  291. this.windowParams.height = (int)(maxScreenHeight * 0.5);
  292. this.windowParams.y = windowParams.height;
  293. }
  294. }
  295. //// this.apkWindowBuild.updateViewLayout(this.contentView, this.windowParams);
  296. }
  297. // 截图方法
  298. private void takePicture() {
  299. if(this.mCameraView.getCamera() != null) {
  300. this.mCameraView.getCamera().takePicture(null, null, new Camera.PictureCallback() {
  301. @Override
  302. public void onPictureTaken(byte[] data, Camera camera) {
  303. // data就是图片的字节数组
  304. Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
  305. // 保存图片
  306. saveBitmap(bitmap);
  307. // 重新开始预览
  308. camera.startPreview();
  309. }
  310. });
  311. }
  312. }
  313. private void saveBitmap(Bitmap bitmap) {
  314. String fileName = "IMG_" + System.currentTimeMillis() + ".jpg";
  315. File file = new File(this.contentView.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
  316. try (FileOutputStream fos = new FileOutputStream(file)) {
  317. bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
  318. fos.flush();
  319. } catch (IOException e) {
  320. Log.e(TAG, e.getMessage());
  321. e.printStackTrace();
  322. }
  323. }
  324. private Handler handler = new Handler();
  325. private Runnable httpRequestRunnable = new Runnable() {
  326. @Override
  327. public void run() {
  328. if (ViewPush.this.destroyed){
  329. Log.d(TAG, "exit request task");
  330. return;
  331. }
  332. updateTextViewContent();
  333. // 每隔3秒发起一次HTTP请求
  334. handler.postDelayed(this, 3000);
  335. }
  336. };
  337. private void updateTextViewContent() {
  338. new Thread(new Runnable() {
  339. @Override
  340. public void run() {
  341. try {
  342. // if (Main.liveBroadcastId.isEmpty()){
  343. // return;
  344. // }
  345. final long count = 0; //HttpUtil.fetchOnlineCount(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId, "push", "");
  346. // 在UI线程更新TextView内容
  347. ViewPush.this.contentView.post(new Runnable() {
  348. @Override
  349. public void run() {
  350. ViewPush.this.txtOnlineCount.setText("在线观看人数:" + count);
  351. }
  352. });
  353. } catch (Exception e) {
  354. Log.e(TAG, e.getMessage());
  355. }
  356. }
  357. }).start();
  358. }
  359. private String getCreateJsonString() {
  360. String formatStr = "{\"sourceType\":\"%s\", \"objectId\":\"%s\", \"config\":\"{}\", \"owner\":\"%s\", \"group\":\"%s\", \"status\":%d}";
  361. return String.format(formatStr, this.sourceType, this.userId, this.owner, this.group.trim().isEmpty() ? "72-0" : this.group.trim(), 10);
  362. }
  363. @Override
  364. public void onRtmpConnecting(String s) {
  365. Toast.makeText(ViewPush.this.contentView.getContext(), "连接中", Toast.LENGTH_SHORT).show();
  366. }
  367. @Override
  368. public void onRtmpConnected(String s) {
  369. mainLooperHandler.post(new Runnable() {
  370. @Override
  371. public void run() {
  372. // // 空的则代表需要创建直播
  373. // new Thread(new Runnable() {
  374. // @Override
  375. // public void run() {
  376. // if (ViewPush.this.destroyed){
  377. // return;
  378. // }
  379. // if (Main.liveBroadcastId.isEmpty()) {
  380. // Main.liveBroadcastId = HttpUtil.createLiveBroadcase(ViewPush.this.apiBaseUrl, ViewPush.this.token, ViewPush.this.getCreateJsonString());
  381. // }else{
  382. // HttpUtil.openLiveBroadcast(ViewPush.this.apiBaseUrl, ViewPush.this.token, Main.liveBroadcastId);
  383. // }
  384. // }
  385. // }).start();
  386. Toast.makeText(ViewPush.this.contentView.getContext(), "连接成功", Toast.LENGTH_SHORT).show();
  387. }
  388. });
  389. }
  390. @Override
  391. public void onRtmpVideoStreaming() {
  392. }
  393. @Override
  394. public void onRtmpAudioStreaming() {
  395. }
  396. @Override
  397. public void onRtmpStopped() {
  398. }
  399. @Override
  400. public void onRtmpDisconnected() {
  401. }
  402. @Override
  403. public void onRtmpVideoFpsChanged(double v) {
  404. }
  405. @Override
  406. public void onRtmpVideoBitrateChanged(double v) {
  407. }
  408. @Override
  409. public void onRtmpAudioBitrateChanged(double v) {
  410. }
  411. @Override
  412. public void onRtmpSocketException(SocketException e) {
  413. }
  414. @Override
  415. public void onRtmpIOException(IOException e) {
  416. }
  417. @Override
  418. public void onRtmpIllegalArgumentException(IllegalArgumentException e) {
  419. }
  420. @Override
  421. public void onRtmpIllegalStateException(IllegalStateException e) {
  422. }
  423. @Override
  424. public void onNetworkWeak() {
  425. }
  426. @Override
  427. public void onNetworkResume() {
  428. }
  429. @Override
  430. public void onEncodeIllegalArgumentException(IllegalArgumentException e) {
  431. }
  432. @Override
  433. public void onRecordPause() {
  434. }
  435. @Override
  436. public void onRecordResume() {
  437. }
  438. @Override
  439. public void onRecordStarted(String s) {
  440. }
  441. @Override
  442. public void onRecordFinished(String s) {
  443. }
  444. @Override
  445. public void onRecordIllegalArgumentException(IllegalArgumentException e) {
  446. }
  447. @Override
  448. public void onRecordIOException(IOException e) {
  449. }
  450. }