CombineImageSender.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. using Android.Graphics;
  2. using System;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Vinno.vCloud.FIS.CrossPlatform.Common;
  7. using Vinno.vCloud.FIS.CrossPlatform.Common.Enum;
  8. using Vinno.vCloud.FIS.CrossPlatform.Common.LiveVideo;
  9. namespace Vinno.vCloud.FIS.CrossPlatform.Android.LiveVideo
  10. {
  11. internal class CombineImageSender : IImageSender
  12. {
  13. private TerminalImageSender _deviceImageSender;
  14. private CameraHelper _cameraHelper;
  15. private Action<EnumLiveStates> _updateState;
  16. private byte[] _screenTempBuffer;
  17. private byte[] _screenTempPushBuffer;
  18. private byte[] _cameraTempBuffer;
  19. private byte[] _cameraTempPushBuffer;
  20. private byte[] _combineTempPushBuffer;
  21. private byte[] _combineTempPushBufferI420;
  22. private int _channelCount;
  23. private Bitmap _tempScreenBitmap;
  24. private Bitmap _tempCameraBitmap;
  25. private Bitmap _tempCombineBitmap;
  26. private Canvas _tempCanvas;
  27. private int _outputScreenHeight;
  28. private int _outputScreenWidth;
  29. private int _cameraHeight;
  30. private int _cameraWidth;
  31. private string _cameraId;
  32. private bool _isStarted;
  33. private object _screenLock = new object();
  34. private object _cameraLock = new object();
  35. private int _lastSendDataTime = 0;
  36. private int _finalOutputHeight;
  37. private int _finalOutputWidth;
  38. private EnumLiveProtocol _liveProtocol;
  39. /// <summary>
  40. /// 实际上ARGB用于RTMP,而I420用于RTC
  41. /// </summary>
  42. public AndroidImageType ImageType { get; private set; }
  43. public event EventHandler<CPVideoFrameData> ImageSended;
  44. public CombineImageSender(int outputScreenWidth, int outputScreenHeight, int cameraWidth, int cameraHeight, string cameraId, EnumLiveProtocol liveProtocol, Action<EnumLiveStates> updateState = null, AndroidImageType imageType = AndroidImageType.ARGB)
  45. {
  46. _channelCount = 4;
  47. ImageType = imageType;
  48. _outputScreenHeight = outputScreenHeight;
  49. _outputScreenWidth = outputScreenWidth;
  50. _cameraWidth = cameraWidth;
  51. _cameraHeight = cameraHeight;
  52. _updateState = updateState;
  53. _cameraId = cameraId;
  54. _liveProtocol = liveProtocol;
  55. switch (ImageType)
  56. {
  57. case AndroidImageType.ARGB:
  58. _finalOutputWidth = outputScreenWidth + cameraWidth;
  59. _finalOutputHeight = outputScreenHeight > cameraHeight ? outputScreenHeight : cameraHeight;
  60. _tempCameraBitmap = Bitmap.CreateBitmap(_cameraWidth, _cameraHeight, Bitmap.Config.Argb8888);
  61. _tempScreenBitmap = Bitmap.CreateBitmap(_outputScreenWidth, _outputScreenHeight, Bitmap.Config.Argb8888);
  62. _tempCombineBitmap = Bitmap.CreateBitmap(_finalOutputWidth, _finalOutputHeight, Bitmap.Config.Argb8888);
  63. _tempCanvas = new Canvas(_tempCombineBitmap);
  64. _tempCanvas.Save();
  65. ResizeBuffer(ref _cameraTempBuffer, _cameraHeight * _cameraWidth * 4);
  66. ResizeBuffer(ref _combineTempPushBuffer, _finalOutputWidth * _finalOutputHeight * _channelCount);
  67. break;
  68. case AndroidImageType.I420:
  69. _finalOutputHeight = 720;
  70. _finalOutputWidth = 1280;
  71. _tempCameraBitmap = Bitmap.CreateBitmap(_cameraWidth, _cameraHeight, Bitmap.Config.Argb8888);
  72. _tempScreenBitmap = Bitmap.CreateBitmap(_outputScreenWidth, _outputScreenHeight, Bitmap.Config.Argb8888);
  73. _tempCombineBitmap = Bitmap.CreateBitmap(_finalOutputWidth, _finalOutputHeight, Bitmap.Config.Argb8888);
  74. _tempCanvas = new Canvas(_tempCombineBitmap);
  75. _tempCanvas.Save();
  76. ResizeBuffer(ref _cameraTempBuffer, _cameraHeight * _cameraWidth * 4);
  77. ResizeBuffer(ref _combineTempPushBuffer, _finalOutputWidth * _finalOutputHeight * _channelCount);
  78. ResizeBuffer(ref _combineTempPushBufferI420, _finalOutputWidth * _finalOutputHeight * 3 / 2);
  79. break;
  80. }
  81. }
  82. public void SetImageType(AndroidImageType imageType)
  83. {
  84. ImageType = imageType;
  85. }
  86. public async void Start()
  87. {
  88. try
  89. {
  90. if (!_isStarted)
  91. {
  92. if (_cameraHelper == null)
  93. {
  94. _cameraHelper = new CameraHelper(_cameraWidth, _cameraHeight, _cameraId, _updateState);
  95. }
  96. if (_cameraHelper != null)
  97. {
  98. _cameraHelper.ImageSended += OnCaptureImageChanged;
  99. _cameraHelper.Start();
  100. }
  101. if (_deviceImageSender == null)
  102. {
  103. _deviceImageSender = new TerminalImageSender(_outputScreenWidth, _outputScreenHeight, _liveProtocol, _updateState);
  104. }
  105. if (_deviceImageSender != null)
  106. {
  107. _deviceImageSender.ImageSended += OnDeviceImageSended;
  108. _deviceImageSender.Start();
  109. }
  110. _isStarted = true;
  111. await Task.Run(() =>
  112. {
  113. while (_isStarted)
  114. {
  115. try
  116. {
  117. if (Math.Abs(_lastSendDataTime - DateTime.Now.Millisecond) <= 40)
  118. {
  119. continue;
  120. }
  121. else
  122. {
  123. _lastSendDataTime = DateTime.Now.Millisecond;
  124. }
  125. lock (_screenLock)
  126. {
  127. if (_screenTempBuffer == null)
  128. {
  129. Thread.Sleep(10);
  130. continue;
  131. }
  132. _tempCanvas.Save();
  133. ResizeBuffer(ref _screenTempPushBuffer, _screenTempBuffer.Length);
  134. Buffer.BlockCopy(_screenTempBuffer, 0, _screenTempPushBuffer, 0, _screenTempBuffer.Length);
  135. }
  136. lock (_cameraLock)
  137. {
  138. ResizeBuffer(ref _cameraTempPushBuffer, _cameraTempBuffer.Length);
  139. Buffer.BlockCopy(_cameraTempBuffer, 0, _cameraTempPushBuffer, 0, _cameraTempBuffer.Length);
  140. }
  141. switch (ImageType)
  142. {
  143. case AndroidImageType.ARGB:
  144. Marshal.Copy(_screenTempPushBuffer, 0, _tempScreenBitmap.LockPixels(), _screenTempPushBuffer.Length);
  145. _tempScreenBitmap.UnlockPixels();
  146. Marshal.Copy(_cameraTempPushBuffer, 0, _tempCameraBitmap.LockPixels(), _cameraTempPushBuffer.Length);
  147. _tempCameraBitmap.UnlockPixels();
  148. _tempCanvas.DrawBitmap(_tempScreenBitmap, 0, 0, new Paint());
  149. _tempCanvas.DrawBitmap(_tempCameraBitmap, _outputScreenWidth, 0, new Paint());
  150. Marshal.Copy(_tempCombineBitmap.LockPixels(), _combineTempPushBuffer, 0, _combineTempPushBuffer.Length);
  151. _tempCombineBitmap.UnlockPixels();
  152. _tempCanvas.Restore();
  153. ImageSended?.Invoke(this, new CPVideoFrameData(_finalOutputWidth, _finalOutputHeight, _combineTempPushBuffer) { ImageData = new CPVideoFrameData(_cameraWidth, _cameraHeight, _cameraTempPushBuffer) });
  154. break;
  155. case AndroidImageType.I420:
  156. Marshal.Copy(_screenTempPushBuffer, 0, _tempScreenBitmap.LockPixels(), _screenTempPushBuffer.Length);
  157. _tempScreenBitmap.UnlockPixels();
  158. _tempCanvas.DrawBitmap(_tempScreenBitmap, 0, 0, new Paint());
  159. Marshal.Copy(_cameraTempPushBuffer, 0, IntPtr.Add(_tempCombineBitmap.LockPixels(), 1280 * _outputScreenHeight * _channelCount), _cameraTempPushBuffer.Length); //640*480
  160. Marshal.Copy(_tempCombineBitmap.LockPixels(), _combineTempPushBuffer, 0, _combineTempPushBuffer.Length);
  161. _tempCombineBitmap.UnlockPixels();
  162. _tempCanvas.Restore();
  163. YuvHelper.ConvertABGRToI420(_combineTempPushBuffer, _finalOutputWidth, _finalOutputHeight, _combineTempPushBufferI420);
  164. ImageSended?.Invoke(this, new CPVideoFrameData(_finalOutputWidth, _finalOutputHeight, _combineTempPushBufferI420) { ImageData = new CPVideoFrameData(_cameraWidth, _cameraHeight, _cameraTempPushBuffer) });
  165. break;
  166. }
  167. }
  168. catch (Exception ex)
  169. {
  170. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"CombineImageSended Error:{ex}");
  171. }
  172. }
  173. });
  174. }
  175. }
  176. catch (Exception ex)
  177. {
  178. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Start Combine Image Sender Error:{ex}");
  179. }
  180. }
  181. private void ResizeBuffer(ref byte[] buffer, int size)
  182. {
  183. if (buffer == null)
  184. {
  185. buffer = new byte[size];
  186. }
  187. else if (buffer.Length != size)
  188. {
  189. Array.Resize(ref buffer, size);
  190. }
  191. }
  192. private void OnDeviceImageSended(object sender, CPVideoFrameData e)
  193. {
  194. try
  195. {
  196. if (e == null || e.Width != _outputScreenWidth || e.Height != _outputScreenHeight)
  197. {
  198. throw new ArgumentOutOfRangeException($"OutputScreenWidth Should be {_outputScreenWidth},Actually:{e?.Width},OutputScreenHeight Should be {_outputScreenHeight},Actually:{e?.Height}");
  199. }
  200. var dataSize = _outputScreenHeight * _outputScreenWidth * 4;
  201. lock (_screenLock)
  202. {
  203. ResizeBuffer(ref _screenTempBuffer, dataSize);
  204. Buffer.BlockCopy(e.Data, 0, _screenTempBuffer, 0, dataSize);
  205. }
  206. }
  207. catch (Exception ex)
  208. {
  209. _updateState?.Invoke(EnumLiveStates.TerminalPushFailed);
  210. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($" OnDeviceImageSended error, ex:{ex}");
  211. }
  212. }
  213. private void OnCaptureImageChanged(object sender, CPVideoFrameData e)
  214. {
  215. try
  216. {
  217. if (e == null || e.Width != _cameraWidth || e.Height != _cameraHeight)
  218. {
  219. throw new ArgumentOutOfRangeException($"CameraWidth Should be {_cameraWidth},Actually:{e?.Width},CameraHeight Should be {_cameraHeight},Actually:{e?.Height}");
  220. }
  221. var dataSize = _cameraHeight * _cameraWidth * 4;
  222. lock (_cameraLock)
  223. {
  224. ResizeBuffer(ref _cameraTempBuffer, dataSize);
  225. Buffer.BlockCopy(e.Data, 0, _cameraTempBuffer, 0, dataSize);
  226. }
  227. }
  228. catch (Exception ex)
  229. {
  230. _updateState?.Invoke(EnumLiveStates.TerminalPushFailed);
  231. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($" OnDeviceImageSended error, ex:{ex}");
  232. }
  233. }
  234. public void Stop()
  235. {
  236. try
  237. {
  238. if (_isStarted)
  239. {
  240. if (_deviceImageSender != null)
  241. {
  242. _deviceImageSender.ImageSended -= OnDeviceImageSended;
  243. _deviceImageSender.Stop();
  244. _deviceImageSender = null;
  245. }
  246. if (_cameraHelper != null)
  247. {
  248. _cameraHelper.ImageSended -= OnCaptureImageChanged;
  249. _cameraHelper.Stop();
  250. _cameraHelper = null;
  251. }
  252. _isStarted = false;
  253. CrossPlatformHelper.Instance.LogWriter?.WriteLineInfo($"Combine Image Sender stoped handle");
  254. }
  255. }
  256. catch (Exception ex)
  257. {
  258. CrossPlatformHelper.Instance.LogWriter?.WriteLineError($"Stop CombineImageSender Failed:{ex}");
  259. _isStarted = false;
  260. }
  261. }
  262. public void SwitchCamera()
  263. {
  264. if (_cameraHelper != null)
  265. {
  266. _cameraHelper.SwitchCamera();
  267. }
  268. }
  269. }
  270. }