增加换肤功能
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <AVKit/AVKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPasterConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPopupController.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaVideoBgmEditInfo.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaBGMEditControllerDelegate;
|
||||
|
||||
/**
|
||||
背景音乐选择界面Controller
|
||||
*/
|
||||
@interface TUIMultimediaBGMEditController : TUIMultimediaPopupController
|
||||
@property(nonatomic) float clipDuration;
|
||||
@property(readonly, nonatomic) TUIMultimediaVideoBgmEditInfo *bgmEditInfo;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaBGMEditControllerDelegate> delegate;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaBGMEditControllerDelegate <NSObject>
|
||||
- (void)onBGMEditController:(TUIMultimediaBGMEditController *)c bgmInfoChanged:(TUIMultimediaVideoBgmEditInfo *)bgmInfo;
|
||||
- (void)onBGMEditControllerExit:(TUIMultimediaBGMEditController *)c;
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaBGMEditController.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <ReactiveObjC/RACEXTScope.h>
|
||||
#import "TUIMultimediaPlugin/NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBGMEditView.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaImagePicker.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPasterSelectView.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPersistence.h"
|
||||
|
||||
@interface TUIMultimediaBGMEditController () <TUIMultimediaBGMEditViewDelegate> {
|
||||
TUIMultimediaBGMEditView *_editView;
|
||||
NSArray<TUIMultimediaBGMGroup *> *_bgmConfig;
|
||||
dispatch_block_t _blockBGMNotify;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaBGMEditController
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_bgmConfig = [TUIMultimediaBGMGroup loadBGMConfigs];
|
||||
_bgmEditInfo = [[TUIMultimediaVideoBgmEditInfo alloc] init];
|
||||
_bgmEditInfo.originAudio = YES;
|
||||
_bgmEditInfo.bgm = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
_editView = [[TUIMultimediaBGMEditView alloc] init];
|
||||
_editView.bgmConfig = _bgmConfig;
|
||||
_editView.originAudioEnabled = YES;
|
||||
_editView.clipDuration = _clipDuration;
|
||||
_editView.delegate = self;
|
||||
self.mainView = _editView;
|
||||
}
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
- (void)popupControllerDidCanceled {
|
||||
[_delegate onBGMEditControllerExit:self];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (void)setClipDuration:(float)videoDuration {
|
||||
_clipDuration = videoDuration;
|
||||
_editView.clipDuration = videoDuration;
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaBGMEditViewDelegate protocol
|
||||
- (void)bgmEditViewValueChanged:(TUIMultimediaBGMEditView *)v {
|
||||
TUIMultimediaVideoBgmEditInfo *newBgmInfo = [[TUIMultimediaVideoBgmEditInfo alloc] init];
|
||||
if (v.bgmEnabled) {
|
||||
newBgmInfo.bgm = [v.selectedBgm copy];
|
||||
} else {
|
||||
newBgmInfo.bgm = nil;
|
||||
}
|
||||
newBgmInfo.originAudio = v.originAudioEnabled;
|
||||
|
||||
// 延迟防抖, 防止拖动时产生怪音
|
||||
if (_blockBGMNotify != nil) {
|
||||
dispatch_block_cancel(_blockBGMNotify);
|
||||
_blockBGMNotify = nil;
|
||||
}
|
||||
_blockBGMNotify = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{
|
||||
self->_bgmEditInfo = newBgmInfo;
|
||||
[self->_delegate onBGMEditController:self bgmInfoChanged:self->_bgmEditInfo];
|
||||
});
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.050 * NSEC_PER_SEC)), dispatch_get_main_queue(), _blockBGMNotify);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaEncodeConfig.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
视频编辑界面Controller
|
||||
*/
|
||||
@interface TUIMultimediaVideoEditorController : UIViewController
|
||||
@property(nullable, nonatomic) NSString *sourceVideoPath;
|
||||
@property(nullable, nonatomic) NSString *resultVideoPath;
|
||||
@property(nonatomic)int sourceType;
|
||||
@property(nonatomic, nullable) void (^completeCallback)(NSString *resultVideoPath, int resultCode);
|
||||
- (instancetype)init;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,299 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaVideoEditorController.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <ReactiveObjC/RACEXTScope.h>
|
||||
#import <TUICore/TUIDefine.h>
|
||||
#import <TXLiteAVSDK_Professional/TXVideoEditer.h>
|
||||
#import <TXLiteAVSDK_Professional/TXVideoEditerTypeDef.h>
|
||||
#import "TUIMultimediaPlugin/NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBGMEditController.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommonEditorControlView.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaImagePicker.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPasterConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPasterSelectController.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaPersistence.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaSubtitleEditController.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConstant.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaAuthorizationPrompter.h"
|
||||
|
||||
@interface TUIMultimediaVideoEditorController () <TXVideoPreviewListener,
|
||||
TXVideoGenerateListener,
|
||||
TUIMultimediaPasterSelectControllerDelegate,
|
||||
TUIMultimediaCommonEditorControlViewDelegate,
|
||||
TUIMultimediaBGMEditControllerDelegate> {
|
||||
TUIMultimediaPasterSelectController *_pasterSelectController;
|
||||
TUIMultimediaCommonEditorControlView *_commonEditCtrlView;
|
||||
TXVideoEditer *_editor;
|
||||
NSString *_sourceVideoPath;
|
||||
TXVideoInfo *_videoInfo;
|
||||
TUIMultimediaSubtitleEditController *_subtitleEditController;
|
||||
TUIMultimediaBGMEditController *_musicController;
|
||||
TUIMultimediaEncodeConfig *_encodeConfig;
|
||||
BOOL _originNavgationBarHidden;
|
||||
float _lastGenerateProgress;
|
||||
BOOL _hasAudioEdited;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaVideoEditorController
|
||||
|
||||
@dynamic sourceVideoPath;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
_encodeConfig = [[TUIMultimediaEncodeConfig alloc] initWithVideoQuality:[[TUIMultimediaConfig sharedInstance] getVideoQuality]];
|
||||
_lastGenerateProgress = 0;
|
||||
_sourceType = SOURCE_TYPE_RECORD;
|
||||
_hasAudioEdited = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[self initUI];
|
||||
_pasterSelectController = [[TUIMultimediaPasterSelectController alloc] init];
|
||||
_pasterSelectController.delegate = self;
|
||||
_pasterSelectController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
|
||||
_subtitleEditController = [[TUIMultimediaSubtitleEditController alloc] init];
|
||||
_subtitleEditController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
|
||||
_musicController = [[TUIMultimediaBGMEditController alloc] init];
|
||||
_musicController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
|
||||
_musicController.delegate = self;
|
||||
_musicController.clipDuration = _videoInfo.duration;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
if (self.navigationController != nil) {
|
||||
_originNavgationBarHidden = self.navigationController.navigationBarHidden;
|
||||
self.navigationController.navigationBarHidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
if (self.navigationController != nil) {
|
||||
self.navigationController.navigationBarHidden = _originNavgationBarHidden;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)sourceVideoPath {
|
||||
return _sourceVideoPath;
|
||||
}
|
||||
|
||||
- (void)setSourceVideoPath:(NSString *)sourceVideoPath {
|
||||
_sourceVideoPath = sourceVideoPath;
|
||||
NSString *currentSourceVideoPath = sourceVideoPath;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (self->_sourceVideoPath != currentSourceVideoPath) return;
|
||||
self->_videoInfo = [TXVideoInfoReader getVideoInfo:self->_sourceVideoPath];
|
||||
NSLog(@"videoInfo angle = %d, width = %d, height = %d, duration = %f",
|
||||
self->_videoInfo.angle, self->_videoInfo.width, self->_videoInfo.height, self->_videoInfo.duration);
|
||||
if (self->_videoInfo.angle == 90 || self->_videoInfo.angle == 270) {
|
||||
int temp = self->_videoInfo.width;
|
||||
self->_videoInfo.width = self->_videoInfo.height;
|
||||
self->_videoInfo.height = temp;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self tryReloadVideoAsset];
|
||||
self->_musicController.clipDuration = self->_videoInfo.duration;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setSourceType:(int)sourceType {
|
||||
NSLog(@"setSourceType sourceType = %d",sourceType);
|
||||
_sourceType = sourceType;
|
||||
if (_commonEditCtrlView != nil) {
|
||||
_commonEditCtrlView.sourceType = sourceType;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tryReloadVideoAsset {
|
||||
if (_editor == nil || _sourceVideoPath == nil) return;
|
||||
int code = [_editor setVideoPath:_sourceVideoPath];
|
||||
[_editor startPlayFromTime:0 toTime:_videoInfo.duration];
|
||||
if (code != 0) {
|
||||
NSString *title = [TUIMultimediaCommon localizedStringForKey:@"modify_load_assert_failed"];
|
||||
[self showAlertWithTitle:title message:@"" action:@"OK"];
|
||||
}
|
||||
_commonEditCtrlView.previewSize = CGSizeMake(_videoInfo.width, _videoInfo.height);
|
||||
}
|
||||
|
||||
#pragma mark - UI Init
|
||||
|
||||
- (void)initUI {
|
||||
_commonEditCtrlView = [[TUIMultimediaCommonEditorControlView alloc] initWithConfig:TUIMultimediaCommonEditorConfig.configForVideoEditor];
|
||||
[self.view addSubview:_commonEditCtrlView];
|
||||
_commonEditCtrlView.backgroundColor = UIColor.blackColor;
|
||||
_commonEditCtrlView.previewSize = CGSizeMake(_videoInfo.width, _videoInfo.height);
|
||||
_commonEditCtrlView.sourceType = _sourceType;
|
||||
_commonEditCtrlView.delegate = self;
|
||||
|
||||
TXPreviewParam *param = [[TXPreviewParam alloc] init];
|
||||
param.videoView = _commonEditCtrlView.previewView;
|
||||
param.renderMode = PREVIEW_RENDER_MODE_FILL_EDGE;
|
||||
_editor = [[TXVideoEditer alloc] initWithPreview:param];
|
||||
[self tryReloadVideoAsset];
|
||||
_editor.previewDelegate = self;
|
||||
_editor.generateDelegate = self;
|
||||
|
||||
[_commonEditCtrlView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
}
|
||||
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message action:(NSString *)action {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:action style:UIAlertActionStyleDefault handler:nil];
|
||||
[alert addAction:defaultAction];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - TXVideoPreviewListener protocol
|
||||
- (void)onPreviewProgress:(CGFloat)time {
|
||||
}
|
||||
- (void)onPreviewFinished {
|
||||
[_editor startPlayFromTime:0 toTime:_videoInfo.duration];
|
||||
}
|
||||
|
||||
#pragma mark - TXVideoGenerateListener protocol
|
||||
- (void)onGenerateProgress:(float)progress {
|
||||
NSLog(@"TUIMultimediaVideoEditorController onGenerateProgress progress = %f",progress);
|
||||
if (progress - _lastGenerateProgress > 0.01f || progress == 1.0f) {
|
||||
_commonEditCtrlView.progressBarProgress = progress;
|
||||
_lastGenerateProgress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onGenerateComplete:(TXGenerateResult *)result {
|
||||
NSLog(@"TUIMultimediaVideoEditorController onGenerateComplete retCode = %ld",(long)result.retCode);
|
||||
int resultCode = (result.retCode != 0) ? VIDEO_EDIT_RESULT_CODE_GENERATE_FAIL : VIDEO_EDIT_RESULT_CODE_GENERATE_SUCCESS;
|
||||
_completeCallback(_resultVideoPath, resultCode);
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaCommonEditorControlViewDelegate protocol
|
||||
- (void)onCommonEditorControlViewComplete:(TUIMultimediaCommonEditorControlView *)view stickers:(NSArray<TUIMultimediaSticker *> *)stickers {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:self]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stickers.count == 0 && !_hasAudioEdited && _sourceType == SOURCE_TYPE_ALBUM) {
|
||||
NSLog(@"Return directly without encoding the video");
|
||||
_resultVideoPath = _sourceVideoPath;
|
||||
_completeCallback(_resultVideoPath, VIDEO_EDIT_RESULT_CODE_NO_EDIT);
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<TXPaster *> *pasterList = [stickers tui_multimedia_map:^TXPaster *(TUIMultimediaSticker *s) {
|
||||
TXPaster *p = [[TXPaster alloc] init];
|
||||
p.pasterImage = s.image;
|
||||
p.frame = s.frame;
|
||||
p.startTime = 0.0;
|
||||
p.endTime = self->_videoInfo.duration;
|
||||
return p;
|
||||
}];
|
||||
[_editor setPasterList:pasterList];
|
||||
|
||||
_commonEditCtrlView.isGenerating = YES;
|
||||
[self.view bringSubviewToFront:_commonEditCtrlView];
|
||||
if (_resultVideoPath == nil || _resultVideoPath.length == 0) {
|
||||
_resultVideoPath = [self getOutFilePath];
|
||||
}
|
||||
[_editor setVideoBitrate:_encodeConfig.bitrate];
|
||||
[_editor generateVideo:[_encodeConfig getVideoEditCompressed] videoOutputPath:_resultVideoPath];
|
||||
}
|
||||
|
||||
- (void)onCommonEditorControlViewCancel:(TUIMultimediaCommonEditorControlView *)view {
|
||||
[_editor stopPlay];
|
||||
_completeCallback(nil, VIDEO_EDIT_RESULT_CODE_CANCEL);
|
||||
}
|
||||
|
||||
- (void)onCommonEditorControlViewNeedAddPaster:(TUIMultimediaCommonEditorControlView *)view {
|
||||
NSLog(@"onCommonEditorControlViewNeedAddPaster");
|
||||
_commonEditCtrlView.modifyButtonsHidden = YES;
|
||||
[self presentViewController:_pasterSelectController animated:NO completion:nil];
|
||||
}
|
||||
|
||||
- (void)onCommonEditorControlViewNeedModifySubtitle:(TUIMultimediaSubtitleInfo *)info callback:(void (^)(TUIMultimediaSubtitleInfo *info, BOOL isOk))callback {
|
||||
_subtitleEditController.subtitleInfo = info;
|
||||
_subtitleEditController.callback = ^(TUIMultimediaSubtitleEditController *c, BOOL isOk) {
|
||||
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
|
||||
if (callback != nil) {
|
||||
callback(c.subtitleInfo, isOk);
|
||||
}
|
||||
};
|
||||
[self presentViewController:_subtitleEditController animated:NO completion:nil];
|
||||
}
|
||||
- (void)onCommonEditorControlViewNeedEditMusic:(TUIMultimediaCommonEditorControlView *)view {
|
||||
_commonEditCtrlView.modifyButtonsHidden = YES;
|
||||
[self presentViewController:_musicController animated:NO completion:nil];
|
||||
}
|
||||
- (void)onCommonEditorControlViewCancelGenerate:(TUIMultimediaCommonEditorControlView *)view {
|
||||
[_editor cancelGenerate];
|
||||
TXPreviewParam *param = [[TXPreviewParam alloc] init];
|
||||
param.videoView = _commonEditCtrlView.previewView;
|
||||
param.renderMode = PREVIEW_RENDER_MODE_FILL_EDGE;
|
||||
_editor = [[TXVideoEditer alloc] initWithPreview:param];
|
||||
[self tryReloadVideoAsset];
|
||||
_editor.previewDelegate = self;
|
||||
_editor.generateDelegate = self;
|
||||
}
|
||||
|
||||
-(NSString*) getOutFilePath{
|
||||
NSDate* currentDate = [NSDate date];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
|
||||
NSString* currentDateString = [dateFormatter stringFromDate:currentDate];
|
||||
NSString* outFileName = [NSString stringWithFormat:@"%@-%u-temp.mp4", currentDateString, arc4random()];
|
||||
return [NSTemporaryDirectory() stringByAppendingPathComponent:outFileName];
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaPasterSelectControllerDelegate protocol
|
||||
- (void)pasterSelectController:(TUIMultimediaPasterSelectController *)c onPasterSelected:(UIImage *)image {
|
||||
[_commonEditCtrlView addPaster:image];
|
||||
_commonEditCtrlView.modifyButtonsHidden = NO;
|
||||
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
- (void)onPasterSelectControllerExit:(TUIMultimediaPasterSelectController *)c {
|
||||
_commonEditCtrlView.modifyButtonsHidden = NO;
|
||||
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - TUIMultimediaMusicControllerDelegate protocol
|
||||
- (void)onBGMEditController:(TUIMultimediaBGMEditController *)bgmController bgmInfoChanged:(TUIMultimediaVideoBgmEditInfo *)bgmInfo {
|
||||
if (![TUIMultimediaAuthorizationPrompter verifyPermissionGranted:bgmController]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_editor setBGMLoop:YES];
|
||||
@weakify(self);
|
||||
[_editor setBGMAsset:bgmInfo.bgm.asset
|
||||
result:^(int result) {
|
||||
@strongify(self);
|
||||
if (result != 0) {
|
||||
NSString *title = [TUIMultimediaCommon localizedStringForKey:@"modify_load_assert_failed"];
|
||||
[self showAlertWithTitle:title message:@"" action:@"OK"];
|
||||
}
|
||||
}];
|
||||
[_editor setBGMAtVideoTime:0];
|
||||
[_editor setBGMStartTime:bgmInfo.bgm.startTime endTime:bgmInfo.bgm.endTime];
|
||||
[_editor setBGMVolume:1];
|
||||
if (bgmInfo.originAudio) {
|
||||
[_editor setVideoVolume:1];
|
||||
} else {
|
||||
[_editor setVideoVolume:0];
|
||||
}
|
||||
_commonEditCtrlView.musicEdited = bgmInfo.bgm != nil;
|
||||
|
||||
_hasAudioEdited = (!bgmInfo.originAudio) || (bgmInfo.bgm != nil);
|
||||
}
|
||||
- (void)onBGMEditControllerExit:(nonnull TUIMultimediaBGMEditController *)c {
|
||||
_commonEditCtrlView.modifyButtonsHidden = NO;
|
||||
[c.presentingViewController dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBGM.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, TUIMultimediaMusicCellState) {
|
||||
TUIMultimediaMusicCellStateNormal,
|
||||
TUIMultimediaMusicCellStateSelected,
|
||||
TUIMultimediaMusicCellStateEnabled,
|
||||
};
|
||||
|
||||
@protocol TUIMultimediaMusicCellDelegate;
|
||||
|
||||
@interface TUIMultimediaMusicCell : UITableViewCell
|
||||
@property(nonatomic) TUIMultimediaMusicCellState state;
|
||||
@property(nullable, nonatomic) TUIMultimediaBGM *music;
|
||||
@property(nonatomic) float selectDuration;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaMusicCellDelegate> delegate;
|
||||
|
||||
+ (NSString *)reuseIdentifier;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaMusicCellDelegate <NSObject>
|
||||
- (void)musicCell:(TUIMultimediaMusicCell *)cell onEditStateChanged:(BOOL)editState;
|
||||
- (void)musicCell:(TUIMultimediaMusicCell *)cell onBGMRangeChanged:(TUIMultimediaBGM *)bgm;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaMusicCell.h"
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "Masonry/Masonry.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaAutoScrollLabel.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaConfig.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaFakeAudioWaveView.h"
|
||||
|
||||
static const CGFloat TitleFontSize = 14;
|
||||
static const CGFloat SubtitleFontSize = 12;
|
||||
|
||||
@interface TUIMultimediaMusicCell () {
|
||||
TUIMultimediaAutoScrollLabel *_lbTitle;
|
||||
UILabel *_lbSubTitle;
|
||||
UILabel *_lbDuration;
|
||||
TUIMultimediaFakeAudioWaveView *_waveView;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaMusicCell
|
||||
|
||||
+ (NSString *)reuseIdentifier {
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self != nil) {
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
self.contentView.backgroundColor = UIColor.clearColor;
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
self.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
_lbTitle = [[TUIMultimediaAutoScrollLabel alloc] init];
|
||||
[self.contentView addSubview:_lbTitle];
|
||||
|
||||
_lbSubTitle = [[UILabel alloc] init];
|
||||
[self.contentView addSubview:_lbSubTitle];
|
||||
_lbSubTitle.font = [UIFont systemFontOfSize:SubtitleFontSize];
|
||||
_lbSubTitle.textColor = TUIMultimediaPluginDynamicColor(@"editor_bgm_text_color", @"#FFFFFF99");
|
||||
|
||||
_lbDuration = [[UILabel alloc] init];
|
||||
[self.contentView addSubview:_lbDuration];
|
||||
_lbDuration.font = [UIFont monospacedSystemFontOfSize:SubtitleFontSize weight:UIFontWeightMedium];
|
||||
_lbDuration.textColor = TUIMultimediaPluginDynamicColor(@"editor_bgm_text_color", @"#FFFFFF99");
|
||||
|
||||
_waveView = [[TUIMultimediaFakeAudioWaveView alloc] init];
|
||||
[self addSubview:_waveView];
|
||||
_waveView.hidden = YES;
|
||||
|
||||
[_lbTitle mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.top.equalTo(self.contentView).inset(10);
|
||||
make.width.mas_equalTo(120);
|
||||
}];
|
||||
[_lbSubTitle mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(_lbTitle.mas_bottom).inset(5);
|
||||
make.left.equalTo(self.contentView).inset(14);
|
||||
make.bottom.equalTo(self.contentView).inset(10);
|
||||
}];
|
||||
[_lbDuration mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerY.equalTo(self.contentView);
|
||||
make.right.equalTo(self.contentView).inset(10);
|
||||
}];
|
||||
[_waveView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(_lbDuration.mas_left).inset(50);
|
||||
make.centerY.equalTo(self);
|
||||
make.height.mas_equalTo(12);
|
||||
make.width.mas_equalTo(48);
|
||||
}];
|
||||
}
|
||||
- (void)updateTitle {
|
||||
UIColor *color = UIColor.whiteColor;
|
||||
BOOL active = NO;
|
||||
if (_state == TUIMultimediaMusicCellStateEnabled) {
|
||||
color = [[TUIMultimediaConfig sharedInstance] getThemeColor];
|
||||
if (_music.lyric != nil && _music.lyric.length > 0) {
|
||||
active = YES;
|
||||
}
|
||||
}
|
||||
NSString *title = _music.lyric != nil && _music.lyric.length > 0 ? _music.lyric : _music.name;
|
||||
_lbTitle.text = [[NSAttributedString alloc] initWithString:title
|
||||
attributes:@{
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:TitleFontSize],
|
||||
NSForegroundColorAttributeName : color,
|
||||
}];
|
||||
_lbTitle.active = active;
|
||||
}
|
||||
- (void)updateWave {
|
||||
switch (_state) {
|
||||
case TUIMultimediaMusicCellStateNormal:
|
||||
_waveView.hidden = YES;
|
||||
_waveView.enabled = NO;
|
||||
break;
|
||||
case TUIMultimediaMusicCellStateSelected:
|
||||
_waveView.color = TUIMultimediaPluginDynamicColor(@"editor_bgm_text_color", @"#FFFFFF99");
|
||||
_waveView.hidden = NO;
|
||||
_waveView.enabled = NO;
|
||||
break;
|
||||
case TUIMultimediaMusicCellStateEnabled:
|
||||
_waveView.color = [[TUIMultimediaConfig sharedInstance] getThemeColor];
|
||||
_waveView.hidden = NO;
|
||||
_waveView.enabled = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#pragma mark - Properties
|
||||
- (void)setState:(TUIMultimediaMusicCellState)state {
|
||||
_state = state;
|
||||
[self updateTitle];
|
||||
[self updateWave];
|
||||
}
|
||||
- (void)setMusic:(TUIMultimediaBGM *)music {
|
||||
_music = music;
|
||||
[self updateTitle];
|
||||
_lbSubTitle.text = music.source;
|
||||
float duration = music.asset == nil ? 0 : music.asset.duration.value / music.asset.duration.timescale;
|
||||
int min = (int)(duration / 60);
|
||||
_lbDuration.text = [NSString stringWithFormat:@"%02d:%02d", min, (int)(duration - min * 60)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaBGM.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol TUIMultimediaBGMEditViewDelegate;
|
||||
|
||||
@interface TUIMultimediaBGMEditView : UIView
|
||||
@property(nonatomic) NSArray<TUIMultimediaBGMGroup *> *bgmConfig;
|
||||
@property(nonatomic) float clipDuration;
|
||||
@property(nonatomic) TUIMultimediaBGM *selectedBgm;
|
||||
@property(nonatomic) BOOL originAudioEnabled;
|
||||
@property(nonatomic) BOOL bgmEnabled;
|
||||
@property(weak, nullable, nonatomic) id<TUIMultimediaBGMEditViewDelegate> delegate;
|
||||
@end
|
||||
|
||||
@protocol TUIMultimediaBGMEditViewDelegate <NSObject>
|
||||
- (void)bgmEditViewValueChanged:(TUIMultimediaBGMEditView *)v;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2024 Tencent. All rights reserved.
|
||||
// Author: eddardliu
|
||||
|
||||
#import "TUIMultimediaBGMEditView.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIMultimediaPlugin/NSArray+Functional.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCheckBox.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaCommon.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaMusicCell.h"
|
||||
#import "TUIMultimediaPlugin/TUIMultimediaTabPanel.h"
|
||||
|
||||
static const CGFloat ItemInset = 10;
|
||||
static const CGFloat ItemWidthFactor = 0.7;
|
||||
|
||||
static const CGFloat CollectionViewHeight = 100;
|
||||
|
||||
@interface TUIMultimediaBGMEditView () <UITableViewDataSource, UITableViewDelegate> {
|
||||
NSArray<UITableView *> *_tableViews;
|
||||
TUIMultimediaTabPanel *_tabPanel;
|
||||
NSInteger _selectedIndex;
|
||||
TUIMultimediaCheckBox *_switchOriginAudio;
|
||||
TUIMultimediaCheckBox *_switchBgm;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIMultimediaBGMEditView
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil) {
|
||||
_bgmConfig = @[];
|
||||
_selectedIndex = -1;
|
||||
[self initUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initUI {
|
||||
self.backgroundColor = TUIMultimediaPluginDynamicColor(@"editor_popup_view_bg_color", @"#000000BF");
|
||||
|
||||
_tabPanel = [[TUIMultimediaTabPanel alloc] initWithFrame:self.bounds];
|
||||
[self addSubview:_tabPanel];
|
||||
|
||||
_switchOriginAudio = [[TUIMultimediaCheckBox alloc] init];
|
||||
[self addSubview:_switchOriginAudio];
|
||||
_switchOriginAudio.text = [TUIMultimediaCommon localizedStringForKey:@"editor_origin_audio"];
|
||||
[_switchOriginAudio addTarget:self action:@selector(onSwitchOriginAudioChanged) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
_switchBgm = [[TUIMultimediaCheckBox alloc] init];
|
||||
[self addSubview:_switchBgm];
|
||||
_switchBgm.text = [TUIMultimediaCommon localizedStringForKey:@"editor_bgm"];
|
||||
[_switchBgm addTarget:self action:@selector(onSwitchBGMChanged) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[_tabPanel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.top.equalTo(self);
|
||||
make.bottom.equalTo(_switchBgm.mas_top).inset(10);
|
||||
make.height.mas_equalTo(400);
|
||||
}];
|
||||
[_switchBgm mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.bottom.equalTo(self.mas_safeAreaLayoutGuideBottom);
|
||||
make.height.mas_equalTo(24);
|
||||
make.left.equalTo(self).inset(20);
|
||||
}];
|
||||
[_switchOriginAudio mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.mas_equalTo(24);
|
||||
make.bottom.equalTo(self.mas_safeAreaLayoutGuideBottom);
|
||||
make.right.equalTo(self).inset(20);
|
||||
}];
|
||||
|
||||
[self reloadConfig];
|
||||
}
|
||||
|
||||
- (void)reloadConfig {
|
||||
_tableViews = [_bgmConfig tui_multimedia_mapWithIndex:^UITableView *(TUIMultimediaBGMGroup *group, NSUInteger idx) {
|
||||
UITableView *v = [[UITableView alloc] init];
|
||||
v.tag = idx;
|
||||
v.backgroundColor = UIColor.clearColor;
|
||||
v.dataSource = self;
|
||||
v.delegate = self;
|
||||
[v registerClass:TUIMultimediaMusicCell.class forCellReuseIdentifier:TUIMultimediaMusicCell.reuseIdentifier];
|
||||
return v;
|
||||
}];
|
||||
_tabPanel.tabs = [_tableViews tui_multimedia_map:^TUIMultimediaTabPanelTab *(UITableView *v) {
|
||||
return [[TUIMultimediaTabPanelTab alloc] initWithName:[TUIMultimediaCommon localizedStringForKey:self->_bgmConfig[v.tag].name] icon:nil view:v];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (NSArray<TUIMultimediaBGM *> *)getBgmListByTableView:(UITableView *)v {
|
||||
return _bgmConfig[v.tag].bgmList;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [self getBgmListByTableView:tableView].count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIMultimediaMusicCell *cell = [tableView dequeueReusableCellWithIdentifier:TUIMultimediaMusicCell.reuseIdentifier forIndexPath:indexPath];
|
||||
cell.music = [self getBgmListByTableView:tableView][indexPath.item];
|
||||
if (cell.music == _selectedBgm) {
|
||||
if (_switchBgm.on) {
|
||||
cell.state = TUIMultimediaMusicCellStateEnabled;
|
||||
} else {
|
||||
cell.state = TUIMultimediaMusicCellStateSelected;
|
||||
}
|
||||
} else {
|
||||
cell.state = TUIMultimediaMusicCellStateNormal;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
#pragma mark - UITableViewDelegate
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
_selectedBgm = [self getBgmListByTableView:tableView][indexPath.item];
|
||||
if (!_switchBgm.on) {
|
||||
_switchBgm.on = YES;
|
||||
}
|
||||
[_delegate bgmEditViewValueChanged:self];
|
||||
for (UITableView *v in _tableViews) {
|
||||
[v reloadData];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)onSwitchOriginAudioChanged {
|
||||
[_delegate bgmEditViewValueChanged:self];
|
||||
}
|
||||
|
||||
- (void)onSwitchBGMChanged {
|
||||
[_delegate bgmEditViewValueChanged:self];
|
||||
for (UITableView *v in _tableViews) {
|
||||
[v reloadData];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
- (void)setBgmConfig:(NSArray<TUIMultimediaBGMGroup *> *)bgmConfig {
|
||||
_bgmConfig = bgmConfig;
|
||||
[self reloadConfig];
|
||||
}
|
||||
- (BOOL)originAudioEnabled {
|
||||
return _switchOriginAudio.on;
|
||||
}
|
||||
- (void)setOriginAudioEnabled:(BOOL)originAudioEnabled {
|
||||
_switchOriginAudio.on = originAudioEnabled;
|
||||
}
|
||||
|
||||
- (BOOL)bgmEnabled {
|
||||
return _switchBgm.on;
|
||||
}
|
||||
- (void)setBgmEnabled:(BOOL)bgmEnabled {
|
||||
_switchBgm.on = bgmEnabled;
|
||||
}
|
||||
|
||||
- (void)setClipDuration:(float)videoDuration {
|
||||
_clipDuration = videoDuration;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user