react-native-video-trim 7.1.0 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -5
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.kt +1 -1
- package/android/src/main/java/com/videotrim/widgets/AudioWaveformView.kt +92 -0
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.kt +502 -8
- package/ios/AudioWaveformView.swift +75 -0
- package/ios/VideoTrim.mm +25 -0
- package/ios/VideoTrimmer.swift +300 -0
- package/ios/VideoTrimmerViewController.swift +33 -1
- package/lib/module/NativeVideoTrim.js.map +1 -1
- package/lib/module/index.js +13 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +10 -0
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeVideoTrim.ts +10 -0
- package/src/index.tsx +28 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Custom UIView that draws an audio waveform as a row of vertical rounded-rect bars.
|
|
4
|
+
///
|
|
5
|
+
/// Each bar's height is driven by a normalized amplitude value in [0, 1].
|
|
6
|
+
/// The view recalculates bar count from its own width and maps the amplitudes
|
|
7
|
+
/// array proportionally, so it works correctly regardless of whether the
|
|
8
|
+
/// amplitudes array has more or fewer entries than the visible bar count.
|
|
9
|
+
///
|
|
10
|
+
/// The `backgroundColor` provides the waveform track color; bars are drawn
|
|
11
|
+
/// on top with `barColor`.
|
|
12
|
+
class AudioWaveformView: UIView {
|
|
13
|
+
var amplitudes: [CGFloat] = [] {
|
|
14
|
+
didSet { setNeedsDisplay() }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var barColor: UIColor = .white {
|
|
18
|
+
didSet { setNeedsDisplay() }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var barWidth: CGFloat = 3 {
|
|
22
|
+
didSet { setNeedsDisplay() }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var barGap: CGFloat = 2 {
|
|
26
|
+
didSet { setNeedsDisplay() }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var barCornerRadius: CGFloat = 1.5 {
|
|
30
|
+
didSet { setNeedsDisplay() }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override init(frame: CGRect) {
|
|
34
|
+
super.init(frame: frame)
|
|
35
|
+
isOpaque = false
|
|
36
|
+
contentMode = .redraw
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
required init?(coder: NSCoder) {
|
|
40
|
+
super.init(coder: coder)
|
|
41
|
+
isOpaque = false
|
|
42
|
+
contentMode = .redraw
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override func draw(_ rect: CGRect) {
|
|
46
|
+
guard !amplitudes.isEmpty else { return }
|
|
47
|
+
guard let ctx = UIGraphicsGetCurrentContext() else { return }
|
|
48
|
+
|
|
49
|
+
let totalHeight = rect.height
|
|
50
|
+
let step = barWidth + barGap
|
|
51
|
+
let barCount = Int(floor(rect.width / step))
|
|
52
|
+
guard barCount > 0 else { return }
|
|
53
|
+
|
|
54
|
+
// Keep bars from touching the container edges
|
|
55
|
+
let verticalPadding = barWidth * 1.5
|
|
56
|
+
let drawableHeight = totalHeight - verticalPadding * 2
|
|
57
|
+
guard drawableHeight > 0 else { return }
|
|
58
|
+
let minBarHeight = barWidth
|
|
59
|
+
|
|
60
|
+
ctx.setFillColor(barColor.cgColor)
|
|
61
|
+
|
|
62
|
+
for i in 0..<barCount {
|
|
63
|
+
let ampIndex = i * amplitudes.count / barCount
|
|
64
|
+
let amp = amplitudes[min(ampIndex, amplitudes.count - 1)]
|
|
65
|
+
let barHeight = max(minBarHeight, amp * drawableHeight)
|
|
66
|
+
let x = CGFloat(i) * step
|
|
67
|
+
let y = verticalPadding + (drawableHeight - barHeight) / 2.0
|
|
68
|
+
let barRect = CGRect(x: x, y: y, width: barWidth, height: barHeight)
|
|
69
|
+
let path = UIBezierPath(roundedRect: barRect, cornerRadius: barCornerRadius)
|
|
70
|
+
ctx.addPath(path.cgPath)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
ctx.fillPath()
|
|
74
|
+
}
|
|
75
|
+
}
|
package/ios/VideoTrim.mm
CHANGED
|
@@ -155,6 +155,31 @@ RCT_EXPORT_MODULE()
|
|
|
155
155
|
dict[@"handleIconColor"] = @(handleIconColorOpt.value());
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
auto waveformColorOpt = config.waveformColor();
|
|
159
|
+
if (waveformColorOpt.has_value()) {
|
|
160
|
+
dict[@"waveformColor"] = @(waveformColorOpt.value());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
auto waveformBackgroundColorOpt = config.waveformBackgroundColor();
|
|
164
|
+
if (waveformBackgroundColorOpt.has_value()) {
|
|
165
|
+
dict[@"waveformBackgroundColor"] = @(waveformBackgroundColorOpt.value());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
auto waveformBarWidthOpt = config.waveformBarWidth();
|
|
169
|
+
if (waveformBarWidthOpt.has_value()) {
|
|
170
|
+
dict[@"waveformBarWidth"] = @(waveformBarWidthOpt.value());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
auto waveformBarGapOpt = config.waveformBarGap();
|
|
174
|
+
if (waveformBarGapOpt.has_value()) {
|
|
175
|
+
dict[@"waveformBarGap"] = @(waveformBarGapOpt.value());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
auto waveformBarCornerRadiusOpt = config.waveformBarCornerRadius();
|
|
179
|
+
if (waveformBarCornerRadiusOpt.has_value()) {
|
|
180
|
+
dict[@"waveformBarCornerRadius"] = @(waveformBarCornerRadiusOpt.value());
|
|
181
|
+
}
|
|
182
|
+
|
|
158
183
|
auto zoomOnWaitingDurationOpt = config.zoomOnWaitingDuration();
|
|
159
184
|
if (zoomOnWaitingDurationOpt.has_value()) {
|
|
160
185
|
dict[@"zoomOnWaitingDuration"] = @(zoomOnWaitingDurationOpt.value());
|
package/ios/VideoTrimmer.swift
CHANGED
|
@@ -135,12 +135,25 @@ import AVFoundation
|
|
|
135
135
|
|
|
136
136
|
var asset: AVAsset? {
|
|
137
137
|
didSet {
|
|
138
|
+
// Clean up *all* async resources unconditionally — even when
|
|
139
|
+
// asset is set to nil (e.g. editor dismissal triggers this via
|
|
140
|
+
// VideoTrimmerViewController.viewWillDisappear).
|
|
141
|
+
generator?.cancelAllCGImageGeneration()
|
|
142
|
+
currentAssetReader?.cancelReading()
|
|
143
|
+
currentAssetReader = nil
|
|
144
|
+
audioDownloadTask?.cancel()
|
|
145
|
+
audioDownloadTask = nil
|
|
146
|
+
cleanupLocalAudioFile()
|
|
147
|
+
localAudioAsset = nil
|
|
148
|
+
|
|
138
149
|
if let asset = asset {
|
|
139
150
|
applyThemeColors()
|
|
140
151
|
let duration = asset.duration
|
|
141
152
|
range = CMTimeRange(start: .zero, duration: duration)
|
|
142
153
|
selectedRange = range
|
|
143
154
|
lastKnownViewSizeForThumbnailGeneration = .zero
|
|
155
|
+
lastKnownWaveformSize = .zero
|
|
156
|
+
lastKnownWaveformRange = .zero
|
|
144
157
|
setNeedsLayout()
|
|
145
158
|
}
|
|
146
159
|
}
|
|
@@ -160,6 +173,34 @@ import AVFoundation
|
|
|
160
173
|
var enableHapticFeedback = true
|
|
161
174
|
var zoomOnWaitingDuration: Double = 5.0 // Default: 5 seconds
|
|
162
175
|
var isLightTheme = false
|
|
176
|
+
/// Explicitly set from JS config (`type != "video"`).
|
|
177
|
+
/// Using a dedicated flag instead of inspecting AVAsset tracks avoids
|
|
178
|
+
/// false negatives — some audio files (e.g. M4A with album art) report
|
|
179
|
+
/// a video track, which caused the original `.video` track guard to
|
|
180
|
+
/// skip waveform generation entirely.
|
|
181
|
+
var isAudioOnly = false {
|
|
182
|
+
didSet {
|
|
183
|
+
lastKnownWaveformSize = .zero
|
|
184
|
+
setNeedsLayout()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MARK: - Waveform customisation (forwarded to AudioWaveformView)
|
|
189
|
+
var waveformBarColor: UIColor = .white {
|
|
190
|
+
didSet { waveformView.barColor = waveformBarColor }
|
|
191
|
+
}
|
|
192
|
+
var waveformBgColor: UIColor = UIColor(red: 0.204, green: 0.471, blue: 0.965, alpha: 1) {
|
|
193
|
+
didSet { waveformView.backgroundColor = waveformBgColor }
|
|
194
|
+
}
|
|
195
|
+
var waveformBarWidth: CGFloat = 3 {
|
|
196
|
+
didSet { waveformView.barWidth = waveformBarWidth }
|
|
197
|
+
}
|
|
198
|
+
var waveformBarGap: CGFloat = 2 {
|
|
199
|
+
didSet { waveformView.barGap = waveformBarGap }
|
|
200
|
+
}
|
|
201
|
+
var waveformBarCornerRadius: CGFloat = 1.5 {
|
|
202
|
+
didSet { waveformView.barCornerRadius = waveformBarCornerRadius }
|
|
203
|
+
}
|
|
163
204
|
|
|
164
205
|
// the available range of the asset.
|
|
165
206
|
// Will be set to the full duration of the asset when assigning a new asset
|
|
@@ -270,10 +311,35 @@ import AVFoundation
|
|
|
270
311
|
private var thumbnails = Array<Thumbnail>()
|
|
271
312
|
private var generator: AVAssetImageGenerator?
|
|
272
313
|
|
|
314
|
+
// MARK: - Audio waveform state
|
|
315
|
+
//
|
|
316
|
+
// AVAssetReader cannot read from remote URLs, so for remote audio we
|
|
317
|
+
// download to a temporary local file first (via URLSession.downloadTask).
|
|
318
|
+
// The local AVURLAsset is reused for zoom re-extractions, and the temp
|
|
319
|
+
// file is deleted in cleanupLocalAudioFile() on dismiss.
|
|
320
|
+
private let waveformView = AudioWaveformView()
|
|
321
|
+
private var lastKnownWaveformSize: CGSize = .zero
|
|
322
|
+
private var lastKnownWaveformRange: CMTimeRange = .zero
|
|
323
|
+
private var currentAssetReader: AVAssetReader?
|
|
324
|
+
/** AVURLAsset pointing to the downloaded local file (nil for local sources). */
|
|
325
|
+
private var localAudioAsset: AVURLAsset?
|
|
326
|
+
/** File URL of the temporary download, for deletion on cleanup. */
|
|
327
|
+
private var localAudioFileURL: URL?
|
|
328
|
+
private var audioDownloadTask: URLSessionDownloadTask?
|
|
329
|
+
|
|
273
330
|
private var impactFeedbackGenerator: UIImpactFeedbackGenerator?
|
|
274
331
|
private var didClampWhilePanning = false
|
|
275
332
|
|
|
276
333
|
|
|
334
|
+
/// Cancel all in-flight async work and delete temporary files.
|
|
335
|
+
/// This fires both on normal dismiss and on immediate close.
|
|
336
|
+
deinit {
|
|
337
|
+
generator?.cancelAllCGImageGeneration()
|
|
338
|
+
audioDownloadTask?.cancel()
|
|
339
|
+
currentAssetReader?.cancelReading()
|
|
340
|
+
cleanupLocalAudioFile()
|
|
341
|
+
}
|
|
342
|
+
|
|
277
343
|
// MARK: - Private
|
|
278
344
|
private func applyThemeColors() {
|
|
279
345
|
let bg: UIColor = isLightTheme ? .white : .black
|
|
@@ -291,6 +357,17 @@ import AVFoundation
|
|
|
291
357
|
thumbnailWrapperView.addSubview(leadingThumbRest)
|
|
292
358
|
thumbnailWrapperView.addSubview(trailingThumbRest)
|
|
293
359
|
thumbnailWrapperView.addSubview(thumbnailTrackView)
|
|
360
|
+
|
|
361
|
+
// Waveform view sits inside the thumbnail track but starts hidden;
|
|
362
|
+
// it's shown only for audio files once data is available.
|
|
363
|
+
waveformView.backgroundColor = waveformBgColor
|
|
364
|
+
waveformView.barColor = waveformBarColor
|
|
365
|
+
waveformView.barWidth = waveformBarWidth
|
|
366
|
+
waveformView.barGap = waveformBarGap
|
|
367
|
+
waveformView.barCornerRadius = waveformBarCornerRadius
|
|
368
|
+
waveformView.isHidden = true
|
|
369
|
+
thumbnailTrackView.addSubview(waveformView)
|
|
370
|
+
|
|
294
371
|
thumbnailWrapperView.addSubview(thumbnailLeadingCoverView)
|
|
295
372
|
thumbnailWrapperView.addSubview(thumbnailTrailingCoverView)
|
|
296
373
|
|
|
@@ -419,6 +496,7 @@ import AVFoundation
|
|
|
419
496
|
let transform = track.preferredTransform
|
|
420
497
|
let fixedSize = naturalSize.applyingVideoTransform(transform)
|
|
421
498
|
|
|
499
|
+
self.generator?.cancelAllCGImageGeneration()
|
|
422
500
|
let generator = AVAssetImageGenerator(asset: asset)
|
|
423
501
|
generator.apertureMode = .cleanAperture
|
|
424
502
|
generator.videoComposition = videoComposition
|
|
@@ -472,6 +550,223 @@ import AVFoundation
|
|
|
472
550
|
}
|
|
473
551
|
}
|
|
474
552
|
|
|
553
|
+
/// Called from layoutSubviews whenever the view size or visible time range changes.
|
|
554
|
+
///
|
|
555
|
+
/// For remote URLs, the first call triggers a download; once the local
|
|
556
|
+
/// file is cached, subsequent calls (e.g. zoom) skip straight to reading.
|
|
557
|
+
private func regenerateWaveformIfNeeded() {
|
|
558
|
+
guard isAudioOnly else { return }
|
|
559
|
+
let size = bounds.size
|
|
560
|
+
guard size.width > 0 && size.height > 0 else { return }
|
|
561
|
+
guard lastKnownWaveformSize != size || !CMTimeRangeEqual(lastKnownWaveformRange, visibleRange) else { return }
|
|
562
|
+
guard let asset = asset else { return }
|
|
563
|
+
|
|
564
|
+
// Remote URL path: download once, then reuse localAudioAsset for reads
|
|
565
|
+
if let urlAsset = asset as? AVURLAsset, !urlAsset.url.isFileURL {
|
|
566
|
+
if let localAsset = localAudioAsset {
|
|
567
|
+
guard let audioTrack = localAsset.tracks(withMediaType: .audio).first else { return }
|
|
568
|
+
readWaveformSamples(from: localAsset, audioTrack: audioTrack, size: size)
|
|
569
|
+
} else if audioDownloadTask == nil {
|
|
570
|
+
downloadAudioForWaveform(from: urlAsset.url)
|
|
571
|
+
}
|
|
572
|
+
return
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Local file path: read directly
|
|
576
|
+
guard let audioTrack = asset.tracks(withMediaType: .audio).first else { return }
|
|
577
|
+
readWaveformSamples(from: asset, audioTrack: audioTrack, size: size)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/// Download remote audio to a temporary local file so AVAssetReader can read it.
|
|
581
|
+
///
|
|
582
|
+
/// The file extension is inferred from the HTTP response (Content-Disposition,
|
|
583
|
+
/// MIME type) or the original URL, because AVURLAsset on iOS relies on
|
|
584
|
+
/// the extension to identify the audio codec — a generic `.tmp` extension
|
|
585
|
+
/// would cause silent failures.
|
|
586
|
+
private func downloadAudioForWaveform(from url: URL) {
|
|
587
|
+
let task = URLSession.shared.downloadTask(with: url) { [weak self] tempURL, response, error in
|
|
588
|
+
guard let self = self, let tempURL = tempURL else {
|
|
589
|
+
print("AudioWaveform: Download failed: \(error?.localizedDescription ?? "unknown")")
|
|
590
|
+
DispatchQueue.main.async { self?.audioDownloadTask = nil }
|
|
591
|
+
return
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
let ext = Self.audioFileExtension(from: response, originalURL: url)
|
|
595
|
+
let destURL = FileManager.default.temporaryDirectory
|
|
596
|
+
.appendingPathComponent("waveform_\(UUID().uuidString).\(ext)")
|
|
597
|
+
do {
|
|
598
|
+
try FileManager.default.moveItem(at: tempURL, to: destURL)
|
|
599
|
+
let localAsset = AVURLAsset(url: destURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
|
|
600
|
+
localAsset.loadValuesAsynchronously(forKeys: ["tracks"]) {
|
|
601
|
+
var trackError: NSError?
|
|
602
|
+
let status = localAsset.statusOfValue(forKey: "tracks", error: &trackError)
|
|
603
|
+
guard status == .loaded else {
|
|
604
|
+
print("AudioWaveform: Failed to load tracks from downloaded file: \(trackError?.localizedDescription ?? "unknown")")
|
|
605
|
+
try? FileManager.default.removeItem(at: destURL)
|
|
606
|
+
DispatchQueue.main.async { self.audioDownloadTask = nil }
|
|
607
|
+
return
|
|
608
|
+
}
|
|
609
|
+
DispatchQueue.main.async {
|
|
610
|
+
self.localAudioFileURL = destURL
|
|
611
|
+
self.localAudioAsset = localAsset
|
|
612
|
+
self.audioDownloadTask = nil
|
|
613
|
+
self.lastKnownWaveformSize = .zero
|
|
614
|
+
self.setNeedsLayout()
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
} catch {
|
|
618
|
+
print("AudioWaveform: Failed to move downloaded file: \(error)")
|
|
619
|
+
DispatchQueue.main.async { self.audioDownloadTask = nil }
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
audioDownloadTask = task
|
|
623
|
+
task.resume()
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/// Determine the correct audio file extension from the HTTP response.
|
|
627
|
+
/// Priority: Content-Disposition → MIME type → URL path extension → "m4a" fallback.
|
|
628
|
+
private static func audioFileExtension(from response: URLResponse?, originalURL: URL) -> String {
|
|
629
|
+
if let suggested = response?.suggestedFilename, !suggested.isEmpty {
|
|
630
|
+
let ext = (suggested as NSString).pathExtension
|
|
631
|
+
if !ext.isEmpty { return ext }
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if let mimeType = response?.mimeType?.lowercased() {
|
|
635
|
+
switch mimeType {
|
|
636
|
+
case "audio/mpeg", "audio/mp3": return "mp3"
|
|
637
|
+
case "audio/mp4", "audio/x-m4a", "audio/aac": return "m4a"
|
|
638
|
+
case "audio/wav", "audio/x-wav", "audio/wave": return "wav"
|
|
639
|
+
case "audio/flac": return "flac"
|
|
640
|
+
case "audio/ogg", "audio/vorbis": return "ogg"
|
|
641
|
+
case "audio/aiff", "audio/x-aiff": return "aiff"
|
|
642
|
+
default: break
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
let urlExt = originalURL.pathExtension
|
|
647
|
+
if !urlExt.isEmpty { return urlExt }
|
|
648
|
+
|
|
649
|
+
return "m4a"
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/// Decode PCM samples from the given asset's audio track and compute
|
|
653
|
+
/// per-bar RMS amplitudes normalised to [0, 1].
|
|
654
|
+
///
|
|
655
|
+
/// Runs the heavy decode on a background queue and posts the result
|
|
656
|
+
/// back to the main thread. The AVAssetReader is stored in
|
|
657
|
+
/// `currentAssetReader` so it can be cancelled if the editor is closed
|
|
658
|
+
/// or the view resizes mid-read.
|
|
659
|
+
private func readWaveformSamples(from asset: AVAsset, audioTrack: AVAssetTrack, size: CGSize) {
|
|
660
|
+
lastKnownWaveformSize = size
|
|
661
|
+
lastKnownWaveformRange = visibleRange
|
|
662
|
+
|
|
663
|
+
waveformView.isHidden = false
|
|
664
|
+
|
|
665
|
+
currentAssetReader?.cancelReading()
|
|
666
|
+
currentAssetReader = nil
|
|
667
|
+
|
|
668
|
+
let timeRange = visibleRange
|
|
669
|
+
let step = waveformBarWidth + waveformBarGap
|
|
670
|
+
let barCount = max(1, Int(floor(size.width / step)))
|
|
671
|
+
|
|
672
|
+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
673
|
+
guard let self = self else { return }
|
|
674
|
+
|
|
675
|
+
let reader: AVAssetReader
|
|
676
|
+
do {
|
|
677
|
+
reader = try AVAssetReader(asset: asset)
|
|
678
|
+
} catch {
|
|
679
|
+
print("AudioWaveform: Failed to create AVAssetReader: \(error)")
|
|
680
|
+
return
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
reader.timeRange = timeRange
|
|
684
|
+
|
|
685
|
+
let outputSettings: [String: Any] = [
|
|
686
|
+
AVFormatIDKey: kAudioFormatLinearPCM,
|
|
687
|
+
AVLinearPCMIsFloatKey: true,
|
|
688
|
+
AVLinearPCMBitDepthKey: 32,
|
|
689
|
+
AVNumberOfChannelsKey: 1,
|
|
690
|
+
]
|
|
691
|
+
let output = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: outputSettings)
|
|
692
|
+
|
|
693
|
+
guard reader.canAdd(output) else {
|
|
694
|
+
print("AudioWaveform: Cannot add output to reader")
|
|
695
|
+
return
|
|
696
|
+
}
|
|
697
|
+
reader.add(output)
|
|
698
|
+
|
|
699
|
+
DispatchQueue.main.sync {
|
|
700
|
+
self.currentAssetReader = reader
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
guard reader.startReading() else {
|
|
704
|
+
print("AudioWaveform: Failed to start reading: \(String(describing: reader.error))")
|
|
705
|
+
return
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
var allSamples = [Float]()
|
|
709
|
+
allSamples.reserveCapacity(barCount * 512)
|
|
710
|
+
|
|
711
|
+
while reader.status == .reading {
|
|
712
|
+
guard let sampleBuffer = output.copyNextSampleBuffer() else { break }
|
|
713
|
+
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else { continue }
|
|
714
|
+
|
|
715
|
+
let length = CMBlockBufferGetDataLength(blockBuffer)
|
|
716
|
+
let sampleCount = length / MemoryLayout<Float>.size
|
|
717
|
+
guard sampleCount > 0 else { continue }
|
|
718
|
+
|
|
719
|
+
var data = [Float](repeating: 0, count: sampleCount)
|
|
720
|
+
CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: length, destination: &data)
|
|
721
|
+
allSamples.append(contentsOf: data)
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
guard !allSamples.isEmpty else {
|
|
725
|
+
DispatchQueue.main.async {
|
|
726
|
+
self.waveformView.amplitudes = []
|
|
727
|
+
}
|
|
728
|
+
return
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
let samplesPerBar = max(1, allSamples.count / barCount)
|
|
732
|
+
var amplitudes = [CGFloat]()
|
|
733
|
+
amplitudes.reserveCapacity(barCount)
|
|
734
|
+
|
|
735
|
+
for i in 0..<barCount {
|
|
736
|
+
let start = i * samplesPerBar
|
|
737
|
+
let end = min(start + samplesPerBar, allSamples.count)
|
|
738
|
+
guard start < allSamples.count else {
|
|
739
|
+
amplitudes.append(0)
|
|
740
|
+
continue
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
var sumSquares: Float = 0
|
|
744
|
+
for j in start..<end {
|
|
745
|
+
let s = allSamples[j]
|
|
746
|
+
sumSquares += s * s
|
|
747
|
+
}
|
|
748
|
+
let rms = sqrt(sumSquares / Float(end - start))
|
|
749
|
+
amplitudes.append(CGFloat(rms))
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
let maxAmp = amplitudes.max() ?? 1
|
|
753
|
+
let normalizer: CGFloat = maxAmp > 0 ? 1.0 / maxAmp : 1.0
|
|
754
|
+
let normalized = amplitudes.map { min($0 * normalizer, 1.0) }
|
|
755
|
+
|
|
756
|
+
DispatchQueue.main.async {
|
|
757
|
+
self.waveformView.amplitudes = normalized
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/// Delete the temporary local audio file created by downloadAudioForWaveform.
|
|
763
|
+
private func cleanupLocalAudioFile() {
|
|
764
|
+
if let url = localAudioFileURL {
|
|
765
|
+
try? FileManager.default.removeItem(at: url)
|
|
766
|
+
localAudioFileURL = nil
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
475
770
|
private func timeForLocation(_ x: CGFloat) -> CMTime {
|
|
476
771
|
let size = bounds.size
|
|
477
772
|
let inset = thumbView.chevronWidth + horizontalInset
|
|
@@ -942,6 +1237,11 @@ import AVFoundation
|
|
|
942
1237
|
}
|
|
943
1238
|
|
|
944
1239
|
regenerateThumbnailsIfNeeded()
|
|
1240
|
+
regenerateWaveformIfNeeded()
|
|
1241
|
+
// Inset waveformView by the leading handle's chevron width so its
|
|
1242
|
+
// background doesn't bleed underneath the translucent handle area.
|
|
1243
|
+
let waveformLeft = thumbView.chevronWidth
|
|
1244
|
+
waveformView.frame = CGRect(x: waveformLeft, y: 0, width: max(0, thumbnailTrackView.bounds.width - waveformLeft), height: thumbnailTrackView.bounds.height)
|
|
945
1245
|
|
|
946
1246
|
for thumbnail in thumbnails {
|
|
947
1247
|
let position = locationForTime(thumbnail.time) - horizontalInset + thumbnailOffset
|
|
@@ -52,6 +52,11 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
52
52
|
private var trimmerColor: UIColor = UIColor.systemYellow
|
|
53
53
|
private var handleIconColor: UIColor = UIColor.black
|
|
54
54
|
private var isLightTheme = false
|
|
55
|
+
private var waveformBarColor: UIColor = .white
|
|
56
|
+
private var waveformBgColor: UIColor = UIColor(red: 0.204, green: 0.471, blue: 0.965, alpha: 1)
|
|
57
|
+
private var waveformBarWidth: CGFloat = 3
|
|
58
|
+
private var waveformBarGap: CGFloat = 2
|
|
59
|
+
private var waveformBarCornerRadius: CGFloat = 1.5
|
|
55
60
|
private var iconColor: UIColor { isLightTheme ? .black : .white }
|
|
56
61
|
private var dimmedIconColor: UIColor { iconColor.withAlphaComponent(0.5) }
|
|
57
62
|
|
|
@@ -243,6 +248,11 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
243
248
|
|
|
244
249
|
playerController.player = nil
|
|
245
250
|
playerController.dismiss(animated: false, completion: nil)
|
|
251
|
+
|
|
252
|
+
// Setting asset to nil triggers VideoTrimmer.asset.didSet, which
|
|
253
|
+
// cancels all in-flight work (thumbnail generation, waveform reader,
|
|
254
|
+
// audio download) and deletes temporary files.
|
|
255
|
+
trimmer?.asset = nil
|
|
246
256
|
}
|
|
247
257
|
|
|
248
258
|
public func pausePlayer() {
|
|
@@ -389,6 +399,12 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
389
399
|
private func setupVideoTrimmer() {
|
|
390
400
|
trimmer = VideoTrimmer()
|
|
391
401
|
trimmer.isLightTheme = isLightTheme
|
|
402
|
+
trimmer.waveformBarColor = waveformBarColor
|
|
403
|
+
trimmer.waveformBgColor = waveformBgColor
|
|
404
|
+
trimmer.waveformBarWidth = waveformBarWidth
|
|
405
|
+
trimmer.waveformBarGap = waveformBarGap
|
|
406
|
+
trimmer.waveformBarCornerRadius = waveformBarCornerRadius
|
|
407
|
+
trimmer.isAudioOnly = !isVideoType
|
|
392
408
|
trimmer.asset = asset
|
|
393
409
|
trimmer.minimumDuration = CMTime(seconds: 1, preferredTimescale: 600)
|
|
394
410
|
trimmer.enableHapticFeedback = enableHapticFeedback
|
|
@@ -972,6 +988,21 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
972
988
|
if let handleIconColorValue = config["handleIconColor"] as? Double {
|
|
973
989
|
handleIconColor = RCTConvert.uiColor(handleIconColorValue) ?? (isLightTheme ? .white : .black)
|
|
974
990
|
}
|
|
991
|
+
if let v = config["waveformColor"] as? Double {
|
|
992
|
+
waveformBarColor = RCTConvert.uiColor(v) ?? .white
|
|
993
|
+
}
|
|
994
|
+
if let v = config["waveformBackgroundColor"] as? Double {
|
|
995
|
+
waveformBgColor = RCTConvert.uiColor(v) ?? UIColor(red: 0.204, green: 0.471, blue: 0.965, alpha: 1)
|
|
996
|
+
}
|
|
997
|
+
if let v = config["waveformBarWidth"] as? Double, v > 0 {
|
|
998
|
+
waveformBarWidth = CGFloat(v)
|
|
999
|
+
}
|
|
1000
|
+
if let v = config["waveformBarGap"] as? Double, v >= 0 {
|
|
1001
|
+
waveformBarGap = CGFloat(v)
|
|
1002
|
+
}
|
|
1003
|
+
if let v = config["waveformBarCornerRadius"] as? Double, v >= 0 {
|
|
1004
|
+
waveformBarCornerRadius = CGFloat(v)
|
|
1005
|
+
}
|
|
975
1006
|
}
|
|
976
1007
|
|
|
977
1008
|
private func onPlayerReady() {
|
|
@@ -992,7 +1023,8 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
992
1023
|
|
|
993
1024
|
if jumpToPositionOnLoad > 0 {
|
|
994
1025
|
let duration = (asset?.duration.seconds ?? 0) * 1000
|
|
995
|
-
let
|
|
1026
|
+
let endMs = trimmer.selectedRange.end.seconds * 1000
|
|
1027
|
+
let time = min(jumpToPositionOnLoad, min(duration, endMs))
|
|
996
1028
|
let cmtime = CMTime(value: CMTimeValue(time), timescale: 1000)
|
|
997
1029
|
|
|
998
1030
|
self.seek(to: cmtime)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;AA+GA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -63,6 +63,11 @@ function createEditorConfig(overrides = {}) {
|
|
|
63
63
|
alertOnFailTitle: 'Error',
|
|
64
64
|
alertOnFailMessage: 'Fail to load media. Possibly invalid file or no network connection',
|
|
65
65
|
alertOnFailCloseText: 'Close',
|
|
66
|
+
waveformColor: processColor('white'),
|
|
67
|
+
waveformBackgroundColor: processColor('#3478F6'),
|
|
68
|
+
waveformBarWidth: 3,
|
|
69
|
+
waveformBarGap: 2,
|
|
70
|
+
waveformBarCornerRadius: 1.5,
|
|
66
71
|
...createBaseOptions(overrides),
|
|
67
72
|
...overrides
|
|
68
73
|
};
|
|
@@ -88,17 +93,23 @@ export function showEditor(filePath, config) {
|
|
|
88
93
|
const {
|
|
89
94
|
headerTextColor,
|
|
90
95
|
trimmerColor,
|
|
91
|
-
handleIconColor
|
|
96
|
+
handleIconColor,
|
|
97
|
+
waveformColor,
|
|
98
|
+
waveformBackgroundColor
|
|
92
99
|
} = config;
|
|
93
100
|
const isLight = config.theme === 'light';
|
|
94
101
|
const _headerTextColor = processColor(headerTextColor || (isLight ? 'black' : 'white'));
|
|
95
102
|
const _trimmerColor = processColor(trimmerColor || '#f1d247');
|
|
96
103
|
const _handleIconColor = processColor(handleIconColor || (isLight ? 'white' : 'black'));
|
|
104
|
+
const _waveformColor = processColor(waveformColor || 'white');
|
|
105
|
+
const _waveformBackgroundColor = processColor(waveformBackgroundColor || '#3478F6');
|
|
97
106
|
VideoTrim.showEditor(filePath, createEditorConfig({
|
|
98
107
|
...config,
|
|
99
108
|
headerTextColor: _headerTextColor,
|
|
100
109
|
trimmerColor: _trimmerColor,
|
|
101
|
-
handleIconColor: _handleIconColor
|
|
110
|
+
handleIconColor: _handleIconColor,
|
|
111
|
+
waveformColor: _waveformColor,
|
|
112
|
+
waveformBackgroundColor: _waveformBackgroundColor
|
|
102
113
|
}));
|
|
103
114
|
}
|
|
104
115
|
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","isLight","theme","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,gBAAgB,MAAM,sBAAmB;AAChD,OAAOC,gBAAgB,MAAM,cAAW;AAQxC,SAASC,YAAY,QAAQ,cAAc;;AAE3C;AACA,MAAMC,QAAQ,GAAG,CAAC,CAAEC,MAAM,CAASC,qBAAqB;AACxD,MAAMC,SAAS,GAAGH,QAAQ,GAAGH,gBAAgB,GAAGC,gBAAgB;AAEhE,SAASM,iBAAiBA,CAACC,SAA+B,GAAG,CAAC,CAAC,EAAe;EAC5E,OAAO;IACLC,WAAW,EAAE,KAAK;IAClBC,IAAI,EAAE,OAAO;IACbC,SAAS,EAAE,KAAK;IAChBC,uBAAuB,EAAE,KAAK;IAC9BC,4BAA4B,EAAE,KAAK;IACnCC,qBAAqB,EAAE,KAAK;IAC5B,GAAGN;EACL,CAAC;AACH;AAEA,SAASO,kBAAkBA,CACzBP,SAAgC,GAAG,CAAC,CAAC,EACvB;EACd,OAAO;IACLQ,oBAAoB,EAAE,IAAI;IAC1BC,WAAW,EAAE,CAAC,CAAC;IACfC,WAAW,EAAE,CAAC,CAAC;IACfC,qBAAqB,EAAE,KAAK;IAC5BC,sBAAsB,EAAE,KAAK;IAC7BC,2BAA2B,EAAE,KAAK;IAClCC,gCAAgC,EAAE,KAAK;IACvCC,iBAAiB,EAAE,KAAK;IACxBC,wBAAwB,EAAE,KAAK;IAC/BC,gBAAgB,EAAE,QAAQ;IAC1BC,cAAc,EAAE,MAAM;IACtBC,kBAAkB,EAAE,IAAI;IACxBC,iBAAiB,EAAE,UAAU;IAC7BC,mBAAmB,EAAE,8BAA8B;IACnDC,sBAAsB,EAAE,OAAO;IAC/BC,uBAAuB,EAAE,SAAS;IAClCC,gBAAgB,EAAE,IAAI;IACtBC,eAAe,EAAE,eAAe;IAChCC,iBAAiB,EAAE,4BAA4B;IAC/CC,oBAAoB,EAAE,OAAO;IAC7BC,qBAAqB,EAAE,SAAS;IAChCC,YAAY,EAAE,mBAAmB;IACjCC,kBAAkB,EAAE,KAAK;IACzBC,QAAQ,EAAE,KAAK;IACfC,oBAAoB,EAAE,CAAC,CAAC;IACxBC,eAAe,EAAE,IAAI;IACrBC,oBAAoB,EAAE,IAAI;IAC1BC,wBAAwB,EAAE,QAAQ;IAClCC,0BAA0B,EAAE,IAAI;IAChCC,yBAAyB,EAAE,UAAU;IACrCC,2BAA2B,EAAE,uCAAuC;IACpEC,8BAA8B,EAAE,OAAO;IACvCC,+BAA+B,EAAE,SAAS;IAC1CC,UAAU,EAAE,EAAE;IACdC,cAAc,EAAE,EAAE;IAClBC,eAAe,EAAEjD,YAAY,CAAC,OAAO,CAAW;IAChDkD,YAAY,EAAElD,YAAY,CAAC,SAAS,CAAW;IAC/CmD,eAAe,EAAEnD,YAAY,CAAC,OAAO,CAAW;IAChDoD,qBAAqB,EAAE,IAAI;IAC3BC,iBAAiB,EAAE,IAAI;IACvBC,gBAAgB,EAAE,OAAO;IACzBC,kBAAkB,EAChB,oEAAoE;IACtEC,oBAAoB,EAAE,OAAO;
|
|
1
|
+
{"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","trimmerColor","handleIconColor","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","waveformColor","waveformBackgroundColor","waveformBarWidth","waveformBarGap","waveformBarCornerRadius","createTrimOptions","startTime","endTime","showEditor","filePath","config","isLight","theme","_headerTextColor","_trimmerColor","_handleIconColor","_waveformColor","_waveformBackgroundColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -126,6 +126,16 @@ export interface EditorConfig extends BaseOptions {
|
|
|
126
126
|
* `"light"` uses a white background with black icons/text and white trimmer-handle chevrons.
|
|
127
127
|
*/
|
|
128
128
|
theme?: string;
|
|
129
|
+
/** Color of the audio waveform bars as a `processColor` value. */
|
|
130
|
+
waveformColor?: number;
|
|
131
|
+
/** Background color behind the audio waveform bars as a `processColor` value. */
|
|
132
|
+
waveformBackgroundColor?: number;
|
|
133
|
+
/** Width of each waveform bar in dp/pt (default: `3`). */
|
|
134
|
+
waveformBarWidth?: number;
|
|
135
|
+
/** Gap between waveform bars in dp/pt (default: `2`). */
|
|
136
|
+
waveformBarGap?: number;
|
|
137
|
+
/** Corner radius of waveform bars in dp/pt (default: `1.5`). */
|
|
138
|
+
waveformBarCornerRadius?: number;
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
131
141
|
* Options for headless (non-UI) trim operations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,10 +8,12 @@ declare const VideoTrim: any;
|
|
|
8
8
|
* @param {Function} onEvent: event callback
|
|
9
9
|
* @returns {void}
|
|
10
10
|
*/
|
|
11
|
-
export declare function showEditor(filePath: string, config: Partial<Omit<EditorConfig, 'headerTextColor' | 'trimmerColor' | 'handleIconColor'>> & {
|
|
11
|
+
export declare function showEditor(filePath: string, config: Partial<Omit<EditorConfig, 'headerTextColor' | 'trimmerColor' | 'handleIconColor' | 'waveformColor' | 'waveformBackgroundColor'>> & {
|
|
12
12
|
headerTextColor?: string;
|
|
13
13
|
trimmerColor?: string;
|
|
14
14
|
handleIconColor?: string;
|
|
15
|
+
waveformColor?: string;
|
|
16
|
+
waveformBackgroundColor?: string;
|
|
15
17
|
}): void;
|
|
16
18
|
/**
|
|
17
19
|
* List output files generated at all time
|