pedroSG94 / RootEncoder

RootEncoder for Android (rtmp-rtsp-stream-client-java) is a stream encoder to push video/audio to media servers using protocols RTMP, RTSP, SRT and UDP with all code written in Java/Kotlin
Apache License 2.0
2.52k stars 770 forks source link

eglswapbuffers failed at 1080p60 #1548

Closed noho501 closed 2 weeks ago

noho501 commented 1 month ago

Hi,

We’re encountering an issue with screen broadcasting at 1080p and 60fps (1080x1920). While some devices perform well, others fail after about 20 seconds with the error "eglswapbuffers failed." (Oppo A77s, Android 14)

When this error occurs, the recording continues in the background without capturing any video—only audio is recorded. We suspect this might be due to the hardware limitations of certain devices.

We have a couple of questions:

Thank you so much for your assistance.

z5743433068914_83dd54208014f2070bfe630165dab382

pedroSG94 commented 1 month ago

Hello,

Can you reproduce the error with 1920x1080 30fps? Reading on internet your device doesn't support 60fps.

You can check the codec fps this way:

    val codec = CodecUtil.getAllEncoders(CodecUtil.H264_MIME, true, true)[0]
    codec.supportedTypes.forEach {
      val fpsRange = codec.getCapabilitiesForType(it).videoCapabilities.supportedFrameRates
      Log.e("Pedro", "type: $it, fps: ${fpsRange.lower}-${fpsRange.upper}")
    }
noho501 commented 1 month ago

Hi,

I tested with 1920x1080 at 30fps, and it worked longer but still encountered an error. It works fine for an extended period without any errors at 1280x720 at 60fps. Do you have any suggestions for a callback error to notify the app user, or any other recommendations?

Thank you so much

pedroSG94 commented 1 month ago

Hello,

Maybe this is caused because your screen resolution exceed the device screen resolution. According with internet your screen is 1612×720px. I will try reproduce it with my devices using a resolution higher than my screen.

I haven't an error callback for this case. For now, I will try check the error and solve it

noho501 commented 1 month ago

Hi,

Please follow these steps:

  1. Set the resolution to 1080x1920 (or higher depending on the device), 60 fps, and a bitrate of 4000 * 1000.
  2. Start the screen broadcast and begin recording.
  3. Move the app to the background.
  4. Open another app (e.g., TikTok).
  5. Within approximately one minute, the error should appear.

Best regards,

pedroSG94 commented 1 month ago

I tested in 3 devices (pixel 4a, pixel 6a and pixel 7) with resolution 4k (3840x2160) and it is working fine. In this devices the screen is around 1080x2400px.

I will try with more devices but I don't understand the reason about why MediaProjection API can generate frames but opengl fail to swap buffers.

Can you tell me which line throw that error? https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt#L181 https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt#L206 https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt#L215 https://github.com/pedroSG94/RootEncoder/blob/master/library/src/main/java/com/pedro/library/view/GlStreamInterface.kt#L224

noho501 commented 3 weeks ago

Hi @pedroSG94 ,

We have attached some screenshots and a logcat. They might help you identify the issue.

IMG_2449 IMG_2450 IMG_2451 logcat_screen_broadcast.txt.txt

Thanks

pedroSG94 commented 3 weeks ago

Hello,

Thank you for the report. The txt was really informative. In your case the VideoEncoder seems to crash producing that surface associated to VideoEncoder has been invalid. For this reason swapbuffer fail, so this seem an error in your encoder. According with the logs, this could be caused by 2 main reasons:

For the first case we can try avoid that the system save battery with the app. You can go to settings > apps > RootEncoder (com.pedro.streamer.App) > remove pause app if not used and in use of battery select no restrictions.

Also, I did a branch with a change that automatically reset VideoEncoder if an error is detected: https://github.com/pedroSG94/RootEncoder/pull/1557

noho501 commented 3 weeks ago

Hi @pedroSG94 ,

Thank you for your feedback and solutions. As you anticipated, the app works fine and no errors occur if it is not put in the background. However, despite selecting "No restrictions" for battery settings and removing the pause for unused apps, the "eglswapbuffers failed" error still appears after 1-2 minutes when the app is in the background.

Will automatically resetting the VideoEncoder resolve this issue? When do you plan to merge this fix into the master branch and release a new version so that I can test it?

Thank you.

pedroSG94 commented 3 weeks ago

I'm not sure if that fix solve your error because I can't reproduce your case. First, I want know if it is working for you and then I will create a release with the fix.

Could you compile the app example in that branch and modify video configuration to reproduce the error and check if the problem is solved? You only need clone the project, swap to that branch (fix/videoencoder-crash) and modify configuration here: https://github.com/pedroSG94/RootEncoder/blob/master/app/src/main/java/com/pedro/streamer/screen/ScreenService.kt#L62

noho501 commented 3 weeks ago

Hi,

I will try it and get back to you.

Best regards

noho501 commented 2 weeks ago

Hi @pedroSG94

This pull request doesn't fully resolve my issue. Do you have any suggestions? Alternatively, would you be available to assist us via a remote meeting? I can share my screen, and we are happy to compensate you for your support.

Best regards

pedroSG94 commented 2 weeks ago

Hello,

Can you try reset using software video encoder?

      setEncoderErrorCallback(object: EncoderErrorCallback {
        override fun onCodecError(type: String, e: MediaCodec.CodecException) {
          if (type.startsWith("video")) {
            forceCodecType(CodecUtil.CodecType.SOFTWARE, CodecUtil.CodecType.FIRST_COMPATIBLE_FOUND)
            resetVideoEncoder()
          }
        }
      })
noho501 commented 2 weeks ago

Hello,

Can you try reset using software video encoder?

      setEncoderErrorCallback(object: EncoderErrorCallback {
        override fun onCodecError(type: String, e: MediaCodec.CodecException) {
          if (type.startsWith("video")) {
            forceCodecType(CodecUtil.CodecType.SOFTWARE, CodecUtil.CodecType.FIRST_COMPATIBLE_FOUND)
            resetVideoEncoder()
          }
        }
      })

Hi @pedroSG94 ,

Your suggested code is working great; however, we should lowercase the 'type' before comparing it, as the 'type' value is currently set as 'VideoEncoder'

if (type.lowercase().startsWith("video"))

Please make this change and merge it into the master branch, then upgrade the version. This will help others who encounter the same issue.

Thank you so much for your help.

pedroSG94 commented 2 weeks ago

Hello,

That is good to know. I'm doing a refactor to avoid compare strings using enums and return boolean depend if the reset was success or not. I will merge this with master.

noho501 commented 2 weeks ago

Hi @pedroSG94 ,

Thank you so much for your help.

Best regards

pedroSG94 commented 2 weeks ago

Hello,

I uploaded a new release with this change. Version 2.5.0 The current code is:

      setEncoderErrorCallback(object: EncoderErrorCallback {
        override fun onCodecError(type: CodecUtil.CodecTypeError, e: MediaCodec.CodecException) {
          if (type == CodecUtil.CodecTypeError.VIDEO_CODEC) {
            forceCodecType(CodecUtil.CodecType.SOFTWARE, CodecUtil.CodecType.FIRST_COMPATIBLE_FOUND)
            if (genericStream.resetVideoEncoder()) {
              //error doing reset stop stream or try reset using first compatible
            }
          }
        }
      })