react-native-compressor 1.19.0 → 1.19.2
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.
|
@@ -788,32 +788,21 @@ object Compressor {
|
|
|
788
788
|
inputFormat: MediaFormat,
|
|
789
789
|
outputSurface: OutputSurface,
|
|
790
790
|
): MediaCodec {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
//
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
|
|
799
|
-
inputFormat.
|
|
800
|
-
MediaFormat.
|
|
801
|
-
} else {
|
|
802
|
-
originalMime
|
|
791
|
+
// Some inputs (e.g. iPhone .MOV files) report a "video/dolby-vision" MIME
|
|
792
|
+
// type that many devices cannot decode. Remap to a decodable base-layer
|
|
793
|
+
// codec, or fail with a clear error, before creating the decoder (#398).
|
|
794
|
+
ensureDecodableVideoFormat(inputFormat)
|
|
795
|
+
|
|
796
|
+
// Clear Dolby Vision specific profile and level to prevent configuration failures
|
|
797
|
+
// when the MIME type has been remapped to AVC/HEVC.
|
|
798
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
799
|
+
inputFormat.removeKey(MediaFormat.KEY_PROFILE)
|
|
800
|
+
inputFormat.removeKey(MediaFormat.KEY_LEVEL)
|
|
803
801
|
}
|
|
804
802
|
|
|
805
|
-
val decoder = MediaCodec.createDecoderByType(
|
|
806
|
-
|
|
807
|
-
try {
|
|
808
|
-
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0)
|
|
809
|
-
} catch (e: Exception) {
|
|
810
|
-
// A codec that throws from configure() is unusable; release it so a
|
|
811
|
-
// configure failure here doesn't leak the decoder handle.
|
|
812
|
-
runCatching { decoder.release() }
|
|
813
|
-
throw e
|
|
814
|
-
}
|
|
803
|
+
val decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)!!)
|
|
815
804
|
|
|
816
|
-
|
|
805
|
+
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0)
|
|
817
806
|
|
|
818
807
|
return decoder
|
|
819
808
|
}
|
|
@@ -13,7 +13,9 @@ public enum NextLevelSessionExporterError: Error, CustomStringConvertible {
|
|
|
13
13
|
case readingFailure
|
|
14
14
|
case writingFailure
|
|
15
15
|
case cancelled
|
|
16
|
-
|
|
16
|
+
case unsupportedVideoOutputConfiguration
|
|
17
|
+
case missingVideoTrackInOutput
|
|
18
|
+
|
|
17
19
|
public var description: String {
|
|
18
20
|
get {
|
|
19
21
|
switch self {
|
|
@@ -25,6 +27,10 @@ public enum NextLevelSessionExporterError: Error, CustomStringConvertible {
|
|
|
25
27
|
return "Writing failure"
|
|
26
28
|
case .cancelled:
|
|
27
29
|
return "Cancelled"
|
|
30
|
+
case .unsupportedVideoOutputConfiguration:
|
|
31
|
+
return "The writer rejected the video output configuration"
|
|
32
|
+
case .missingVideoTrackInOutput:
|
|
33
|
+
return "Export finished without a video track in the output"
|
|
28
34
|
}
|
|
29
35
|
}
|
|
30
36
|
}
|
|
@@ -249,7 +255,16 @@ extension NextLevelSessionExporter {
|
|
|
249
255
|
}
|
|
250
256
|
}
|
|
251
257
|
|
|
252
|
-
|
|
258
|
+
// Fail loudly when the writer rejects the video output configuration.
|
|
259
|
+
// Continuing here used to export only the audio track and still resolve
|
|
260
|
+
// as a success (issue #400).
|
|
261
|
+
guard self.setupVideoOutput(withAsset: asset) else {
|
|
262
|
+
DispatchQueue.main.async {
|
|
263
|
+
self._completionHandler?(.failure(NextLevelSessionExporterError.unsupportedVideoOutputConfiguration))
|
|
264
|
+
self._completionHandler = nil
|
|
265
|
+
}
|
|
266
|
+
return
|
|
267
|
+
}
|
|
253
268
|
if !self.stripAudio {
|
|
254
269
|
self.setupAudioOutput(withAsset: asset)
|
|
255
270
|
self.setupAudioInput()
|
|
@@ -316,11 +331,14 @@ extension NextLevelSessionExporter {
|
|
|
316
331
|
|
|
317
332
|
extension NextLevelSessionExporter {
|
|
318
333
|
|
|
319
|
-
|
|
334
|
+
/// Returns `false` when the asset has video tracks but a video writer input
|
|
335
|
+
/// could not be created — exporting would silently produce an audio-only
|
|
336
|
+
/// file (see numandev1/react-native-compressor#400).
|
|
337
|
+
private func setupVideoOutput(withAsset asset: AVAsset) -> Bool {
|
|
320
338
|
let videoTracks = asset.tracks(withMediaType: AVMediaType.video)
|
|
321
|
-
|
|
339
|
+
|
|
322
340
|
guard videoTracks.count > 0 else {
|
|
323
|
-
return
|
|
341
|
+
return true
|
|
324
342
|
}
|
|
325
343
|
|
|
326
344
|
self._videoOutput = AVAssetReaderVideoCompositionOutput(videoTracks: videoTracks, videoSettings: self.videoInputConfiguration)
|
|
@@ -344,8 +362,8 @@ extension NextLevelSessionExporter {
|
|
|
344
362
|
self._videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: self.videoOutputConfiguration)
|
|
345
363
|
self._videoInput?.expectsMediaDataInRealTime = self.expectsMediaDataInRealTime
|
|
346
364
|
} else {
|
|
347
|
-
print("
|
|
348
|
-
return
|
|
365
|
+
print("NextLevelSessionExporter, unsupported video output configuration, failing export instead of writing an audio-only file")
|
|
366
|
+
return false
|
|
349
367
|
}
|
|
350
368
|
|
|
351
369
|
if let writer = self._writer,
|
|
@@ -367,8 +385,9 @@ extension NextLevelSessionExporter {
|
|
|
367
385
|
|
|
368
386
|
self._pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoInput, sourcePixelBufferAttributes: pixelBufferAttrib)
|
|
369
387
|
}
|
|
388
|
+
return true
|
|
370
389
|
}
|
|
371
|
-
|
|
390
|
+
|
|
372
391
|
private func setupAudioOutput(withAsset asset: AVAsset) {
|
|
373
392
|
let audioTracks = asset.tracks(withMediaType: AVMediaType.audio)
|
|
374
393
|
var audioTracksToUse: [AVAssetTrack] = []
|
|
@@ -632,6 +651,22 @@ extension NextLevelSessionExporter {
|
|
|
632
651
|
break
|
|
633
652
|
}
|
|
634
653
|
|
|
654
|
+
// Guard against silently returning an audio-only file: when the source
|
|
655
|
+
// has a video track and a video output was configured, the exported file
|
|
656
|
+
// must contain a video track as well. A configuration the encoder rejects
|
|
657
|
+
// at write time can still end with `.completed` while the video track is
|
|
658
|
+
// dropped (issue #400) — surface that as an error instead of a success.
|
|
659
|
+
if self.videoOutputConfiguration != nil,
|
|
660
|
+
let asset = self.asset,
|
|
661
|
+
let outputURL = self.outputURL,
|
|
662
|
+
asset.tracks(withMediaType: AVMediaType.video).count > 0,
|
|
663
|
+
AVAsset(url: outputURL).tracks(withMediaType: AVMediaType.video).count == 0 {
|
|
664
|
+
try? FileManager.default.removeItem(at: outputURL)
|
|
665
|
+
self._completionHandler?(.failure(NextLevelSessionExporterError.missingVideoTrackInOutput))
|
|
666
|
+
self._completionHandler = nil
|
|
667
|
+
return
|
|
668
|
+
}
|
|
669
|
+
|
|
635
670
|
self._completionHandler?(.success(self.status))
|
|
636
671
|
self._completionHandler = nil
|
|
637
672
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-compressor",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.2",
|
|
4
4
|
"description": "Compress Image, Video, and Audio same like Whatsapp & Auto/Manual Compression | Background Upload | Download File | Create Video Thumbnail",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|