package com.runt.live.cpp;
|
|
import android.graphics.Bitmap;
|
import android.graphics.Canvas;
|
import android.graphics.Rect;
|
import android.util.Log;
|
import android.view.Surface;
|
|
import com.runt.live.R;
|
import com.runt.live.data.StreamWindow;
|
import com.runt.live.ui.stream.LiveLayoutView;
|
import com.runt.live.util.AudioUtil;
|
import com.runt.live.util.BitmapUtils;
|
import com.runt.open.mvi.OpenApplication;
|
|
import java.nio.ByteBuffer;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
public class LiveMiniView{
|
|
static final String TAG = "LiveMiniView";
|
public static AudioUtil audioUtil = new AudioUtil();
|
static HashMap<String,PcmData> pcmdatas = new HashMap<>();
|
static HashMap<String,Bitmap> textBitmaps = new HashMap<>();
|
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
public static int mainStreamCode = 0 ;
|
public static final int FRAME_WIDTH = 1080,FRAME_HEIGHT = 1920,FRAME_PS = 30;
|
public static final int SAMPLE_RATE = 44100;
|
public static final int SAMPLE_SIZE = (SAMPLE_RATE * 2 * 16 / 8 / 4096);//每秒音频帧的数量
|
|
static{
|
System.loadLibrary("live-tool");
|
}
|
|
public static native void native_set_main_stream_code(int streamCode);
|
public static native void native_update_mini_view(int streamCode,int sn,int width,int height,float pXrate,float pYrate,
|
boolean audioOn,boolean videoOn,long videoDelay,int streamType,float mainPositionRate,float viewRate);
|
public static native void native_remove_mini_view(int streamCode);
|
public static native void native_push_nv21(int streamCode,byte[] bytes);
|
public static native void native_push_yuv(int streamCode,byte[] bytes);
|
public static native void native_push_yuyv(int streamCode,byte[] bytes);
|
public static native void native_push_rgba(int streamCode,byte[] bytes);
|
public static native void native_push_nv_21_data(int streamCode,ByteBuffer yData, ByteBuffer uData, ByteBuffer vData,
|
int yStride,int uStride,int vStride,int uvStride,int rotation,int widht,int height);
|
public static native void native_push_pcm(byte[] bytes);
|
public static native void native_set_main_surface(Surface surface);
|
public static native void native_release_main_surface();
|
|
public static byte[] drawText(byte[] rgba,int streamCode,int width,int height){
|
|
int length = rgba.length;
|
|
byte[] argb = new byte[length];
|
StreamWindow streamWindow = LiveLayoutView.Companion.getLiveStreamsState().getValue().getStream(streamCode);
|
String key = streamWindow.getId()+"";
|
if(!textBitmaps.containsKey(key)){
|
Bitmap bitmap = BitmapUtils.Companion.getInstance().textToBitmap(streamWindow.getRemark(),130f , OpenApplication.Companion.getApplication().getResources().getColor(R.color.white),
|
OpenApplication.Companion.getApplication().getResources().getColor(R.color.transparent));
|
textBitmaps.put(key,bitmap);
|
}
|
Bitmap bitmap = textBitmaps.get(key);
|
Bitmap mainBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
//Log.e(TAG , "updateText: ${width}x${cutHeight} ${streamWindow.sizeState.value} ${( streamWindow.sizeState.value.x * 1.0 / streamWindow.sizeState.value.y )}", )
|
ByteBuffer buffer = ByteBuffer.wrap(rgba);
|
mainBitmap.copyPixelsFromBuffer(buffer);
|
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,width,height, true);
|
|
Canvas canvas = new Canvas(mainBitmap);
|
|
// 构建目标区域
|
Rect destRect = new Rect(0,0,width,height);
|
// 绘制小图到大图
|
canvas.drawBitmap(scaledBitmap, null, destRect, null);
|
|
ByteBuffer buffer2 = ByteBuffer.allocate(mainBitmap.getByteCount());
|
mainBitmap.copyPixelsToBuffer(buffer2);
|
argb = buffer2.array();
|
return argb;
|
}
|
|
/* external fun native_update_mini_sn(code : Int ,streamCode : Int, sn : Int)
|
external fun native_update_mini_live(code : Int ,streamCode : Int, isLive:Boolean)
|
external fun native_update_mini_main(code : Int ,streamCode : Int, isMain:Boolean)*/
|
//相机画面和话筒声音分离,系统画面和扬声器声音分离
|
//声音合并还是掉帧
|
private static byte[] mixPCM(byte[] data,PcmData pcmData){
|
|
long time = new Date().getTime();
|
while (pcmData.state == 1){
|
if(pcmData.state == 0 ){
|
break;
|
}
|
if(new Date().getTime() - time > 100){
|
Log.e("LiveMiniView", "mixPCM: 等待超时");
|
pcmData.state = -1;
|
break;
|
}
|
}
|
if(pcmData.state == 0){
|
synchronized (pcmData){
|
data = audioUtil.mixPCM(data,pcmData.pcmData);
|
pcmData.state = 1;
|
}
|
}
|
return data;
|
}
|
|
public static void pushPCM(int streamCode,byte[] data){
|
PcmData pcmData;
|
String streamKey = streamCode+"";
|
if(pcmdatas.containsKey(streamKey)){
|
pcmData = pcmdatas.get(streamKey);
|
}else{
|
pcmData = new PcmData(streamCode);
|
lock.writeLock().lock();
|
try {
|
pcmdatas.put(streamKey,pcmData);
|
} finally {
|
lock.writeLock().unlock();
|
}
|
}
|
lock.readLock().lock();
|
List<String> codes = new ArrayList<>();
|
codes.addAll(pcmdatas.keySet());
|
int index = codes.indexOf(streamKey);
|
//Log.i("LiveMiniView", "pushPCM: streamKey="+streamKey +" index="+index);
|
if(index == 0){
|
//Log.i("LiveMiniView", "pushPCM: isFront="+streamKey);
|
for (String key : pcmdatas.keySet()) {
|
if(!key.equals(streamKey)){
|
PcmData pcmData1 = pcmdatas.get(key);
|
data = mixPCM(data,pcmData1);
|
}
|
}
|
lock.readLock().unlock();
|
native_push_pcm(data);
|
}else{
|
lock.readLock().unlock();
|
//Log.i("LiveMiniView", "pushPCM: "+streamKey);
|
synchronized (pcmData){
|
pcmData.pcmData = data;
|
pcmData.state = 0;
|
}
|
}
|
}
|
|
public static void removePcm(int streamCode){
|
lock.writeLock().lock();
|
if(pcmdatas.containsKey(streamCode+"")){
|
pcmdatas.remove(streamCode+"");
|
}
|
lock.writeLock().unlock();
|
}
|
|
public static class PcmData{
|
public byte[] pcmData;
|
public int state = -1,streamCode;
|
|
public PcmData(int streamCode) {
|
this.streamCode = streamCode;
|
}
|
}
|
}
|