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 pcmdatas = new HashMap<>(); static HashMap 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 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; } } }