/*
* Copyright (c) 2023-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "AceTexture.h"
#include "base/log/log.h"
#define TEXTURE_FLAG @"texture@"
#define PARAM_AND @"#HWJS-&-#"
#define PARAM_EQUALS @"#HWJS-=-#"
#define PARAM_BEGIN @"#HWJS-?-#"
#define METHOD @"method"
#define EVENT @"event"
#define SUCCESS @"success"
#define FAIL @"fail"
#define KEY_SOURCE @"src"
#define KEY_VALUE @"value"
#define FILE_SCHEME @"file://"
#define HAP_SCHEME @"/"
@interface AceTexture()
@property(nonatomic, assign) int64_t textureId;
@property(nonatomic, assign) int32_t textureName;
@property(nonatomic, assign) int32_t instanceId;
@property(nonatomic, copy) IAceOnResourceEvent onEvent;
@property(nonatomic, copy) IAceTextureAttachEventCallback attachEventCallback;
@property (nonatomic, strong) NSMutableDictionary<NSString*, IAceOnCallSyncResourceMethod>* callMethodMap;
@end
@implementation AceTexture
- (instancetype)initWithEvents:(IAceOnResourceEvent)callback
textureId:(int64_t)textureId abilityInstanceId:(int32_t)abilityInstanceId
{
if (self = [super init]) {
self.onEvent = callback;
self.textureId = textureId;
self.instanceId = abilityInstanceId;
self.textureName = -1;
self.callMethodMap = [NSMutableDictionary dictionary];
__weak AceTexture* weakSelf = self;
IAceOnCallSyncResourceMethod callSetTextureSize = ^NSString*(NSDictionary* param) {
if (weakSelf) {
return [weakSelf setSurfaceBounds:param];
} else {
LOGE("AceSurfaceView: setSurfaceBounds fail");
return FAIL;
}
};
[self.callMethodMap setObject:[callSetTextureSize copy] forKey:[self method_hashFormat:@"setTextureSize"]];
IAceOnCallSyncResourceMethod callAttachToGLContext = ^NSString*(NSDictionary* param) {
if (weakSelf) {
return [weakSelf attachToGLContext:param];
} else {
LOGE("AceSurfaceView: attachToGLContext fail");
return FAIL;
}
};
NSString *methodString = [self method_hashFormat:@"attachToGLContext"];
[self.callMethodMap setObject:[callAttachToGLContext copy] forKey:methodString];
}
return self;
}
- (NSDictionary<NSString*, IAceOnCallSyncResourceMethod>*)getCallMethod
{
return [self.callMethodMap copy];
}
- (void)refreshPixelBuffer
{
[self markTextureAvailable];
}
- (void)addAttachEventCallback:(IAceTextureAttachEventCallback)callback
{
self.attachEventCallback = callback;
}
- (NSString*)attachToGLContext:(NSDictionary*)params
{
if (!params) {
LOGE("AceTexture: attachToGLContext failed: params is null");
return FAIL;
}
self.textureName = [params[@"textureId"] longLongValue];
if (self.attachEventCallback) {
self.attachEventCallback(self.textureName);
}
return SUCCESS;
}
- (void)releaseObject
{
LOGI("AceTextureReleaseObject");
if (self.videoOutput) {
self.videoOutput = nil;
}
if (self.callMethodMap) {
for (id key in self.callMethodMap) {
IAceOnCallSyncResourceMethod block = [self.callMethodMap objectForKey:key];
block = nil;
}
[self.callMethodMap removeAllObjects];
self.callMethodMap = nil;
}
self.onEvent = nil;
}
- (void)dealloc
{
LOGI("AceTexture dealloc instanceId=%{public}lld", self.instanceId);
}
- (void)markTextureAvailable
{
if (self.onEvent) {
NSString *param = [NSString stringWithFormat:@"instanceId=%lld&textureId=%lld",
self.instanceId, self.textureId];
NSString *prepared_method_hash = [NSString stringWithFormat:@"%@%lld%@%@%@%@",
TEXTURE_FLAG, self.textureId, EVENT, PARAM_EQUALS, @"markTextureAvailable", PARAM_BEGIN];
self.onEvent(prepared_method_hash, param);
}
}
- (NSString*)setSurfaceBounds:(NSDictionary*)params
{
return SUCCESS;
}
- (NSString *)method_hashFormat:(NSString *)method
{
return [NSString stringWithFormat:@"%@%lld%@%@%@%@",
TEXTURE_FLAG, self.textureId, METHOD, PARAM_EQUALS, method, PARAM_BEGIN];
}
- (AVPlayerItemVideoOutput *)videoOutput
{
if(!_videoOutput){
NSDictionary* pixBuffAttributes = @{
(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferIOSurfacePropertiesKey : @{}
};
_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:pixBuffAttributes];
}
return _videoOutput;
}
- (CVPixelBufferRef)getPixelBuffer
{
CMTime outputItemTime = [self.videoOutput itemTimeForHostTime:CACurrentMediaTime()];
if ([self.videoOutput hasNewPixelBufferForItemTime:outputItemTime]) {
return [self.videoOutput copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL];
} else {
return NULL;
}
}
@end