using ManageLiteAV;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace JianGongYun.TRTC.Components
{
public class TXLiteAVVideoView : Canvas, IDisposable
{
private bool mOccupy = false; // view 是否已被占用
private bool mLocalView = false; // 是否为本地画面
private bool mPause = false;
private bool mFirstFrame = false;
private string mUserId;
private TRTCVideoStreamType mStreamType;
private TRTCVideoFillMode mRenderMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit; // 0:填充,1:适应
private volatile FrameBufferInfo mArgbFrame = new FrameBufferInfo(); // 帧缓存
// 位图缓存,防止GC频繁
private WriteableBitmap mWriteableBitmap;
private Int32Rect mInt32Rect;
private Pen mPen;
public TXLiteAVVideoView()
{
mPen = new Pen { Brush = Brushes.DarkGray, Thickness = 1 };
}
///
/// 设置 View 绑定参数
///
/// 需要渲染画面的 userId,如果是本地画面,则传空字符串。
/// 渲染类型
/// TRTCCloud 实例,用户注册视频数据回调。
/// 渲染本地画面,SDK 返回的 userId 为""
/// true:绑定成功,false:绑定失败
public bool RegEngine(string userId, TRTCVideoStreamType type, ITRTCCloud engine, bool local = false)
{
if (mOccupy) return false;
mLocalView = local;
mUserId = userId;
mStreamType = type;
int count = TXLiteAVVideoViewManager.GetInstance().Count;
if (engine != null)
{
if (count == 0)
{
engine.setLocalVideoRenderCallback(TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32,
TRTCVideoBufferType.TRTCVideoBufferType_Buffer, TXLiteAVVideoViewManager.GetInstance());
}
if (!mLocalView)
{
engine.setRemoteVideoRenderCallback(userId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32,
TRTCVideoBufferType.TRTCVideoBufferType_Buffer, TXLiteAVVideoViewManager.GetInstance());
}
}
if (mLocalView)
TXLiteAVVideoViewManager.GetInstance().AddView("", type, this);
else
TXLiteAVVideoViewManager.GetInstance().AddView(userId, type, this);
lock (mArgbFrame)
ReleaseBuffer(mArgbFrame);
mOccupy = true;
this.InvalidateVisual();
return true;
}
///
/// 移除 View 绑定参数
///
/// TRTCCloud 实例,用户注册视频数据回调。
public void RemoveEngine(ITRTCCloud engine)
{
if (mLocalView)
TXLiteAVVideoViewManager.GetInstance().RemoveView("", mStreamType, this);
else
TXLiteAVVideoViewManager.GetInstance().RemoveView(mUserId, mStreamType, this);
int count = TXLiteAVVideoViewManager.GetInstance().Count;
if (engine != null)
{
if (count == 0)
{
engine.setLocalVideoRenderCallback(TRTCVideoPixelFormat.TRTCVideoPixelFormat_Unknown,
TRTCVideoBufferType.TRTCVideoBufferType_Unknown, null);
}
if (!mLocalView && !TXLiteAVVideoViewManager.GetInstance().HasUserId(mUserId))
{
engine.setRemoteVideoRenderCallback(mUserId, TRTCVideoPixelFormat.TRTCVideoPixelFormat_Unknown,
TRTCVideoBufferType.TRTCVideoBufferType_Unknown, null);
}
}
lock (mArgbFrame)
ReleaseBuffer(mArgbFrame);
mUserId = "";
mOccupy = false;
mLocalView = false;
mFirstFrame = false;
mRenderMode = TRTCVideoFillMode.TRTCVideoFillMode_Fit;
//this.OnViewRemove?.Invoke();
this.InvalidateVisual();
}
///
/// 设置 View 的渲染模式
///
/// 渲染模式
public void SetRenderMode(TRTCVideoFillMode mode)
{
mRenderMode = mode;
}
///
/// 判断 View 是否被占用
///
/// true:当前 View 已被占用,false:当前 View 未被占用
public bool IsViewOccupy()
{
return mOccupy;
}
///
/// 暂停渲染,显示默认图片
///
/// 是否暂停
public void SetPause(bool pause)
{
if (mPause != pause)
{
mPause = pause;
if (mPause)
{
this.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20));
}
else
{
this.Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x00, 0x00, 0x00));
// 避免刷新最后一帧数据
lock (mArgbFrame)
ReleaseBuffer(mArgbFrame);
}
this.InvalidateVisual();
}
}
///
/// 清除所有映射信息
///
public static void RemoveAllRegEngine()
{
TXLiteAVVideoViewManager.GetInstance().RemoveAllView();
}
public bool AppendVideoFrame(byte[] data, int width, int height, TRTCVideoPixelFormat videoFormat, TRTCVideoRotation rotation)
{
if (!mFirstFrame)
mFirstFrame = true;
OnRenderVideoFrameHandler?.Invoke(data, width, height);
if (mPause)
return false;
if (data == null || data.Length <= 0)
return false;
// data 数据有误
if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32 && width * height * 4 != data.Length)
return false;
// 暂时不支持其他 YUV 类型
if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_I420 && width * height * 3 / 2 != data.Length)
return false;
// 目前只实现了 BGRA32 的数据类型,如需其他类型请重写,并设置回调的数据类型
if (videoFormat == TRTCVideoPixelFormat.TRTCVideoPixelFormat_BGRA32)
{
lock (mArgbFrame)
{
if (mArgbFrame.data == null || mArgbFrame.width != width || mArgbFrame.height != height)
{
ReleaseBuffer(mArgbFrame);
mArgbFrame.width = width;
mArgbFrame.height = height;
mArgbFrame.data = new byte[data.Length];
}
Buffer.BlockCopy(data, 0, mArgbFrame.data, 0, (int)data.Length);
mArgbFrame.newFrame = true;
mArgbFrame.rotation = rotation;
}
}
// 回到主线程刷新当前画面
this.Dispatcher.Invoke(new Action(() =>
{
this.InvalidateVisual();
}));
return true;
}
protected override void OnRender(DrawingContext dc)
{
bool needDrawFrame = true;
if (mPause)
needDrawFrame = false;
if (mArgbFrame.data == null)
needDrawFrame = false;
if (!needDrawFrame)
{
return;
}
lock (mArgbFrame)
{
if (mArgbFrame.data == null)
return;
if (mRenderMode == TRTCVideoFillMode.TRTCVideoFillMode_Fill)
{
RenderFillMode(dc, mArgbFrame.data, mArgbFrame.width, mArgbFrame.height, (int)mArgbFrame.rotation * 90);
}
else if (mRenderMode == TRTCVideoFillMode.TRTCVideoFillMode_Fit)
{
RenderFitMode(dc, mArgbFrame.data, mArgbFrame.width, mArgbFrame.height, (int)mArgbFrame.rotation * 90);
}
}
}
//public event Action OnRenderVideoFrameHandler;
///
/// 渲染回调byte[] data,int width, int height
///
public event Action OnRenderVideoFrameHandler;
//public event Action OnViewRemove;
private void RenderFillMode(DrawingContext dc, byte[] data, int width, int height, int rotation)
{
int viewWidth = (int)this.ActualWidth, viewHeight = (int)this.ActualHeight;
PixelFormat pixelFormat = PixelFormats.Pbgra32;
int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8;
int stride = bytesPerPixel * width;
if (mWriteableBitmap == null || mWriteableBitmap.PixelWidth != width || mWriteableBitmap.PixelHeight != height)
{
mWriteableBitmap = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
mInt32Rect = new Int32Rect(0, 0, width, height);
}
mWriteableBitmap.Lock();
Marshal.Copy(data, 0, mWriteableBitmap.BackBuffer, data.Length);
mWriteableBitmap.AddDirtyRect(mInt32Rect);
mWriteableBitmap.Unlock();
ImageBrush brush = new ImageBrush(mWriteableBitmap);
if (rotation > 0)
{
Matrix transform = Matrix.Identity;
double scale = (double)viewWidth / (double)viewHeight;
if (rotation == 90 || rotation == 270)
transform.ScaleAt(scale, scale, 0.5, 0.5);
transform.RotateAt(rotation, 0.5, 0.5);
brush.RelativeTransform = new MatrixTransform(transform);
}
brush.Stretch = Stretch.UniformToFill;
Rect rect = new Rect(0, 0, viewWidth, viewHeight);
dc.DrawRectangle(brush, mPen, rect);
}
private void RenderFitMode(DrawingContext dc, byte[] data, int width, int height, int rotation)
{
int viewWidth = (int)this.ActualWidth, viewHeight = (int)this.ActualHeight;
PixelFormat pixelFormat = PixelFormats.Pbgra32;
int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8;
int stride = bytesPerPixel * width;
if (mWriteableBitmap == null || mWriteableBitmap.PixelWidth != width || mWriteableBitmap.PixelHeight != height)
{
mWriteableBitmap = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
mInt32Rect = new Int32Rect(0, 0, width, height);
}
mWriteableBitmap.Lock();
Marshal.Copy(data, 0, mWriteableBitmap.BackBuffer, data.Length);
mWriteableBitmap.AddDirtyRect(mInt32Rect);
mWriteableBitmap.Unlock();
//OnRenderVideoFrameHandler?.Invoke(mWriteableBitmap);
ImageBrush brush = new ImageBrush(mWriteableBitmap);
if (rotation > 0)
{
Matrix transform = Matrix.Identity;
double scale = (double)viewHeight / (double)viewWidth;
if (rotation == 90 || rotation == 270)
transform.ScaleAt(1, scale, 0.5, 0.5);
transform.RotateAt(rotation, 0.5, 0.5);
brush.RelativeTransform = new MatrixTransform(transform);
}
brush.Stretch = Stretch.Uniform;
Rect rect = new Rect(0, 0, viewWidth, viewHeight);
dc.DrawRectangle(brush, mPen, rect);
}
private void ReleaseBuffer(FrameBufferInfo info)
{
if (info.data != null)
info.data = null;
info.width = 0;
info.height = 0;
info.newFrame = false;
info.rotation = TRTCVideoRotation.TRTCVideoRotation0;
}
#region Dispose
private bool disposed = false;
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
if (disposing)
{
ReleaseBuffer(mArgbFrame);
mWriteableBitmap = null;
}
disposed = true;
}
~TXLiteAVVideoView()
{
this.Dispose(false);
}
#endregion
}
public class TXLiteAVVideoViewManager : ITRTCVideoRenderCallback
{
private volatile Dictionary mMapViews;
public static TXLiteAVVideoViewManager sInstance;
private static Object mLocker = new Object();
public static TXLiteAVVideoViewManager GetInstance()
{
if (sInstance == null)
{
lock (mLocker)
{
if (sInstance == null)
sInstance = new TXLiteAVVideoViewManager();
}
}
return sInstance;
}
private TXLiteAVVideoViewManager()
{
mMapViews = new Dictionary();
}
private string GetKey(string userId, TRTCVideoStreamType type)
{
return String.Format("{0}_{1}", userId, type);
}
// 主要用于判断当前 user 是否还有存在流画面,存在则不移除监听。
public bool HasUserId(string userId)
{
bool exit = false;
lock (mMapViews)
{
exit = mMapViews.ContainsKey(GetKey(userId, TRTCVideoStreamType.TRTCVideoStreamTypeBig)) ||
mMapViews.ContainsKey(GetKey(userId, TRTCVideoStreamType.TRTCVideoStreamTypeSub));
}
return exit;
}
public void AddView(string userId, TRTCVideoStreamType type, TXLiteAVVideoView view)
{
lock (mMapViews)
{
bool find = false;
foreach (var item in mMapViews)
{
if (item.Key.Equals(GetKey(userId, type)))
{
find = true;
break;
}
}
if (!find)
{
mMapViews.Add(GetKey(userId, type), view);
}
}
}
public void RemoveView(string userId, TRTCVideoStreamType type, TXLiteAVVideoView view)
{
lock (mMapViews)
{
foreach (var item in mMapViews.ToList())
{
if (item.Key.Equals(GetKey(userId, type)))
{
if (item.Value != null)
{
item.Value.Dispose();
}
mMapViews.Remove(item.Key);
break;
}
}
}
}
public void RemoveAllView()
{
lock (mMapViews)
mMapViews.Clear();
}
public int Count
{
get
{
lock (mMapViews)
return mMapViews.Count;
}
}
//public event Action OnRenderVideoFrameHandler;
///
/// 自定义渲染回调,只能存在一个回调
///
///
///
///
public void onRenderVideoFrame(string userId, TRTCVideoStreamType streamType, TRTCVideoFrame frame)
{
//OnRenderVideoFrameHandler?.Invoke(userId, streamType, frame);
// 大小视频是占一个视频位,底层支持动态切换。
if (streamType == TRTCVideoStreamType.TRTCVideoStreamTypeSmall)
streamType = TRTCVideoStreamType.TRTCVideoStreamTypeBig;
TXLiteAVVideoView view = null;
lock (mMapViews)
{
foreach (var item in mMapViews)
{
if (item.Key.Equals(GetKey(userId, streamType)) && item.Value != null)
{
view = item.Value;
break;
}
}
}
if (view != null)
view.AppendVideoFrame(frame.data, (int)frame.width, (int)frame.height, frame.videoFormat, frame.rotation);
}
}
class FrameBufferInfo
{
public byte[] data { get; set; }
public int width { get; set; }
public int height { get; set; }
public bool newFrame { get; set; }
public TRTCVideoRotation rotation { get; set; }
public FrameBufferInfo()
{
rotation = TRTCVideoRotation.TRTCVideoRotation0;
newFrame = false;
width = 0;
height = 0;
data = null;
}
}
}