This commit is contained in:
启星
2025-08-08 10:49:36 +08:00
parent 6400cf78bb
commit b5ce3d580a
8780 changed files with 978183 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import <AVKit/AVKit.h>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
BGM信息
*/
@interface TUIMultimediaBGM : NSObject <NSCopying>
@property(nonatomic) NSString *name;
@property(nullable, nonatomic) NSString *lyric; // 歌词
@property(nullable, nonatomic) NSString *source; // 音乐来源或歌手
@property(nonatomic) float startTime;
@property(nonatomic) float endTime;
@property(nullable, nonatomic) AVAsset *asset;
- (instancetype)initWithName:(NSString *)name lyric:(nullable NSString *)lyric source:(nullable NSString *)source asset:(nullable AVAsset *)asset;
@end
@interface TUIMultimediaBGMGroup : NSObject
@property(nonatomic) NSString *name;
@property(nonatomic) NSArray<TUIMultimediaBGM *> *bgmList;
- (instancetype)initWithName:(NSString *)name bgmList:(NSArray<TUIMultimediaBGM *> *)bgmList;
+ (NSArray<TUIMultimediaBGMGroup *> *)loadBGMConfigs;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaBGM.h"
#import <TUICore/NSDictionary+TUISafe.h>
#import "TUIMultimediaPlugin/NSArray+Functional.h"
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
@implementation TUIMultimediaBGM
- (instancetype)initWithName:(NSString *)name lyric:(NSString *)lyric source:(NSString *)source asset:(nullable AVAsset *)asset {
self = [super init];
_name = name;
_lyric = lyric;
_source = source;
_asset = asset;
_startTime = 0;
_endTime = 0;
if (asset != nil && asset.duration.timescale != 0) {
_endTime = asset.duration.value / asset.duration.timescale;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
TUIMultimediaBGM *cp = [[[self class] allocWithZone:zone] init];
cp->_name = _name;
cp->_lyric = _lyric;
cp->_source = _source;
cp->_asset = _asset;
cp->_startTime = _startTime;
cp->_endTime = _endTime;
return cp;
}
@end
@implementation TUIMultimediaBGMGroup
- (instancetype)initWithName:(NSString *)name bgmList:(NSArray<TUIMultimediaBGM *> *)bgmList {
self = [super init];
_name = name;
_bgmList = bgmList;
return self;
}
+ (TUIMultimediaBGMGroup *)loadGroupFromJsonDict:(NSDictionary *)dict {
NSString *name = dict[@"bgm_type_name"];
NSArray *jsonMusicList = dict[@"bgm_item_list"];
NSMutableArray<TUIMultimediaBGM *> *bgmList = [NSMutableArray array];
for (NSDictionary *music in jsonMusicList) {
NSString *musicFile = music[@"item_bgm_path"];
NSURL *url = [TUIMultimediaCommon getURLByResourcePath:musicFile];
AVAsset *asset = [AVAsset assetWithURL:url];
TUIMultimediaBGM *bgm = [[TUIMultimediaBGM alloc] initWithName:music[@"item_bgm_name"] lyric:@"" source:music[@"item_bgm_author"] asset:asset];
double startTime = [(NSNumber *)music[@"item_start_time"] doubleValue];
double endTime = [(NSNumber *)music[@"item_end_time"] doubleValue];
if (startTime >= 0 && endTime > 0 && endTime > startTime) {
bgm.startTime = MIN(bgm.endTime, startTime);
bgm.endTime = MIN(bgm.endTime, endTime);
}
if (asset.duration.value != 0) {
[bgmList addObject:bgm];
}
}
return [[TUIMultimediaBGMGroup alloc] initWithName:name bgmList:bgmList];
}
+ (NSArray<TUIMultimediaBGMGroup *> *)loadBGMConfigs {
NSData *jsonData = [NSData dataWithContentsOfFile:[TUIMultimediaCommon.bundle pathForResource:[[TUIMultimediaConfig sharedInstance] getBGMConfigFilePath] ofType:@"json"]];
NSError *err = nil;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
if (err || ![dic isKindOfClass:[NSDictionary class]]) {
NSLog(@"[TUIMultimedia] Json parse failed: %@", err);
return nil;
}
NSMutableArray<TUIMultimediaBGMGroup *> *groupList = [NSMutableArray array];
NSArray *jsonGroupList = dic[@"bgm_list"];
for (NSDictionary *jsonGroup in jsonGroupList) {
[groupList addObject:[self loadGroupFromJsonDict:jsonGroup]];
}
return groupList;
}
@end

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class TUIMultimediaPasterGroupConfig;
@class TUIMultimediaPasterItemConfig;
/**
贴纸配置
*/
@interface TUIMultimediaPasterConfig : NSObject
@property(nonatomic) NSArray<TUIMultimediaPasterGroupConfig *> *groups;
+ (TUIMultimediaPasterConfig *)loadConfig;
+ (void)saveConfig:(TUIMultimediaPasterConfig *)config;
+ (NSURL *)saveCustomPaster:(UIImage *)img;
+ (void)removeCustomPaster:(TUIMultimediaPasterItemConfig *)paster;
@end
/**
贴纸组,包含一系列贴纸
*/
@interface TUIMultimediaPasterGroupConfig : NSObject <NSSecureCoding>
@property(nonatomic) NSString *name;
@property(nullable, nonatomic) NSURL *iconUrl;
@property(nonatomic) BOOL customizable; // 是否可由用户添加贴纸到此组
@property(nonatomic) NSArray<TUIMultimediaPasterItemConfig *> *itemList;
- (instancetype)initWithName:(NSString *)name
iconUrl:(nullable NSURL *)iconUrl
itemList:(NSArray<TUIMultimediaPasterItemConfig *> *)itemList
customizable:(BOOL)customizable;
- (UIImage *)loadIcon;
@end
@interface TUIMultimediaPasterItemConfig : NSObject <NSSecureCoding>
@property(nullable, nonatomic) NSURL *imageUrl;
@property(nullable, nonatomic) NSURL *iconUrl;
@property(nonatomic) BOOL isUserAdded;
@property(nonatomic) BOOL isAddButton;
- (UIImage *)loadImage;
- (UIImage *)loadIcon;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,201 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaPasterConfig.h"
#import "TUIMultimediaPlugin/NSArray+Functional.h"
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
#import "TUIMultimediaPlugin/TUIMultimediaPersistence.h"
static NSString *const FileCustomPasterConfig = @"liteav_multimedia_paster_config";
static NSString *const PathCustomPasters = @"liteav_multimedia_pasters";
@implementation TUIMultimediaPasterConfig
- (instancetype)init {
self = [super init];
_groups = @[];
return self;
}
+ (TUIMultimediaPasterItemConfig *)loadItemFromJsonDict:(NSDictionary *)groupData {
NSString *iconPath = groupData[@"item_icon_path"];
NSString *imagePath = groupData[@"item_image_path"];
NSNumber *isAddButton = groupData[@"item_is_file_selector"];
TUIMultimediaPasterItemConfig *item = [[TUIMultimediaPasterItemConfig alloc] init];
item.iconUrl = [TUIMultimediaCommon getURLByResourcePath:iconPath];
item.imageUrl = [TUIMultimediaCommon getURLByResourcePath:imagePath];
item.isAddButton = isAddButton != nil && [isAddButton boolValue];
item.isUserAdded = NO;
return item;
}
+ (TUIMultimediaPasterGroupConfig *)loadGroupFromJsonDict:(NSDictionary *)groupData {
NSString *name = groupData[@"type_name"];
NSString *iconPath = groupData[@"type_icon_path"];
NSURL *iconUrl = [TUIMultimediaCommon getURLByResourcePath:iconPath];
BOOL customizable = NO;
NSMutableArray<TUIMultimediaPasterItemConfig *> *pasterList = [NSMutableArray array];
NSArray *itemDataList = groupData[@"paster_item_list"];
for (NSDictionary *itemData in itemDataList) {
TUIMultimediaPasterItemConfig *item = [self loadItemFromJsonDict:itemData];
if (item.isAddButton) {
customizable = YES;
}
[pasterList addObject:item];
}
return [[TUIMultimediaPasterGroupConfig alloc] initWithName:name iconUrl:iconUrl itemList:pasterList customizable:customizable];
}
+ (void)loadCustomPasterTo:(TUIMultimediaPasterConfig *)config {
NSError *err;
NSString *path = [TUIMultimediaPersistence.basePath stringByAppendingPathComponent:FileCustomPasterConfig];
NSData *data = [TUIMultimediaPersistence loadDataFromFile:path error:&err];
NSMutableArray<NSMutableArray<TUIMultimediaPasterItemConfig *> *> *saveList =
[NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithArray:@[ NSArray.class, TUIMultimediaPasterItemConfig.class ]] fromData:data error:&err];
for (int i = 0; i < config.groups.count; i++) {
NSMutableArray<TUIMultimediaPasterItemConfig *> *itemList = [NSMutableArray arrayWithArray:config.groups[i].itemList];
[itemList addObjectsFromArray:saveList[i]];
config.groups[i].itemList = itemList;
}
}
+ (TUIMultimediaPasterConfig *)loadConfig {
NSData *jsonData = [NSData dataWithContentsOfFile:[TUIMultimediaCommon.bundle pathForResource:[[TUIMultimediaConfig sharedInstance] getPicturePasterConfigFilePath] ofType:@"json"]];
NSError *err = nil;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
if (err || ![dic isKindOfClass:[NSDictionary class]]) {
NSLog(@"[TUIMultimedia] Json parse failed: %@", err);
return nil;
}
NSArray *groupListData = dic[@"paster_type_list"];
NSMutableArray<TUIMultimediaPasterGroupConfig *> *groups = [NSMutableArray array];
for (NSDictionary *jsonGroup in groupListData) {
[groups addObject:[self loadGroupFromJsonDict:jsonGroup]];
}
TUIMultimediaPasterConfig *config = [[TUIMultimediaPasterConfig alloc] init];
config.groups = groups;
[self loadCustomPasterTo:config];
return config;
}
+ (void)saveConfig:(TUIMultimediaPasterConfig *)config {
NSMutableArray<NSMutableArray<TUIMultimediaPasterItemConfig *> *> *saveList = [NSMutableArray array];
for (TUIMultimediaPasterGroupConfig *g in config.groups) {
NSMutableArray<TUIMultimediaPasterItemConfig *> *list = [NSMutableArray array];
[saveList addObject:list];
if (g.customizable) {
for (TUIMultimediaPasterItemConfig *item in g.itemList) {
if (item.isUserAdded) {
[list addObject:item];
}
}
}
}
NSError *err;
NSString *path = [TUIMultimediaPersistence.basePath stringByAppendingPathComponent:FileCustomPasterConfig];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:saveList requiringSecureCoding:YES error:&err];
[TUIMultimediaPersistence saveData:data toFile:path error:nil];
}
+ (NSURL *)saveCustomPaster:(UIImage *)img {
NSString *pastersDir = [TUIMultimediaPersistence.basePath stringByAppendingPathComponent:PathCustomPasters];
NSString *file = [pastersDir stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
[TUIMultimediaPersistence saveData:UIImagePNGRepresentation(img) toFile:file error:nil];
return [NSURL fileURLWithPath:file];
}
+ (void)removeCustomPaster:(TUIMultimediaPasterItemConfig *)paster {
if (!paster.isUserAdded) {
return;
}
if (paster.iconUrl != nil && ![paster.iconUrl.absoluteString containsString:TUIMultimediaCommon.bundle.resourceURL.absoluteString]) {
[NSFileManager.defaultManager removeItemAtURL:paster.iconUrl error:nil];
}
if (paster.imageUrl != nil && ![paster.imageUrl.absoluteString containsString:TUIMultimediaCommon.bundle.resourceURL.absoluteString]) {
[NSFileManager.defaultManager removeItemAtURL:paster.imageUrl error:nil];
}
}
@end
@interface TUIMultimediaPasterGroupConfig () {
UIImage *_cachedIcon;
}
@end
@implementation TUIMultimediaPasterGroupConfig
- (instancetype)initWithName:(NSString *)name
iconUrl:(nullable NSURL *)iconUrl
itemList:(NSArray<TUIMultimediaPasterItemConfig *> *)itemList
customizable:(BOOL)customizable {
self = [super init];
_name = name;
_iconUrl = iconUrl;
_itemList = itemList;
_customizable = customizable;
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_name forKey:@"name"];
[coder encodeObject:_iconUrl forKey:@"iconUrl"];
[coder encodeObject:_itemList forKey:@"itemList"];
[coder encodeBool:_customizable forKey:@"customizable"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
_name = [coder decodeObjectOfClass:NSString.class forKey:@"name"];
_iconUrl = [coder decodeObjectOfClass:NSURL.class forKey:@"iconUrl"];
_itemList = [coder decodeObjectOfClasses:[NSSet setWithObjects:NSArray.class, TUIMultimediaPasterItemConfig.class, nil] forKey:@"itemList"];
_customizable = [coder decodeBoolForKey:@"customizable"];
return self;
}
+ (BOOL)supportsSecureCoding {
return YES;
}
- (UIImage *)loadIcon {
if (_cachedIcon != nil) {
return _cachedIcon;
}
_cachedIcon = [UIImage imageWithData:[NSData dataWithContentsOfURL:_iconUrl]];
return _cachedIcon;
}
@end
@interface TUIMultimediaPasterItemConfig () {
UIImage *_cachedImage;
UIImage *_cachedIcon;
}
@end
@implementation TUIMultimediaPasterItemConfig
- (UIImage *)loadImage {
if (_cachedImage != nil) {
return _cachedImage;
}
_cachedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:_imageUrl]];
return _cachedImage;
}
- (UIImage *)loadIcon {
if (_iconUrl == nil) {
return [self loadImage];
}
if (_cachedIcon != nil) {
return _cachedIcon;
}
_cachedIcon = [UIImage imageWithData:[NSData dataWithContentsOfURL:_iconUrl]];
return _cachedIcon;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_imageUrl forKey:@"imageUrl"];
[coder encodeObject:_iconUrl forKey:@"iconUrl"];
[coder encodeBool:_isUserAdded forKey:@"isUserAdded"];
[coder encodeBool:_isAddButton forKey:@"isAddButton"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
_imageUrl = [coder decodeObjectOfClass:NSURL.class forKey:@"imageUrl"];
_iconUrl = [coder decodeObjectOfClass:NSURL.class forKey:@"iconUrl"];
_isUserAdded = [coder decodeBoolForKey:@"isUserAdded"];
_isAddButton = [coder decodeBoolForKey:@"isAddButton"];
return self;
}
+ (BOOL)supportsSecureCoding {
return YES;
}
@end

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
贴纸或字幕
*/
@interface TUIMultimediaSticker : NSObject
@property(nullable, nonatomic) UIImage *image;
@property(nonatomic) CGRect frame;
- (instancetype)init;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,11 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaSticker.h"
@implementation TUIMultimediaSticker
- (instancetype)init {
self = [super init];
return self;
}
@end

View File

@@ -0,0 +1,18 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
字幕信息
*/
@interface TUIMultimediaSubtitleInfo : NSObject <NSCopying>
@property(nonatomic) NSString *text;
@property(nonatomic) UIColor *color;
@property(nonatomic) NSString *wrappedText;
- (instancetype)initWithText:(NSString *)text color:(UIColor *)color;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaSubtitleInfo.h"
#import "TUIMultimediaPlugin/NSArray+Functional.h"
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
@implementation TUIMultimediaSubtitleInfo
- (instancetype)init {
return [self initWithText:@"" color:UIColor.whiteColor];
}
- (instancetype)initWithText:(NSString *)text color:(UIColor *)color {
self = [super init];
if (self != nil) {
_text = text;
_color = color;
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone {
TUIMultimediaSubtitleInfo *cp = [[[self class] allocWithZone:zone] init];
cp.text = _text;
cp.color = _color;
return cp;
}
@end

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import <Foundation/Foundation.h>
#import "TUIMultimediaBGM.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIMultimediaVideoBgmEditInfo : NSObject
@property(nonatomic) BOOL originAudio;
@property(nullable, nonatomic) TUIMultimediaBGM *bgm;
- (instancetype)init;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,11 @@
// Copyright (c) 2024 Tencent. All rights reserved.
// Author: eddardliu
#import "TUIMultimediaVideoBgmEditInfo.h"
@implementation TUIMultimediaVideoBgmEditInfo
- (instancetype)init {
self = [super init];
return self;
}
@end