Runt
2025-04-13 8c0f96b20f4cf44d230b373f51261566af02de8e
app/src/main/java/com/runt/live/ui/stream/LiveViewModel.kt
@@ -1,13 +1,27 @@
package com.runt.live.ui.stream
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
import android.view.SurfaceHolder
import com.runt.live.R
import com.runt.live.cpp.LiveMiniView
import com.runt.live.cpp.LiveMiniView.FRAME_HEIGHT
import com.runt.live.cpp.LiveMiniView.FRAME_WIDTH
import com.runt.live.data.StreamWindow
import com.runt.live.enum.LiveState
import com.runt.live.enum.StreamType
import com.runt.live.native.LivePuller
import com.runt.live.native.LivePusher
import com.runt.live.native.MediaPlayer
import com.runt.live.ui.stream.LiveLayoutView.Companion.subStreamsState
import com.runt.live.util.BitmapUtils
import com.runt.open.mvi.base.model.BaseViewModel
import java.nio.ByteBuffer
import java.util.concurrent.ConcurrentSkipListMap
import kotlin.concurrent.thread
/**
@@ -37,8 +51,6 @@
        players.put(streamWindow.id,player)
        player.load(filePath)
    }
    fun closePlayer(streamCode : Int){
        players.get(streamCode)?.let {
@@ -94,6 +106,7 @@
        }
        pushers.remove(code);
    }
    fun closePusher(url : String){
        var code = getPusherCode(url);
        if(code > -1){
@@ -138,6 +151,85 @@
        LiveMiniView.native_remove_mini_view(streamCode)
    }
    fun updateText(streamWindow : StreamWindow){
        thread {
            var bytes = LiveMiniView.native_get_cut_frame(subStreamsState.value.indexOf(streamWindow),streamWindow.id)
            var bitmap = BitmapUtils.instance!!.textToBitmap(streamWindow.remark!!,130f,getActivity().resources.getColor(R.color.white)!!,getActivity().resources.getColor(R.color.transparent)!!)
            var cutwidth : Int = (FRAME_WIDTH * streamWindow.viewRateState.value).toInt()
            var cutHeight : Int = (FRAME_WIDTH / ( streamWindow.sizeState.value.x * 1.0 /  streamWindow.sizeState.value.y ) * streamWindow.viewRateState.value).toInt()
            //涉及到 YUV 4:2:0 格式的图像时,宽度和高度通常需要是 2 的倍数
            if (cutwidth % 2 == 1) {
                cutwidth += 1
            }
            if (cutHeight % 2 == 1) {
                cutHeight += 1
            }
            if (cutwidth > FRAME_WIDTH) {
                cutwidth = FRAME_WIDTH
            }
            if (cutHeight > FRAME_HEIGHT) {
                cutHeight = FRAME_HEIGHT
            }
            val cutBitmap = Bitmap.createBitmap(cutwidth, cutHeight, Bitmap.Config.ARGB_8888)
            Log.e(TAG , "updateText: ${cutwidth}x${cutHeight} ${streamWindow.sizeState.value} ${( streamWindow.sizeState.value.x * 1.0 /  streamWindow.sizeState.value.y )}", )
            val buffer = ByteBuffer.wrap(bytes)
            cutBitmap.copyPixelsFromBuffer(buffer)
            val dstBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
            val canvas = Canvas(dstBitmap)
            // 构建目标区域
            val destRect = Rect(0,0,dstBitmap.width,dstBitmap.height)
            // 绘制小图到大图
            canvas.drawBitmap(cutBitmap, null, destRect, null)
            //canvas.drawBitmap(bitmap, null, destRect, null)
            val width : Int = bitmap.getWidth()
            val height : Int = bitmap.getHeight()
            val argb = IntArray(width * height)
            dstBitmap.getPixels(argb , 0 , width , 0 , 0 , width , height)
            //LiveMiniView.native_push_nv21(streamWindow.id,BitmapUtils.instance!!.argbToNV21(argb,width,height));
        }
    }
    fun openText(streamWindow : StreamWindow){
        var bitmap = BitmapUtils.instance!!.textToBitmap(streamWindow.remark!!,130f,getActivity().resources.getColor(R.color.white)!!,getActivity().resources.getColor(R.color.transparent)!!)
        streamWindow.hasAudioState.value = false;
        streamWindow.hasVideoState.value = true;
        streamWindow.sizeState.value = Point(bitmap.width,bitmap.height);
        streamWindow.surfaceCallBack = object : SurfaceHolder.Callback {
            override fun surfaceCreated(holder : SurfaceHolder) {
                Log.w(TAG , "surfaceCreated: ${holder.hashCode()}")
            }
            override fun surfaceChanged(holder : SurfaceHolder , format : Int , width : Int , height : Int) {
                streamWindow.surfaceHolder = holder;
                streamWindow.listener?.onSurfaceUpdate?.let { it(holder.surface) }
                thread {
                    BitmapUtils.instance!!.cavansSurface(holder,bitmap);
                    val width : Int = bitmap.getWidth()
                    val height : Int = bitmap.getHeight()
                    val argb = IntArray(width * height)
                    bitmap.getPixels(argb , 0 , width , 0 , 0 , width , height)
                    LiveMiniView.native_push_nv21(streamWindow.id,BitmapUtils.instance!!.argbToNV21(argb,width,height));
                }
                //Log.w(TAG , "surfaceChanged: ${holder.hashCode()}" , )
            }
            override fun surfaceDestroyed(holder : SurfaceHolder) {
                streamWindow.surfaceHolder = null;
                streamWindow.listener?.onSurfaceUpdate?.let { it(null) }
                //Log.w(TAG , "surfaceDestroyed: ${holder.hashCode()} ${id}" , )
            }
        }
        streamWindow.listener?.onStarted?.invoke()
    }
    /**
     * 拉流
     */