Skip to content

Commit

Permalink
fix: [texture rendering] Fix texture id lost when widget is updated (#…
Browse files Browse the repository at this point in the history
…1543)

We get the texture id from the `VideoViewController` that is passed by
the user directly, which will be reset if the widget is updated, so we
need to maintain it internally.
  • Loading branch information
littleGnAl committed Feb 5, 2024
1 parent 7edcd59 commit f72552d
Show file tree
Hide file tree
Showing 3 changed files with 572 additions and 284 deletions.
141 changes: 114 additions & 27 deletions lib/src/impl/agora_video_view_impl.dart
@@ -1,5 +1,7 @@
import 'package:agora_rtc_engine/src/agora_base.dart';
import 'package:agora_rtc_engine/src/agora_media_base.dart';
import 'package:agora_rtc_engine/src/agora_rtc_engine.dart';
import 'package:agora_rtc_engine/src/agora_rtc_engine_ex.dart';

import 'package:agora_rtc_engine/src/impl/video_view_controller_impl.dart';
import 'package:agora_rtc_engine/src/render/agora_video_view.dart';
Expand Down Expand Up @@ -206,6 +208,76 @@ class _AgoraRtcRenderPlatformViewState extends State<AgoraRtcRenderPlatformView>
}
}

/// Delegate of the `VideoViewController` to handle the state of the texture rendering.
class _VideoViewControllerInternal with VideoViewControllerBaseMixin {
_VideoViewControllerInternal(this._controller);

final VideoViewControllerBaseMixin _controller;

int _textureId = kTextureNotInit;

@override
VideoCanvas get canvas => _controller.canvas;

@override
RtcConnection? get connection => _controller.connection;

@override
bool get useAndroidSurfaceView => _controller.useAndroidSurfaceView;

@override
bool get useFlutterTexture => _controller.useFlutterTexture;

@override
int getVideoSourceType() {
return _controller.getVideoSourceType();
}

@override
RtcEngine get rtcEngine => _controller.rtcEngine;

@override
bool get shouldHandlerRenderMode => _controller.shouldHandlerRenderMode;

@override
int getTextureId() => _textureId;

@override
void addInitializedCompletedListener(VoidCallback listener) =>
_controller.addInitializedCompletedListener(listener);

@override
void removeInitializedCompletedListener(VoidCallback listener) =>
_controller.removeInitializedCompletedListener(listener);

@override
Future<void> dispose() => _controller.dispose();

@override
Future<void> disposeRenderInternal() => _controller.disposeRenderInternal();

@override
Future<int> createTextureRender(
int uid,
String channelId,
int videoSourceType,
int videoViewSetupMode,
) =>
_controller.createTextureRender(
uid, channelId, videoSourceType, videoViewSetupMode);

@override
Future<void> initializeRender() async {
await _controller.initializeRender();
// Renew the texture id
_textureId = _controller.getTextureId();
}

@override
Future<void> setupView(int nativeViewPtr) =>
_controller.setupView(nativeViewPtr);
}

class AgoraRtcRenderTexture extends StatefulWidget {
const AgoraRtcRenderTexture({
Key? key,
Expand All @@ -227,6 +299,8 @@ class _AgoraRtcRenderTextureState extends State<AgoraRtcRenderTexture>

VoidCallback? _listener;

VideoViewControllerBaseMixin? _controllerInternal;

@override
void initState() {
super.initState();
Expand All @@ -235,24 +309,27 @@ class _AgoraRtcRenderTextureState extends State<AgoraRtcRenderTexture>
}

Future<void> _initialize() async {
if (!_controller(widget.controller).isInitialzed) {
final sourceController = widget.controller;
_controllerInternal = _VideoViewControllerInternal(
sourceController as VideoViewControllerBaseMixin);

if (!_controllerInternal!.isInitialzed) {
_listener ??= () {
_controller(widget.controller)
.removeInitializedCompletedListener(_listener!);
_controllerInternal!.removeInitializedCompletedListener(_listener!);
_listener = null;

_initializeTexture();
};
_controller(widget.controller)
.addInitializedCompletedListener(_listener!);
_controllerInternal!.addInitializedCompletedListener(_listener!);
} else {
await _initializeTexture();
}
}

Future<void> _initializeTexture() async {
final oldTextureId = widget.controller.getTextureId();
await widget.controller.initializeRender();
final textureId = widget.controller.getTextureId();
final oldTextureId = _controllerInternal!.getTextureId();
await _controllerInternal!.initializeRender();
final textureId = _controllerInternal!.getTextureId();
if (oldTextureId != textureId) {
_width = 0;
_height = 0;
Expand All @@ -272,34 +349,41 @@ class _AgoraRtcRenderTextureState extends State<AgoraRtcRenderTexture>

Future<void> _didUpdateWidget(
covariant AgoraRtcRenderTexture oldWidget) async {
if (!oldWidget.controller.isSame(widget.controller)) {
await oldWidget.controller.disposeRender();
if (_controllerInternal == null ||
_controllerInternal!.getTextureId() == kTextureNotInit) {
return;
}
if (!oldWidget.controller.isSame(widget.controller) &&
_controllerInternal != null) {
await _controllerInternal!.disposeRender();
await _initialize();
} else {
_controller(widget.controller).updateController(oldWidget.controller);
}
}

@override
void deactivate() {
super.deactivate();
if (_listener != null) {
_controller(widget.controller)
.removeInitializedCompletedListener(_listener!);
_controllerInternal?.removeInitializedCompletedListener(_listener!);
_listener = null;
}
}

@override
void dispose() {
widget.controller.disposeRender();
_controllerInternal?.disposeRender();
_controllerInternal = null;

super.dispose();
}

@override
void maybeCreateChannel(int viewId, String viewType) {
// Only handle render mode on macos at this time
final textureId = widget.controller.getTextureId();
if (_controllerInternal == null) {
return;
}
final textureId = _controllerInternal!.getTextureId();

methodChannel = MethodChannel('agora_rtc_engine/texture_render_$textureId');
methodChannel!.setMethodCallHandler((call) async {
if (call.method == 'onSizeChanged') {
Expand Down Expand Up @@ -394,25 +478,28 @@ class _AgoraRtcRenderTextureState extends State<AgoraRtcRenderTexture>
@override
Widget build(BuildContext context) {
Widget result = const SizedBox.expand();

if (widget.controller.getTextureId() != kTextureNotInit) {
if (_controllerInternal == null) {
return result;
}
final controller = _controllerInternal!;
if (controller.getTextureId() != kTextureNotInit) {
if (_height != 0 && _width != 0) {
result = buildTexure(widget.controller.getTextureId());
final renderMode = widget.controller.canvas.renderMode ??
RenderModeType.renderModeHidden;
result = buildTexure(controller.getTextureId());
final renderMode =
controller.canvas.renderMode ?? RenderModeType.renderModeHidden;

if (widget.controller.shouldHandlerRenderMode) {
if (controller.shouldHandlerRenderMode) {
result = _applyRenderMode(renderMode, result);
VideoMirrorModeType mirrorMode;
if (widget.controller.isLocalUid) {
mirrorMode = widget.controller.canvas.mirrorMode ??
if (controller.isLocalUid) {
mirrorMode = controller.canvas.mirrorMode ??
VideoMirrorModeType.videoMirrorModeEnabled;
} else {
mirrorMode = widget.controller.canvas.mirrorMode ??
mirrorMode = controller.canvas.mirrorMode ??
VideoMirrorModeType.videoMirrorModeDisabled;
}

final sourceType = widget.controller.canvas.sourceType ??
final sourceType = controller.canvas.sourceType ??
VideoSourceType.videoSourceCameraPrimary;

result = _applyMirrorMode(mirrorMode, result, sourceType);
Expand Down
2 changes: 1 addition & 1 deletion test_shard/integration_test_app/android/build.gradle
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

0 comments on commit f72552d

Please sign in to comment.