From e150e7f6c5011ed05861895a1d571002fcb3558f Mon Sep 17 00:00:00 2001 From: hm21 Date: Fri, 6 Mar 2026 16:22:04 +0100 Subject: [PATCH 1/2] feat(audio): add looping functionality for custom audio in video rendering --- CHANGELOG.md | 3 ++ .../render/helpers/AudioSequenceBuilder.kt | 37 ++++++++++++++++++- .../render/helpers/CompositionBuilder.kt | 1 + .../features/render/models/RenderConfig.kt | 9 ++++- .../features/render/video_renderer_page.dart | 26 +++++++++++++ .../src/features/render/RenderVideo.swift | 3 +- .../render/helpers/ApplyComposition.swift | 4 +- .../render/helpers/AudioSequenceBuilder.swift | 17 ++++++++- .../render/helpers/CompositionBuilder.swift | 11 ++++++ .../features/render/models/RenderConfig.swift | 8 +++- .../models/video/video_render_data_model.dart | 17 +++++++++ .../src/features/render/RenderVideo.swift | 3 +- .../render/helpers/ApplyComposition.swift | 4 +- .../render/helpers/AudioSequenceBuilder.swift | 17 ++++++++- .../render/helpers/CompositionBuilder.swift | 11 ++++++ .../features/render/models/RenderConfig.swift | 8 +++- pubspec.yaml | 2 +- 17 files changed, 168 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88dd7f..f308fd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.7.0 +- **FEAT**(android, iOS, macOS): Add `loopCustomAudio` option to `VideoRenderData`. When `false`, custom audio plays once instead of looping to match the video duration. Defaults to `true` for backward compatibility. + ## 1.6.2 - **FIX**(iOS, macOS): Fixed crash when merging multiple MOV video clips on older devices (e.g., iPhone 7, iOS 15). The issue was caused by `AVMutableVideoCompositionInstruction` not properly deriving `requiredSourceTrackIDs` from layer instructions when using a custom video compositor. Introduced `CustomVideoCompositionInstruction` that explicitly provides source track IDs. diff --git a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/AudioSequenceBuilder.kt b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/AudioSequenceBuilder.kt index 8465a39..adc674a 100644 --- a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/AudioSequenceBuilder.kt +++ b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/AudioSequenceBuilder.kt @@ -26,6 +26,7 @@ class AudioSequenceBuilder( ) { private var volume: Float = 1.0f private var needsNormalization: Boolean = false + private var loopAudio: Boolean = true /** * Sets the volume multiplier for the custom audio. @@ -48,6 +49,16 @@ class AudioSequenceBuilder( return this } + /** + * Sets whether the audio should loop to match video duration. + * + * @param loop If true, audio repeats; if false, plays once + */ + fun setLoop(loop: Boolean): AudioSequenceBuilder { + this.loopAudio = loop + return this + } + /** * Builds the audio sequence with looping to match video duration. * @@ -73,8 +84,12 @@ class AudioSequenceBuilder( val audioProcessors = buildAudioProcessors() val audioEffects = Effects(audioProcessors, emptyList()) - // Create audio items with looping - val audioItems = createLoopedAudioItems(audioFile, audioDurationUs, audioEffects) + // Create audio items with looping or single play + val audioItems = if (loopAudio) { + createLoopedAudioItems(audioFile, audioDurationUs, audioEffects) + } else { + createSingleAudioItem(audioFile, audioDurationUs, audioEffects) + } return EditedMediaItemSequence.Builder(audioItems).build() } @@ -191,6 +206,24 @@ class AudioSequenceBuilder( return audioItems } + /** + * Creates a single audio item (no looping). Trims if audio is longer than video. + */ + private fun createSingleAudioItem( + audioFile: File, + audioDurationUs: Long, + effects: Effects + ): List { + val trimDurationUs = if (audioDurationUs > videoDurationUs && videoDurationUs > 0) { + Log.d(RENDER_TAG, "Trimming audio to ${videoDurationUs / 1000} ms (no loop)") + videoDurationUs + } else { + Log.d(RENDER_TAG, "Playing audio once (${audioDurationUs / 1000} ms, no loop)") + null + } + return listOf(createAudioItem(audioFile, trimDurationUs, effects)) + } + /** * Creates a single audio EditedMediaItem with optional trimming. */ diff --git a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/CompositionBuilder.kt b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/CompositionBuilder.kt index 8ab5ef5..550a5d6 100644 --- a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/CompositionBuilder.kt +++ b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/helpers/CompositionBuilder.kt @@ -110,6 +110,7 @@ class CompositionBuilder( val audioSequence = AudioSequenceBuilder(config.customAudioPath!!, totalVideoDuration) .setVolume(config.customAudioVolume ?: 1.0f) .setNormalization(needsNormalization) + .setLoop(config.loopCustomAudio) .build() if (audioSequence != null) { diff --git a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/models/RenderConfig.kt b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/models/RenderConfig.kt index 98f2d97..71d0af5 100644 --- a/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/models/RenderConfig.kt +++ b/android/src/main/kotlin/ch/waio/pro_video_editor/src/features/render/models/RenderConfig.kt @@ -50,7 +50,11 @@ data class RenderConfig( /** Whether to apply cropping to the image overlay along with the video. * When true, the image overlay is applied before cropping (cropped together with video). * When false (default), the overlay is scaled to the final cropped size. */ - val imageBytesWithCropping: Boolean = false + val imageBytesWithCropping: Boolean = false, + /** Whether to loop the custom audio if it is shorter than the video. + * When true (default), audio is repeated to match video duration. + * When false, audio plays once and silence fills the rest. */ + val loopCustomAudio: Boolean = true ) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -129,7 +133,8 @@ data class RenderConfig( startUs = call.argument("startUs")?.toLong(), endUs = call.argument("endUs")?.toLong(), shouldOptimizeForNetworkUse = call.argument("shouldOptimizeForNetworkUse") ?: true, - imageBytesWithCropping = call.argument("imageBytesWithCropping") ?: false + imageBytesWithCropping = call.argument("imageBytesWithCropping") ?: false, + loopCustomAudio = call.argument("loopCustomAudio") ?: true ) } } diff --git a/example/lib/features/render/video_renderer_page.dart b/example/lib/features/render/video_renderer_page.dart index a394fb4..f86f5bf 100644 --- a/example/lib/features/render/video_renderer_page.dart +++ b/example/lib/features/render/video_renderer_page.dart @@ -226,6 +226,26 @@ class _VideoRendererPageState extends State { await _renderVideo(data); } + /// Play custom audio once without looping. + /// + /// By default, custom audio loops to match the video duration. + /// Setting `loopCustomAudio: false` plays the audio only once, + /// with silence for the remaining video duration. + Future _customAudioNoLoop() async { + final customAudioFile = + await _writeAssetAudioToFile(kVideoEditorExampleAudio1Path); + + var data = VideoRenderData( + video: _video, + customAudioPath: customAudioFile.path, + originalAudioVolume: 0.0, + customAudioVolume: 1.0, + loopCustomAudio: false, + ); + + await _renderVideo(data); + } + Future _layers() async { final imageBytes = await _captureLayerContent(); var data = VideoRenderData( @@ -716,6 +736,12 @@ class _VideoRendererPageState extends State { title: const Text('Adjust Original Volume'), subtitle: const Text('Reduce to 20%'), ), + ListTile( + onTap: _customAudioNoLoop, + leading: const Icon(Icons.music_off_outlined), + title: const Text('Custom Audio Without Loop'), + subtitle: const Text('Plays once, then silence'), + ), ..._buildSectionTitle('Quality'), ListTile( onTap: _qualityPreset1080p, diff --git a/ios/Classes/src/features/render/RenderVideo.swift b/ios/Classes/src/features/render/RenderVideo.swift index 24d7891..f6dec21 100644 --- a/ios/Classes/src/features/render/RenderVideo.swift +++ b/ios/Classes/src/features/render/RenderVideo.swift @@ -107,7 +107,8 @@ class RenderVideo { enableAudio: config.enableAudio, customAudioPath: config.customAudioPath, originalAudioVolume: config.originalAudioVolume, - customAudioVolume: config.customAudioVolume + customAudioVolume: config.customAudioVolume, + loopCustomAudio: config.loopCustomAudio ) // Set source track ID for fallback on older iOS versions (e.g., iPhone 7) diff --git a/ios/Classes/src/features/render/helpers/ApplyComposition.swift b/ios/Classes/src/features/render/helpers/ApplyComposition.swift index 741e9b6..348bdd6 100644 --- a/ios/Classes/src/features/render/helpers/ApplyComposition.swift +++ b/ios/Classes/src/features/render/helpers/ApplyComposition.swift @@ -29,12 +29,14 @@ func applyComposition( enableAudio: Bool, customAudioPath: String?, originalAudioVolume: Float?, - customAudioVolume: Float? + customAudioVolume: Float?, + loopCustomAudio: Bool ) async throws -> (AVMutableComposition, AVMutableVideoComposition, CGSize, AVAudioMix?, CMPersistentTrackID) { return try await CompositionBuilder(videoClips: videoClips, videoEffects: videoEffects) .setEnableAudio(enableAudio) .setCustomAudioPath(customAudioPath) .setOriginalAudioVolume(originalAudioVolume) .setCustomAudioVolume(customAudioVolume) + .setLoopCustomAudio(loopCustomAudio) .build() } diff --git a/ios/Classes/src/features/render/helpers/AudioSequenceBuilder.swift b/ios/Classes/src/features/render/helpers/AudioSequenceBuilder.swift index 63458f3..9e1182c 100644 --- a/ios/Classes/src/features/render/helpers/AudioSequenceBuilder.swift +++ b/ios/Classes/src/features/render/helpers/AudioSequenceBuilder.swift @@ -10,6 +10,7 @@ internal class AudioSequenceBuilder { private let audioPath: String private let targetDuration: CMTime private var volume: Float = 1.0 + private var loopAudio: Bool = true /// Initializes builder with audio path and target duration. /// @@ -30,6 +31,15 @@ internal class AudioSequenceBuilder { return self } + /// Sets whether the audio should loop to match video duration. + /// + /// - Parameter loop: If true, audio repeats; if false, plays once + /// - Returns: Self for chaining + func setLoop(_ loop: Bool) -> AudioSequenceBuilder { + self.loopAudio = loop + return self + } + /// Builds custom audio track and adds it to composition. /// /// Trims or loops the audio to match target duration and applies volume. @@ -68,7 +78,7 @@ internal class AudioSequenceBuilder { let timeRange = CMTimeRange(start: .zero, duration: targetDuration) try compositionAudioTrack.insertTimeRange(timeRange, of: audioTrack, at: .zero) print("✂️ Custom audio trimmed to \(targetDuration.seconds)s") - } else { + } else if loopAudio { // Loop audio to match video duration var currentTime = CMTime.zero var loopCount = 0 @@ -84,6 +94,11 @@ internal class AudioSequenceBuilder { } print("🔄 Custom audio looped \(loopCount) times to match \(targetDuration.seconds)s duration") + } else { + // Play audio once without looping + let timeRange = CMTimeRange(start: .zero, duration: audioDuration) + try compositionAudioTrack.insertTimeRange(timeRange, of: audioTrack, at: .zero) + print("▶️ Custom audio plays once (\(audioDuration.seconds)s, no loop)") } if volume != 1.0 { diff --git a/ios/Classes/src/features/render/helpers/CompositionBuilder.swift b/ios/Classes/src/features/render/helpers/CompositionBuilder.swift index 4d6fb3c..98cc071 100644 --- a/ios/Classes/src/features/render/helpers/CompositionBuilder.swift +++ b/ios/Classes/src/features/render/helpers/CompositionBuilder.swift @@ -14,6 +14,7 @@ internal class CompositionBuilder { private var customAudioPath: String? private var originalAudioVolume: Float = 1.0 private var customAudioVolume: Float = 1.0 + private var loopCustomAudio: Bool = true /// Initializes builder with configuration. /// @@ -61,6 +62,15 @@ internal class CompositionBuilder { return self } + /// Sets whether custom audio should loop. + /// + /// - Parameter loop: If true, audio repeats to match video duration + /// - Returns: Self for chaining + func setLoopCustomAudio(_ loop: Bool) -> CompositionBuilder { + self.loopCustomAudio = loop + return self + } + /// Builds the complete composition. /// /// - Returns: Tuple containing composition, video composition, render size, audio mix, and source track ID @@ -100,6 +110,7 @@ internal class CompositionBuilder { audioPath: customPath, targetDuration: videoResult.totalDuration ).setVolume(customAudioVolume) + .setLoop(loopCustomAudio) customAudioTrack = try await audioBuilder.build(in: composition) } diff --git a/ios/Classes/src/features/render/models/RenderConfig.swift b/ios/Classes/src/features/render/models/RenderConfig.swift index 051fbec..da8e876 100644 --- a/ios/Classes/src/features/render/models/RenderConfig.swift +++ b/ios/Classes/src/features/render/models/RenderConfig.swift @@ -84,6 +84,11 @@ struct RenderConfig { /// When true, the image overlay is cropped together with the video. /// When false (default), the overlay is scaled to the final cropped size. let imageBytesWithCropping: Bool + + /// Whether to loop the custom audio if it is shorter than the video. + /// When true (default), audio is repeated to match video duration. + /// When false, audio plays once and silence fills the rest. + let loopCustomAudio: Bool static func fromArguments(_ arguments: [String: Any]?) -> RenderConfig? { guard let args = arguments else { return nil @@ -154,7 +159,8 @@ struct RenderConfig { startUs: (args["startUs"] as? NSNumber)?.int64Value, endUs: (args["endUs"] as? NSNumber)?.int64Value, shouldOptimizeForNetworkUse: args["shouldOptimizeForNetworkUse"] as? Bool ?? true, - imageBytesWithCropping: args["imageBytesWithCropping"] as? Bool ?? false + imageBytesWithCropping: args["imageBytesWithCropping"] as? Bool ?? false, + loopCustomAudio: args["loopCustomAudio"] as? Bool ?? true ) } } diff --git a/lib/core/models/video/video_render_data_model.dart b/lib/core/models/video/video_render_data_model.dart index c2334ff..a6d3cd0 100644 --- a/lib/core/models/video/video_render_data_model.dart +++ b/lib/core/models/video/video_render_data_model.dart @@ -33,6 +33,7 @@ class VideoRenderData { this.customAudioVolume, this.shouldOptimizeForNetworkUse = false, this.imageBytesWithCropping = false, + this.loopCustomAudio = true, String? id, }) : id = id ?? DateTime.now().microsecondsSinceEpoch.toString(), assert( @@ -104,6 +105,7 @@ class VideoRenderData { double? customAudioVolume, bool shouldOptimizeForNetworkUse = false, bool imageBytesWithCropping = false, + bool loopCustomAudio = true, String? id, }) { final qualityConfig = VideoQualityConfig.fromPreset(qualityPreset); @@ -127,6 +129,7 @@ class VideoRenderData { customAudioVolume: customAudioVolume, shouldOptimizeForNetworkUse: shouldOptimizeForNetworkUse, imageBytesWithCropping: imageBytesWithCropping, + loopCustomAudio: loopCustomAudio, ); } @@ -298,6 +301,17 @@ class VideoRenderData { /// - `true`: Overlay is cropped together with the video final bool imageBytesWithCropping; + /// Whether to loop the custom audio track if it is shorter than the video. + /// + /// When `true` (default), the custom audio will be repeated until it + /// matches the video duration. When `false`, the audio plays once and + /// silence fills the remaining duration. + /// + /// This parameter is only effective when [customAudioPath] is provided. + /// + /// **Default**: `true` + final bool loopCustomAudio; + /// Returns a [Stream] of [ProgressModel] objects that provides updates on /// the progress of the video rendering process associated with this model's /// [id]. @@ -367,6 +381,7 @@ class VideoRenderData { 'endUs': videoSegments != null ? endTime?.inMicroseconds : null, 'shouldOptimizeForNetworkUse': shouldOptimizeForNetworkUse, 'imageBytesWithCropping': imageBytesWithCropping, + 'loopCustomAudio': loopCustomAudio, }; } @@ -391,6 +406,7 @@ class VideoRenderData { double? customAudioVolume, bool? shouldOptimizeForNetworkUse, bool? imageBytesWithCropping, + bool? loopCustomAudio, }) { return VideoRenderData( id: id ?? this.id, @@ -414,6 +430,7 @@ class VideoRenderData { shouldOptimizeForNetworkUse ?? this.shouldOptimizeForNetworkUse, imageBytesWithCropping: imageBytesWithCropping ?? this.imageBytesWithCropping, + loopCustomAudio: loopCustomAudio ?? this.loopCustomAudio, ); } } diff --git a/macos/Classes/src/features/render/RenderVideo.swift b/macos/Classes/src/features/render/RenderVideo.swift index 587ba8f..b7bbe49 100644 --- a/macos/Classes/src/features/render/RenderVideo.swift +++ b/macos/Classes/src/features/render/RenderVideo.swift @@ -108,7 +108,8 @@ class RenderVideo { enableAudio: config.enableAudio, customAudioPath: config.customAudioPath, originalAudioVolume: config.originalAudioVolume, - customAudioVolume: config.customAudioVolume + customAudioVolume: config.customAudioVolume, + loopCustomAudio: config.loopCustomAudio ) // Set source track ID for fallback on older macOS versions diff --git a/macos/Classes/src/features/render/helpers/ApplyComposition.swift b/macos/Classes/src/features/render/helpers/ApplyComposition.swift index f40cf8b..44ef844 100644 --- a/macos/Classes/src/features/render/helpers/ApplyComposition.swift +++ b/macos/Classes/src/features/render/helpers/ApplyComposition.swift @@ -29,12 +29,14 @@ func applyComposition( enableAudio: Bool, customAudioPath: String?, originalAudioVolume: Float?, - customAudioVolume: Float? + customAudioVolume: Float?, + loopCustomAudio: Bool ) async throws -> (AVMutableComposition, AVMutableVideoComposition, CGSize, AVAudioMix?, CMPersistentTrackID) { return try await CompositionBuilder(videoClips: videoClips, videoEffects: videoEffects) .setEnableAudio(enableAudio) .setCustomAudioPath(customAudioPath) .setOriginalAudioVolume(originalAudioVolume) .setCustomAudioVolume(customAudioVolume) + .setLoopCustomAudio(loopCustomAudio) .build() } diff --git a/macos/Classes/src/features/render/helpers/AudioSequenceBuilder.swift b/macos/Classes/src/features/render/helpers/AudioSequenceBuilder.swift index 3626043..3c97879 100644 --- a/macos/Classes/src/features/render/helpers/AudioSequenceBuilder.swift +++ b/macos/Classes/src/features/render/helpers/AudioSequenceBuilder.swift @@ -10,6 +10,7 @@ internal class AudioSequenceBuilder { private let audioPath: String private let targetDuration: CMTime private var volume: Float = 1.0 + private var loopAudio: Bool = true /// Initializes builder with audio path and target duration. /// @@ -30,6 +31,15 @@ internal class AudioSequenceBuilder { return self } + /// Sets whether the audio should loop to match video duration. + /// + /// - Parameter loop: If true, audio repeats; if false, plays once + /// - Returns: Self for chaining + func setLoop(_ loop: Bool) -> AudioSequenceBuilder { + self.loopAudio = loop + return self + } + /// Builds custom audio track and adds it to composition. /// /// Trims or loops the audio to match target duration and applies volume. @@ -68,7 +78,7 @@ internal class AudioSequenceBuilder { let timeRange = CMTimeRange(start: .zero, duration: targetDuration) try compositionAudioTrack.insertTimeRange(timeRange, of: audioTrack, at: .zero) print("✂️ Custom audio trimmed to \(targetDuration.seconds)s") - } else { + } else if loopAudio { // Loop audio to match video duration var currentTime = CMTime.zero var loopCount = 0 @@ -84,6 +94,11 @@ internal class AudioSequenceBuilder { } print("🔄 Custom audio looped \(loopCount) times to match \(targetDuration.seconds)s duration") + } else { + // Play audio once without looping + let timeRange = CMTimeRange(start: .zero, duration: audioDuration) + try compositionAudioTrack.insertTimeRange(timeRange, of: audioTrack, at: .zero) + print("▶️ Custom audio plays once (\(audioDuration.seconds)s, no loop)") } if volume != 1.0 { diff --git a/macos/Classes/src/features/render/helpers/CompositionBuilder.swift b/macos/Classes/src/features/render/helpers/CompositionBuilder.swift index 8c4a20e..1c53d8a 100644 --- a/macos/Classes/src/features/render/helpers/CompositionBuilder.swift +++ b/macos/Classes/src/features/render/helpers/CompositionBuilder.swift @@ -14,6 +14,7 @@ internal class CompositionBuilder { private var customAudioPath: String? private var originalAudioVolume: Float = 1.0 private var customAudioVolume: Float = 1.0 + private var loopCustomAudio: Bool = true /// Initializes builder with configuration. /// @@ -61,6 +62,15 @@ internal class CompositionBuilder { return self } + /// Sets whether custom audio should loop. + /// + /// - Parameter loop: If true, audio repeats to match video duration + /// - Returns: Self for chaining + func setLoopCustomAudio(_ loop: Bool) -> CompositionBuilder { + self.loopCustomAudio = loop + return self + } + /// Builds the complete composition. /// /// - Returns: Tuple containing composition, video composition, render size, audio mix, and source track ID @@ -100,6 +110,7 @@ internal class CompositionBuilder { audioPath: customPath, targetDuration: videoResult.totalDuration ).setVolume(customAudioVolume) + .setLoop(loopCustomAudio) customAudioTrack = try await audioBuilder.build(in: composition) } diff --git a/macos/Classes/src/features/render/models/RenderConfig.swift b/macos/Classes/src/features/render/models/RenderConfig.swift index 1065a80..d0a2172 100644 --- a/macos/Classes/src/features/render/models/RenderConfig.swift +++ b/macos/Classes/src/features/render/models/RenderConfig.swift @@ -84,6 +84,11 @@ struct RenderConfig { /// When true, the image overlay is cropped together with the video. /// When false (default), the overlay is scaled to the final cropped size. let imageBytesWithCropping: Bool + + /// Whether to loop the custom audio if it is shorter than the video. + /// When true (default), audio is repeated to match video duration. + /// When false, audio plays once and silence fills the rest. + let loopCustomAudio: Bool static func fromArguments(_ arguments: [String: Any]?) -> RenderConfig? { guard let args = arguments else { return nil @@ -145,7 +150,8 @@ struct RenderConfig { startUs: (args["startUs"] as? NSNumber)?.int64Value, endUs: (args["endUs"] as? NSNumber)?.int64Value, shouldOptimizeForNetworkUse: args["shouldOptimizeForNetworkUse"] as? Bool ?? true, - imageBytesWithCropping: args["imageBytesWithCropping"] as? Bool ?? false + imageBytesWithCropping: args["imageBytesWithCropping"] as? Bool ?? false, + loopCustomAudio: args["loopCustomAudio"] as? Bool ?? true ) } } diff --git a/pubspec.yaml b/pubspec.yaml index ed5328f..56099eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: pro_video_editor description: "A Flutter video editor: Seamlessly enhance your videos with user-friendly editing features." -version: 1.6.2 +version: 1.7.0 homepage: https://github.com/hm21/pro_video_editor/ repository: https://github.com/hm21/pro_video_editor/ documentation: https://github.com/hm21/pro_video_editor/ From b30f4931d05cbd87f98fca4683e8443d9229aaa8 Mon Sep 17 00:00:00 2001 From: hm21 Date: Fri, 6 Mar 2026 17:10:46 +0100 Subject: [PATCH 2/2] chore: run build_runner --- test/pro_video_editor_method_channel_test.mocks.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/pro_video_editor_method_channel_test.mocks.dart b/test/pro_video_editor_method_channel_test.mocks.dart index 3a7e6c1..0b0ff1f 100644 --- a/test/pro_video_editor_method_channel_test.mocks.dart +++ b/test/pro_video_editor_method_channel_test.mocks.dart @@ -381,6 +381,12 @@ class MockVideoRenderData extends _i1.Mock implements _i4.VideoRenderData { returnValue: false, ) as bool); + @override + bool get loopCustomAudio => (super.noSuchMethod( + Invocation.getter(#loopCustomAudio), + returnValue: false, + ) as bool); + @override _i7.Stream<_i11.ProgressModel> get progressStream => (super.noSuchMethod( Invocation.getter(#progressStream), @@ -418,6 +424,7 @@ class MockVideoRenderData extends _i1.Mock implements _i4.VideoRenderData { double? customAudioVolume, bool? shouldOptimizeForNetworkUse, bool? imageBytesWithCropping, + bool? loopCustomAudio, }) => (super.noSuchMethod( Invocation.method( @@ -443,6 +450,7 @@ class MockVideoRenderData extends _i1.Mock implements _i4.VideoRenderData { #customAudioVolume: customAudioVolume, #shouldOptimizeForNetworkUse: shouldOptimizeForNetworkUse, #imageBytesWithCropping: imageBytesWithCropping, + #loopCustomAudio: loopCustomAudio, }, ), returnValue: _FakeVideoRenderData_2( @@ -470,6 +478,7 @@ class MockVideoRenderData extends _i1.Mock implements _i4.VideoRenderData { #customAudioVolume: customAudioVolume, #shouldOptimizeForNetworkUse: shouldOptimizeForNetworkUse, #imageBytesWithCropping: imageBytesWithCropping, + #loopCustomAudio: loopCustomAudio, }, ), ),