Agora 4.2.0 AR 核心

问题描述 投票:0回答:1

我正在尝试将 AR core 与 Agora 集成。 我对 OpenGL 概念完全是新手。

到目前为止我有什么? 我在我的应用程序中使用此示例 AR Kotlin

AR 核心一切正常 我有这个课程用于将自定义视频发送到 Agora

    package com.ex.ab.arcore.kotlin.common.helpers

import android.app.ActivityManager
import android.content.Context
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.util.Log
import timber.log.Timber
import java.nio.ByteBuffer
import java.nio.ByteOrder


object GLSurfaceViewToI420Converter {
        private const val TAG = "GLSurfaceToI420"
        private const val BYTES_PER_PIXEL = Float.SIZE_BYTES // Assuming RGBA format
        private const val YUV_BYTES_PER_PIXEL = 3 // I420 format


    fun isAvailable(context: Context): Boolean {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val configurationInfo = activityManager.deviceConfigurationInfo
        return configurationInfo.reqGlEsVersion >= 0x30000
    }


    fun convertGLSurfaceViewToByteArray(width: Int, height: Int): ByteArray? {
        Timber.d("CHECK width = $width height = $height")
//        val width = glSurfaceView.width
//        val height = glSurfaceView.height
        val rgbaBuffer = ByteBuffer.allocateDirect(width * height * 4)
        rgbaBuffer.order(ByteOrder.nativeOrder())

        // Read pixel data from the framebuffer
        GLES30.glReadPixels(
            0,
            0,
            width,
            height,
            GLES30.GL_RGBA,
            GLES30.GL_UNSIGNED_BYTE,
            rgbaBuffer
        )

        // Convert to byte array
        val byteArray = ByteArray(rgbaBuffer.remaining())
        rgbaBuffer[byteArray]
        return byteArray
    }

    fun convertGLSurfaceViewToI420(width: Int, height: Int): ByteBuffer? {
        val framebuffer = IntArray(1)
        GLES30.glGenFramebuffers(1, framebuffer, 0)

        if (framebuffer[0] == 0) {
            Log.e(TAG, "CHECK Failed to generate framebuffer")
            return null
        }

        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebuffer[0])

        val texture = IntArray(1)
        GLES30.glGenTextures(1, texture, 0)

        if (texture[0] == 0) {
            Log.e(TAG, "CHECK  Failed to generate texture")
            GLES30.glDeleteFramebuffers(1, framebuffer, 0)
            return null
        }

        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texture[0])

        GLES30.glTexImage2D(
            GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, width, height, 0,
            GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null
        )

        GLES30.glFramebufferTexture2D(
            GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D, texture[0], 0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e(TAG, "CHECK Framebuffer is not complete")
            GLES30.glDeleteTextures(1, texture, 0)
            GLES30.glDeleteFramebuffers(1, framebuffer, 0)
            return null
        }

        val buffer = ByteBuffer.allocateDirect(width * height * YUV_BYTES_PER_PIXEL)
        buffer.order(ByteOrder.nativeOrder())

        val rgbaBuffer = ByteBuffer.allocateDirect(width * height * BYTES_PER_PIXEL)
        rgbaBuffer.order(ByteOrder.nativeOrder())

        GLES30.glReadPixels(0, 0, width, height, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, rgbaBuffer)

        convertRGBAToI420(rgbaBuffer, buffer, width, height)

        GLES30.glDeleteTextures(1, texture, 0)
        GLES30.glDeleteFramebuffers(1, framebuffer, 0)

        return buffer
    }

    private fun convertRGBAToI420(rgbaBuffer: ByteBuffer, i420Buffer: ByteBuffer, width: Int, height: Int) {
        val ySize = width * height
        val uvSize = ySize / 4

        for (i in 0 until height) {
            for (j in 0 until width) {
                val rgbaIndex = (i * width + j) * BYTES_PER_PIXEL
                val yIndex = i * width + j
                val uvIndex = ySize + (i / 2) * (width / 2) + (j / 2) * 2

                val r = rgbaBuffer.get(rgbaIndex).toInt() and 0xFF
                val g = rgbaBuffer.get(rgbaIndex + 1).toInt() and 0xFF
                val b = rgbaBuffer.get(rgbaIndex + 2).toInt() and 0xFF

                val y = ((66 * r + 129 * g + 25 * b + 128) shr 8) + 16
                val u = ((-38 * r - 74 * g + 112 * b + 128) shr 8) + 128
                val v = ((112 * r - 94 * g - 18 * b + 128) shr 8) + 128

                i420Buffer.put(yIndex, y.toByte())
                i420Buffer.put(uvIndex, u.toByte())
                i420Buffer.put(uvIndex + 1, v.toByte())
            }
        }
    }
}

在OnDrawFrame()中,如果我使用

convertGLSurfaceViewToI420
这个方法,它会给出
Log.e(TAG, "CHECK Failed to generate framebuffer")
这个错误,如果我使用
convertGLSurfaceViewToByteArray
这个方法,我只能在另一端看到绿屏。

我发送缓冲区的代码看起来像这样

activity.lifecycleScope.launch(Dispatchers.Main) {
      val width = activity.localSurfaceView.width
      val height = activity.localSurfaceView.height
//      withContext(Dispatchers.IO) {
      val isAvailable = GLSurfaceViewToI420Converter.isAvailable(activity.requireActivity())
      Timber.d("CHECK isavailable $isAvailable")
        val i420Buffer: ByteBuffer? =
          GLSurfaceViewToI420Converter.convertGLSurfaceViewToI420(width, height)
        i420Buffer?.let { data ->
      val byteArray = ByteArray(data.remaining())
      data.get(byteArray)
//      byteArray?.let {
        onByteArrayAvailable?.onByteDataAvailable(byteArray, width, height)
      } ?: kotlin.run {
        Timber.d("CHECK i420 is null")
      }
//      }
    }

我没有收到任何异常错误。还检查了 GLError。 如果有人能指出我的解决方案,那就太好了。

android augmented-reality arcore glsurfaceview agora
1个回答
0
投票

似乎有一些问题可能导致您遇到的问题。让我们一步一步地看一下:

帧缓冲区生成问题: 错误消息“无法生成帧缓冲区”表示帧缓冲区生成存在问题。这可能是由于 OpenGL ES 上下文初始化不正确或不受支持的 OpenGL ES 版本造成的。确保您已正确初始化 OpenGL 上下文并且设备支持 OpenGL ES 3.0 或更高版本。

绿屏问题: 如果您仅在接收端看到绿屏,则表明颜色转换或数据传输可能存在问题。确保您向 Agora 发送正确的字节数组数据,并确保接收端正确地将数据解释为 I420 格式。

仔细检查convertRGBAToI420函数中的颜色转换过程。确保 RGB 值正确转换为 YUV 值并存储在 i420Buffer 中。

字节顺序问题: 将字节数组转换为 ByteBuffer 时请注意字节顺序,反之亦然。 ConvertGLSurfaceViewToByteArray 方法返回字节数组,但 ConvertGLSurfaceViewToI420 方法需要 ByteBuffer。确保在创建 ByteBuffer 以及从 ByteBuffer 检索字节数组时使用正确的字节顺序。

您可以尝试修改convertGLSurfaceViewToI420方法以直接接受字节数组而不是ByteBuffer。然后,您可以使用正确的字节顺序从字节数组创建 ByteBuffer。

以下是对 ConvertGLSurfaceViewToI420 方法的修改示例:

fun convertGLSurfaceViewToI420(width: Int, height: Int): ByteBuffer? {

        // ...
        val byteArray = convertGLSurfaceViewToByteArray(width, height)
        byteArray?.let {
            val buffer = ByteBuffer.allocateDirect(width * height * YUV_BYTES_PER_PIXEL).order(ByteOrder.nativeOrder())
            buffer.put(byteArray).position(0)
            // ...
            return buffer
        }
        return null
    }
    ```

声网集成: 确保您正确地将字节数组数据发送到 Agora 进行渲染。验证您使用的是适当的 Agora API 并且传递的是正确的数据格式 (I420) 和尺寸(宽度和高度)。

考虑到这些要点,请相应地检查并修改您的代码。另外,请检查Agora或Android系统提供的任何相关日志或错误消息,这可以进一步帮助诊断问题。

© www.soinside.com 2019 - 2024. All rights reserved.