Skip to content

Commit

Permalink
fix: [android/ios] Fix crash due to AgoraVideoView.dispose call befor…
Browse files Browse the repository at this point in the history
…e RtcEngine.setupxxVideo is completed (#1224)

fix: [android/ios] Fix crash due to AgoraVideoView.dispose call before RtcEngine.setupxxVideo is completed (#1224)
  • Loading branch information
littleGnAl authored Jul 25, 2023
1 parent 98a2b8e commit f50c4e4
Show file tree
Hide file tree
Showing 14 changed files with 526 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package io.agora.agora_rtc_ng;

import android.content.Context;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import io.agora.iris.IrisApiEngine;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
Expand All @@ -23,14 +21,18 @@ public class AgoraPlatformViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
private final PlatformViewProvider viewProvider;

private final VideoViewController controller;

AgoraPlatformViewFactory(
String viewType,
BinaryMessenger messenger,
PlatformViewProvider viewProvider) {
PlatformViewProvider viewProvider,
VideoViewController controller) {
super(StandardMessageCodec.INSTANCE);
this.viewType = viewType;
this.messenger = messenger;
this.viewProvider = viewProvider;
this.controller = controller;
}

interface PlatformViewProvider {
Expand Down Expand Up @@ -59,34 +61,41 @@ static class AgoraPlatformView implements PlatformView, MethodChannel.MethodCall

private final MethodChannel methodChannel;

private final VideoViewController controller;

private SimpleRef viewRef;

private long platformViewPtr;
private final int platformViewId;

AgoraPlatformView(Context context,
String viewType,
int viewId,
PlatformViewProvider viewProvider,
BinaryMessenger messenger) {
BinaryMessenger messenger,
VideoViewController controller) {
methodChannel = new MethodChannel(messenger, "agora_rtc_ng/" + viewType + "_" + viewId);
methodChannel.setMethodCallHandler(this);
innerView = viewProvider.provide(context);
this.controller = controller;
this.platformViewId = viewId;
this.viewRef = controller.createPlatformRender(viewId, context, viewProvider);

innerView = (View) viewRef.getValue();
parentView = new FrameLayout(context);
parentView.addView(innerView);

platformViewPtr = IrisApiEngine.GetJObjectAddress(innerView);
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (call.method.equals("getNativeViewPtr")) {
long platformViewPtr = 0L;
if (viewRef != null) {
this.controller.addPlatformRenderRef(this.platformViewId);
platformViewPtr = viewRef.getNativeHandle();
}

result.success(platformViewPtr);
} else if (call.method.equals("deleteNativeViewPtr")) {
if (platformViewPtr != 0L) {
IrisApiEngine.FreeJObjectByAddress(platformViewPtr);
platformViewPtr = 0;
}
parentView.removeAllViews();
innerView = null;
// Do nothing.
result.success(0);
}
}
Expand All @@ -99,7 +108,11 @@ public View getView() {

@Override
public void dispose() {

this.controller.dePlatformRenderRef(this.platformViewId);
viewRef = null;
parentView.removeAllViews();
parentView = null;
innerView = null;
}
}

Expand All @@ -111,7 +124,8 @@ public PlatformView create(@Nullable Context context, int viewId, @Nullable Obje
viewType,
viewId,
viewProvider,
messenger
messenger,
this.controller
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,25 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "agora_rtc_ng");
channel.setMethodCallHandler(this);
flutterPluginBindingRef = new WeakReference<>(flutterPluginBinding);
videoViewController = new VideoViewController(flutterPluginBinding.getBinaryMessenger());

flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(
"AgoraTextureView",
new AgoraPlatformViewFactory(
"AgoraTextureView",
flutterPluginBinding.getBinaryMessenger(),
new AgoraPlatformViewFactory.PlatformViewProviderTextureView()));
new AgoraPlatformViewFactory.PlatformViewProviderTextureView(),
this.videoViewController));

flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(
"AgoraSurfaceView",
new AgoraPlatformViewFactory(
"AgoraSurfaceView",
flutterPluginBinding.getBinaryMessenger(),
new AgoraPlatformViewFactory.PlatformViewProviderSurfaceView()));
new AgoraPlatformViewFactory.PlatformViewProviderSurfaceView(),
this.videoViewController));


videoViewController = new VideoViewController(flutterPluginBinding.getBinaryMessenger());
}

@Override
Expand Down
116 changes: 112 additions & 4 deletions android/src/main/java/io/agora/agora_rtc_ng/VideoViewController.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,129 @@
package io.agora.agora_rtc_ng;

import android.content.Context;
import android.view.View;

import androidx.annotation.NonNull;

import java.util.HashMap;
import java.util.Map;

import io.agora.iris.IrisApiEngine;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

class SimpleRef {
private Object value;
private int refCount;
private long nativeHandle;

SimpleRef(Object value) {
this.value = value;
this.refCount = 1;
this.nativeHandle = IrisApiEngine.GetJObjectAddress(this.value);
}

int getRefCount() {
return this.refCount;
}

Object getValue() {
return value;
}

long getNativeHandle() {
return this.nativeHandle;
}

void addRef() {
++this.refCount;
}

void deRef() {
--this.refCount;
}

void releaseRef() {
IrisApiEngine.FreeJObjectByAddress(this.nativeHandle);
this.nativeHandle = 0L;
this.value = null;
this.refCount = 0;
}
}

class PlatformRenderPool {

private final Map<Integer, SimpleRef> renders = new HashMap<>();
SimpleRef createView(int platformViewId,
Context context,
AgoraPlatformViewFactory.PlatformViewProvider viewProvider) {
final View view = viewProvider.provide(context);

final SimpleRef simpleRef = new SimpleRef(view);
renders.put(platformViewId, simpleRef);

return simpleRef;
}

boolean addViewRef(int platformViewId) {
if (renders.containsKey(platformViewId)) {
final SimpleRef simpleRef = renders.get(platformViewId);

//noinspection ConstantConditions
simpleRef.addRef();
return true;
}
return false;
}

boolean deViewRef(int platformViewId) {
if (renders.containsKey(platformViewId)) {
final SimpleRef simpleRef = renders.get(platformViewId);

//noinspection ConstantConditions
simpleRef.deRef();

if (simpleRef.getRefCount() <= 0) {
simpleRef.releaseRef();
renders.remove(platformViewId);
}

return true;
}

return false;
}
}

public class VideoViewController implements MethodChannel.MethodCallHandler {

private final MethodChannel methodChannel;
private final PlatformRenderPool pool;

VideoViewController(BinaryMessenger binaryMessenger) {
methodChannel = new MethodChannel(binaryMessenger, "agora_rtc_ng/video_view_controller");
methodChannel.setMethodCallHandler(this);
pool = new PlatformRenderPool();
}

private long createPlatformRender(){
return 0L;
public SimpleRef createPlatformRender(
int platformViewId,
Context context,
AgoraPlatformViewFactory.PlatformViewProvider viewProvider) {
return this.pool.createView(platformViewId, context, viewProvider);
}

private boolean destroyPlatformRender(long platformRenderId) {
return true;
public boolean destroyPlatformRender(int platformRenderId) {
return this.pool.deViewRef(platformRenderId);
}

public boolean addPlatformRenderRef(int platformViewId) {
return this.pool.addViewRef(platformViewId);
}

public boolean dePlatformRenderRef(int platformViewId) {
return this.pool.deViewRef(platformViewId);
}

private long createTextureRender() {
Expand All @@ -40,6 +143,11 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
case "detachVideoFrameBufferManager":
result.success(true);
break;
case "dePlatfromViewRef":
int platformViewId = (int) call.arguments;
this.dePlatformRenderRef(platformViewId);
result.success(true);
break;

case "createTextureRender":
case "destroyTextureRender":
Expand Down
9 changes: 5 additions & 4 deletions ios/Classes/AgoraRtcNgPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
instance.registrar = registrar;
[registrar addMethodCallDelegate:instance channel:channel];

[registrar registerViewFactory:[[AgoraSurfaceViewFactory alloc]
initWith:[registrar messenger]]
withId:@"AgoraSurfaceView"];
instance.videoViewController = [[VideoViewController alloc] initWith:registrar.textures messenger:registrar.messenger];

instance.videoViewController = [[VideoViewController alloc] initWith:registrar.textures messenger:registrar.messenger];
[registrar registerViewFactory:[[AgoraSurfaceViewFactory alloc]
initWith:[registrar messenger]
controller:instance.videoViewController]
withId:@"AgoraSurfaceView"];
}

- (void)getAssetAbsolutePath:(FlutterMethodCall *)call
Expand Down
4 changes: 3 additions & 1 deletion ios/Classes/AgoraSurfaceViewFactory.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#import <Flutter/Flutter.h>
#import "VideoViewController.h"

@interface AgoraSurfaceViewFactory : NSObject <FlutterPlatformViewFactory>

- (instancetype)initWith:(NSObject<FlutterBinaryMessenger> *)messenger;
- (instancetype)initWith:(NSObject<FlutterBinaryMessenger> *)messenger
controller:(VideoViewController *)controller;

@end
Loading

0 comments on commit f50c4e4

Please sign in to comment.