Runt
2025-04-14 d0aec90013f06ed4b258235bcabe02e50550271a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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;
        }
    }
}