通过SDK操控缩略图

本文介绍如何借助无影云手机SDK来操控缩略图。

池化使用缩略图

说明

建议使用池化的方式来管理和复用缩略图。关于具体的实现方法,请参见各SDKDemo。

Android SDK

final WeakReference<ViewHolder> weakHolder = new WeakReference<>(holder);
CloudPhonePool.getInstance().requestThumbnail(mContext, phone, new ThumbnailCallback() {
    @Override
    public void onFinished(CloudPhone phone, Bitmap bitmap, int errorCode) {
        if (errorCode != 0) {
            return;
        }
        ViewHolder currentHolder = weakHolder.get();
        if (currentHolder != null) {
            currentHolder.ivThumbnail.setImageBitmap(bitmap);
        }
    }
});

Web SDK

  function requestThumbnail(params, callback) {
    if (!params.appInstanceId) {
      console.log(`Thumbnail params appInstanceId is null`);
      if (callback) callback(params, null, -1);
      return;
    }
    if (thumbnailSDKMap.has(params.appInstanceId)) {
      console.log(`Thumbnail ${params.appInstanceId}  reuse`);
      var item = thumbnailSDKMap.get(params.appInstanceId);
      if (item.canReuse()) {
        item.callback = callback;
        return;
      } else {
        item.stopThumbnail();
        thumbnailSDKMap.delete(params.appInstanceId);
      }
    }
    console.log(`Thumbnail ${params.appInstanceId}  new`);
    var item = new WYThumbnailImg(params, callback);
    thumbnailSDKMap.set(params.appInstanceId, item);
  }

iOS SDK

__weak typeof(cell) weakCell = cell;
[[ASPCloudPhonePool sharedInstance] requestThumbnail:engineItem completion:^(ASPCloudPhoneEngineItem *engineItem, UIImage *image, NSInteger errorCode) {
    weakCell.imageView.image = image;
}];

创建缩略图对象

Android SDK

public CloudPhoneEngine getCloudPhoneEngine(CloudPhone phone, Context context) {
    IASPEngineListener mListener = new IASPEngineListener() {
        @Override
        public void onConnectionSuccess(int connectionId) {
            Log.i(TAG, "onConnectionSuccess " + phone.getName());
        }
        @Override
        public void onConnectionFailure(int errorCode, String errorMsg) {
            Log.i(TAG, "onConnectionFailure errorCode:" + errorCode + " errorMsg:" + errorMsg + "," + phone.getName());
            String s = String.format("%s 错误码:%d", errorMsg, errorCode);
            showError(s);
        }

        @Override
        public void onEngineError(int errorCode, String errorMsg) {
            Log.i(TAG, "onEngineError errorCode:" + errorCode + " errorMsg:" + errorMsg + "," + phone.getName());
            String s = String.format("%s 错误码:%d", errorMsg, errorCode);
            showError(s);
        }

        @Override
        public void onDisconnected(int reason) {
            Log.i(TAG, "onDisconnected reason=" + reason + "," + phone.getName());
        }

        @Override
        public void onReconnect(int errorCode) {
            Log.i(TAG, "onReconnect: " + errorCode + "," + phone.getName());
        }

        @Override
        public void onFirstFrameRendered(long timeCostMS) {
            Log.i(TAG, "onFirstFrameRendered " + phone.getName());
        }

        @Override
        public void onPolicyUpdate(String policy) {
            Log.i(TAG, "policy=" + policy);
        }

        @Override
        public void onSessionSuccess() {
            Log.i(TAG, "onSessionSuccess " + phone.getName());
        }
        @Override
        public void onDisposed() {
            Log.i(TAG, "onDisposed");
        }
    };
    Bundle bundle = new Bundle();
    // 物理会议ID,以ai-开头。
    bundle.putString(CloudPhoneEngine.CONFIG_INSTANCEID, phone.getId());
    bundle.putString(CloudPhoneEngine.CONFIG_TICKET, phone.getTicket());
    bundle.putBoolean(CloudPhoneEngine.CONFIG_ENABLE_THUMBNAIL, true);
    bundle.putInt(CloudPhoneEngine.CONFIG_ENABLE_THUMBNAIL_WIDTH, 200);
      bundle.putInt(CloudPhoneEngine.CONFIG_ENABLE_THUMBNAIL_HEIGHT, 360);
    CloudPhoneEngine engine = new CloudPhoneEngine();
    engine.prepare(bundle, context);
    engine.getASPEngine().registerASPEngineListener(mListener);
    return engine;
}

Web SDK

说明

推荐通过获取缩略图的src来显示缩略图。创建缩略图时,需指定onThumbnailData回调。

const containerId = `thumbnail_${index}`;//每个缩略图必须保持唯一
const div = document.createElement("div");
div.id = containerId;
div.style.width = "200px";      // 固定宽度
div.style.height = "360px";     // 固定高度
div.style.border = "1px solid #ccc"; // (可选)边框
try {
  var thumbnail = new window.Wuying.ThumbnailSDK({
    appInstanceId: data.appInstanceId,//物理会话ID 以ai-开头
    ticket: data.ticket,
    containerId: containerId,
    defaultConfig: { width: 200, height: 360, fps: 1 }
  }, {
    onConnected: (data) => {
      console.log(`Thumbnail ${index} connected`, data);
    },
    onDisConnected: (data) => {
      console.log(`Thumbnail ${index} disconnected`, data);
    },
    onThumbnailData: (url) => {
      console.log(`Thumbnail ${index} url:`, url);
      img.src = url
    }
  });
} catch (error) {
  console.error(`Failed to initialize SDK for ${containerId}:`, error);
}

iOS SDK

ASPCloudPhoneEngine *engine = [[ASPCloudPhoneEngine alloc] init];
engine.engineDelegate = self;
engine.thumbnailDelegate = self;

启动缩略图

Android SDK

CloudPhoneEngine engine = getCloudPhoneEngine(phone, mContext);
engine.start();

iOS SDK

ASPCloudPhoneEngineItem *engineItem = [[ASPCloudPhoneEngineItem alloc] init];
engineItem.ticket = tciket;
engineItem.instanceId = instanceId; //以ai-开头
engineItem.isThumbnail = YES;
engineItem.thumbnailSize = CGSizeMake(200, 360);
[engine start:engineItem];

监听缩略图数据回调

Android SDK

engine.unregisterThumbnailListener();
// 使用弱引用包裹 ViewHolder,避免内存泄漏。
final WeakReference<ViewHolder> weakHolder = new WeakReference<>(holder);
engine.registerThumbnailListener(new IThumbnailListener() {
    @Override
    public void onThumbnailUpdate(byte[] rgba, int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
        // 通过弱引用安全获取 ViewHolder。
        ViewHolder currentHolder = weakHolder.get();
        if (currentHolder != null) {
            // 直接在主线程更新 UI(假设 onThumbnailUpdate 在非主线程调用)。
            ((Activity) currentHolder.itemView.getContext()).runOnUiThread(() -> {
                currentHolder.ivThumbnail.setImageBitmap(bitmap);
            });
        }
    }
});

iOS SDK

#pragma mark - ASPThumbnailDelegate
- (void)onThumbnailUpdate:(void*)buffer width:(uint32_t)width height:(uint32_t)height {
    if (buffer && width > 0 && height > 0) {
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate(buffer, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
        CGImageRef imageRef = CGBitmapContextCreateImage(context);
        self.bitmap = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        CGContextRelease(context);
        CGColorSpaceRelease(colorSpace);
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof (weakSelf) strongSelf = weakSelf;
            if (strongSelf.completion) strongSelf.completion(strongSelf.engineItem, strongSelf.bitmap, 0);
        });
    } else {
        NSLog(@"onThumbnailUpdate error");
    }
}

停止和释放缩略图

Android SDK

engine.dispose();

Web SDK

thumbnail.session.stop();

iOS SDK

- (void)stopThumbnail {
    if (self.engine) {
        self.engine.thumbnailDelegate = nil;
        [self.engine dispose];
        self.engine.engineDelegate = nil;
        self.engine = nil;
    }
}

相关文档