react-native-compressor 1.8.1 → 1.8.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.
Files changed (33) hide show
  1. package/README.md +2 -1
  2. package/android/build.gradle +5 -2
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +2 -2
  4. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +3 -3
  5. package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +11 -11
  6. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +42 -36
  7. package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +1 -1
  8. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/CompressionInterface.kt +73 -0
  9. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/VideoCompressorClass.kt +148 -0
  10. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/compressor/Compressor.kt +562 -0
  11. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/Atoms.kt +55 -0
  12. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/CompressorUtils.kt +196 -0
  13. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/NumbersUtils.kt +47 -0
  14. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/StreamableVideo.kt +209 -0
  15. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/InputSurface.kt +128 -0
  16. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/MP4Builder.kt +388 -0
  17. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mdat.kt +74 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mp4Movie.kt +54 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/OutputSurface.kt +102 -0
  20. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Result.kt +9 -0
  21. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Sample.kt +3 -0
  22. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/TextureRenderer.kt +214 -0
  23. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Track.kt +286 -0
  24. package/lib/commonjs/utils/index.js +2 -2
  25. package/lib/commonjs/utils/index.js.map +1 -1
  26. package/lib/module/utils/index.js +2 -2
  27. package/lib/module/utils/index.js.map +1 -1
  28. package/lib/typescript/index.d.ts +13 -3
  29. package/lib/typescript/index.d.ts.map +1 -1
  30. package/lib/typescript/utils/index.d.ts +16 -3
  31. package/lib/typescript/utils/index.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/utils/index.tsx +22 -8
@@ -0,0 +1,196 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.utils
2
+
3
+ import android.media.MediaCodecInfo
4
+ import android.media.MediaCodecList
5
+ import android.media.MediaExtractor
6
+ import android.media.MediaFormat
7
+ import android.media.MediaMetadataRetriever
8
+ import android.util.Log
9
+ import com.reactnativecompressor.Video.VideoCompressor.video.Mp4Movie
10
+ import java.io.File
11
+
12
+ object CompressorUtils {
13
+
14
+ // Minimum height and width for videos
15
+ private const val MIN_HEIGHT = 640.0
16
+ private const val MIN_WIDTH = 368.0
17
+
18
+ // Interval between I-frames (keyframes) in seconds
19
+ private const val I_FRAME_INTERVAL = 1
20
+
21
+ /**
22
+ * Get the width of the video from metadata or use a default value if not available.
23
+ */
24
+ fun prepareVideoWidth(
25
+ mediaMetadataRetriever: MediaMetadataRetriever,
26
+ ): Double {
27
+ val widthData =
28
+ mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
29
+ return if (widthData.isNullOrEmpty()) {
30
+ MIN_WIDTH
31
+ } else {
32
+ widthData.toDouble()
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get the height of the video from metadata or use a default value if not available.
38
+ */
39
+ fun prepareVideoHeight(
40
+ mediaMetadataRetriever: MediaMetadataRetriever,
41
+ ): Double {
42
+ val heightData =
43
+ mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
44
+ return if (heightData.isNullOrEmpty()) {
45
+ MIN_HEIGHT
46
+ } else {
47
+ heightData.toDouble()
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Set up an Mp4Movie with rotation and cache file.
53
+ */
54
+ fun setUpMP4Movie(
55
+ rotation: Int,
56
+ cacheFile: File,
57
+ ): Mp4Movie {
58
+ val movie = Mp4Movie()
59
+ movie.apply {
60
+ setCacheFile(cacheFile)
61
+ setRotation(rotation)
62
+ }
63
+ return movie
64
+ }
65
+
66
+ /**
67
+ * Set output parameters like bitrate and frame rate for video encoding.
68
+ */
69
+ fun setOutputFileParameters(
70
+ inputFormat: MediaFormat,
71
+ outputFormat: MediaFormat,
72
+ newBitrate: Int,
73
+ ) {
74
+ val newFrameRate = getFrameRate(inputFormat)
75
+ val iFrameInterval = getIFrameIntervalRate(inputFormat)
76
+ outputFormat.apply {
77
+ setInteger(
78
+ MediaFormat.KEY_COLOR_FORMAT,
79
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
80
+ )
81
+
82
+ setInteger(MediaFormat.KEY_FRAME_RATE, newFrameRate)
83
+ setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval)
84
+ // Bitrate in bits per second
85
+ setInteger(MediaFormat.KEY_BIT_RATE, newBitrate)
86
+ setInteger(
87
+ MediaFormat.KEY_BITRATE_MODE,
88
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR
89
+ )
90
+
91
+ getColorStandard(inputFormat)?.let {
92
+ setInteger(MediaFormat.KEY_COLOR_STANDARD, it)
93
+ }
94
+
95
+ getColorTransfer(inputFormat)?.let {
96
+ setInteger(MediaFormat.KEY_COLOR_TRANSFER, it)
97
+ }
98
+
99
+ getColorRange(inputFormat)?.let {
100
+ setInteger(MediaFormat.KEY_COLOR_RANGE, it)
101
+ }
102
+
103
+ Log.i(
104
+ "Output file parameters",
105
+ "videoFormat: $this"
106
+ )
107
+ }
108
+ }
109
+
110
+ // Get the frame rate from the input format or use a default value
111
+ private fun getFrameRate(format: MediaFormat): Int {
112
+ return if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) format.getInteger(MediaFormat.KEY_FRAME_RATE)
113
+ else 30
114
+ }
115
+
116
+ // Get the I-frame (keyframe) interval from the input format or use a default value
117
+ private fun getIFrameIntervalRate(format: MediaFormat): Int {
118
+ return if (format.containsKey(MediaFormat.KEY_I_FRAME_INTERVAL)) format.getInteger(
119
+ MediaFormat.KEY_I_FRAME_INTERVAL
120
+ )
121
+ else I_FRAME_INTERVAL
122
+ }
123
+
124
+ // Get the color standard from the input format or null if not available
125
+ private fun getColorStandard(format: MediaFormat): Int? {
126
+ return if (format.containsKey(MediaFormat.KEY_COLOR_STANDARD)) format.getInteger(
127
+ MediaFormat.KEY_COLOR_STANDARD
128
+ )
129
+ else null
130
+ }
131
+
132
+ // Get the color transfer from the input format or null if not available
133
+ private fun getColorTransfer(format: MediaFormat): Int? {
134
+ return if (format.containsKey(MediaFormat.KEY_COLOR_TRANSFER)) format.getInteger(
135
+ MediaFormat.KEY_COLOR_TRANSFER
136
+ )
137
+ else null
138
+ }
139
+
140
+ // Get the color range from the input format or null if not available
141
+ private fun getColorRange(format: MediaFormat): Int? {
142
+ return if (format.containsKey(MediaFormat.KEY_COLOR_RANGE)) format.getInteger(
143
+ MediaFormat.KEY_COLOR_RANGE
144
+ )
145
+ else null
146
+ }
147
+
148
+ /**
149
+ * Find the track index for video or audio in the media extractor.
150
+ *
151
+ * @param extractor MediaExtractor used to extract data from the media source.
152
+ * @param isVideo Determines whether we are looking for a video or audio track.
153
+ * @return Index of the requested track, or -5 if not found.
154
+ */
155
+ fun findTrack(
156
+ extractor: MediaExtractor,
157
+ isVideo: Boolean,
158
+ ): Int {
159
+ val numTracks = extractor.trackCount
160
+ for (i in 0 until numTracks) {
161
+ val format = extractor.getTrackFormat(i)
162
+ val mime = format.getString(MediaFormat.KEY_MIME)
163
+ if (isVideo) {
164
+ if (mime?.startsWith("video/")!!) return i
165
+ } else {
166
+ if (mime?.startsWith("audio/")!!) return i
167
+ }
168
+ }
169
+ return -5
170
+ }
171
+
172
+ /**
173
+ * Log an exception with a meaningful message.
174
+ */
175
+ fun printException(exception: Exception) {
176
+ var message = "An error has occurred!"
177
+ exception.localizedMessage?.let {
178
+ message = it
179
+ }
180
+ Log.e("Compressor", message, exception)
181
+ }
182
+
183
+ /**
184
+ * Check if the device has QTI (Qualcomm Technologies, Inc.) codecs.
185
+ */
186
+ fun hasQTI(): Boolean {
187
+ val list = MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos
188
+ for (codec in list) {
189
+ Log.i("CODECS: ", codec.name)
190
+ if (codec.name.contains("qti.avc")) {
191
+ return true
192
+ }
193
+ }
194
+ return false
195
+ }
196
+ }
@@ -0,0 +1,47 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.utils
2
+
3
+ /**
4
+ * Converts a 32-bit unsigned integer to a long.
5
+ * @param int32 The 32-bit unsigned integer to convert.
6
+ * @return The equivalent long value.
7
+ */
8
+ fun uInt32ToLong(int32: Int): Long {
9
+ return int32.toLong()
10
+ }
11
+
12
+ /**
13
+ * Converts a long to a 32-bit unsigned integer, throwing an exception if the value is too large.
14
+ * @param uInt32 The long value to convert.
15
+ * @return The equivalent 32-bit unsigned integer.
16
+ * @throws Exception if the provided long value is greater than Int.MAX_VALUE or negative.
17
+ */
18
+ fun uInt32ToInt(uInt32: Long): Int {
19
+ if (uInt32 > Int.MAX_VALUE || uInt32 < 0) {
20
+ throw Exception("uInt32 value is too large or negative")
21
+ }
22
+ return uInt32.toInt()
23
+ }
24
+
25
+ /**
26
+ * Converts a 64-bit unsigned integer to a long, throwing an exception if the value is negative.
27
+ * @param uInt64 The 64-bit unsigned integer to convert.
28
+ * @return The equivalent long value.
29
+ * @throws Exception if the provided long value is negative.
30
+ */
31
+ fun uInt64ToLong(uInt64: Long): Long {
32
+ if (uInt64 < 0) throw Exception("uInt64 value is negative")
33
+ return uInt64
34
+ }
35
+
36
+ /**
37
+ * Converts a 32-bit unsigned integer to an integer, throwing an exception if the value is negative.
38
+ * @param uInt32 The 32-bit unsigned integer to convert.
39
+ * @return The equivalent integer value.
40
+ * @throws Exception if the provided integer value is negative.
41
+ */
42
+ fun uInt32ToInt(uInt32: Int): Int {
43
+ if (uInt32 < 0) {
44
+ throw Exception("uInt32 value is negative")
45
+ }
46
+ return uInt32
47
+ }
@@ -0,0 +1,209 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.utils
2
+
3
+ import android.util.Log
4
+ import com.reactnativecompressor.Video.VideoCompressor.data.*
5
+ import java.io.*
6
+ import java.nio.ByteBuffer
7
+ import java.nio.ByteOrder
8
+ import java.nio.channels.FileChannel
9
+
10
+ object StreamableVideo {
11
+
12
+ private const val tag = "StreamableVideo"
13
+ private const val ATOM_PREAMBLE_SIZE = 8
14
+
15
+ /**
16
+ * Starts the process of making a video file "fast start" and saves it to the output file.
17
+ *
18
+ * @param inputVideoFile The input video file.
19
+ * @param outputVideoFile The output video file.
20
+ * @return `true` if the operation is successful, `false` if the input file is already fast start.
21
+ * @throws IOException if an I/O error occurs.
22
+ */
23
+ @Throws(IOException::class)
24
+ fun start(`in`: File?, out: File): Boolean {
25
+ var ret = false
26
+ var inStream: FileInputStream? = null
27
+ var outStream: FileOutputStream? = null
28
+ return try {
29
+ inStream = FileInputStream(`in`)
30
+ val infile = inStream.channel
31
+ outStream = FileOutputStream(out)
32
+ val outfile = outStream.channel
33
+ convert(infile, outfile).also { ret = it }
34
+ } finally {
35
+ safeClose(inStream)
36
+ safeClose(outStream)
37
+ if (!ret) {
38
+ out.delete()
39
+ }
40
+ }
41
+ }
42
+
43
+ @Throws(IOException::class)
44
+ private fun convert(inputFileChannel: FileChannel, outputFileChannel: FileChannel): Boolean {
45
+ val atomBytes = ByteBuffer.allocate(ATOM_PREAMBLE_SIZE).order(ByteOrder.BIG_ENDIAN)
46
+ var atomType = 0
47
+ var atomSize: Long = 0
48
+ val lastOffset: Long
49
+ val moovAtom: ByteBuffer
50
+ var ftypAtom: ByteBuffer? = null
51
+ var startOffset: Long = 0
52
+
53
+ // Traverse through the atoms in the file to ensure that 'moov' is at the end.
54
+ while (readAndFill(inputFileChannel, atomBytes)) {
55
+ atomSize = uInt32ToLong(atomBytes.int)
56
+ atomType = atomBytes.int
57
+
58
+ // Keep the 'ftyp' atom.
59
+ if (atomType == FTYP_ATOM) {
60
+ val ftypAtomSize = uInt32ToInt(atomSize)
61
+ ftypAtom = ByteBuffer.allocate(ftypAtomSize).order(ByteOrder.BIG_ENDIAN)
62
+ atomBytes.rewind()
63
+ ftypAtom.put(atomBytes)
64
+ if (inputFileChannel.read(ftypAtom) < ftypAtomSize - ATOM_PREAMBLE_SIZE) break
65
+ ftypAtom.flip()
66
+ startOffset = inputFileChannel.position() // After 'ftyp' atom.
67
+ } else {
68
+ if (atomSize == 1L) {
69
+ /* 64-bit special case */
70
+ atomBytes.clear()
71
+ if (!readAndFill(inputFileChannel, atomBytes)) break
72
+ atomSize = uInt64ToLong(atomBytes.long)
73
+ inputFileChannel.position(inputFileChannel.position() + atomSize - ATOM_PREAMBLE_SIZE * 2) // Seek.
74
+ } else {
75
+ inputFileChannel.position(inputFileChannel.position() + atomSize - ATOM_PREAMBLE_SIZE) // Seek.
76
+ }
77
+ }
78
+ if (atomType != FREE_ATOM
79
+ && atomType != JUNK_ATOM
80
+ && atomType != MDAT_ATOM
81
+ && atomType != MOOV_ATOM
82
+ && atomType != PNOT_ATOM
83
+ && atomType != SKIP_ATOM
84
+ && atomType != WIDE_ATOM
85
+ && atomType != PICT_ATOM
86
+ && atomType != UUID_ATOM
87
+ && atomType != FTYP_ATOM
88
+ ) {
89
+ Log.wtf(tag, "Encountered a non-QT top-level atom (Is this a QuickTime file?)")
90
+ break
91
+ }
92
+
93
+ /* The atom header is 8 (or 16 bytes). If the atom size (which
94
+ * includes these 8 or 16 bytes) is less than that, we won't be
95
+ * able to continue scanning sensibly after this atom, so break. */
96
+ if (atomSize < 8) break
97
+ }
98
+ if (atomType != MOOV_ATOM) {
99
+ Log.wtf(tag, "The last atom in the file was not a 'moov' atom")
100
+ return false
101
+ }
102
+
103
+ // 'atomSize' is 'uint64', but for 'moov', 'uint32' should be stored.
104
+ val moovAtomSize: Int = uInt32ToInt(atomSize)
105
+ lastOffset =
106
+ inputFileChannel.size() - moovAtomSize
107
+ moovAtom = ByteBuffer.allocate(moovAtomSize).order(ByteOrder.BIG_ENDIAN)
108
+ if (!readAndFill(inputFileChannel, moovAtom, lastOffset)) {
109
+ throw Exception("Failed to read 'moov' atom")
110
+ }
111
+
112
+ if (moovAtom.getInt(12) == CMOV_ATOM) {
113
+ throw Exception("This utility does not support compressed 'moov' atoms yet")
114
+ }
115
+
116
+ // Crawl through the 'moov' chunk in search of 'stco' or 'co64' atoms.
117
+ while (moovAtom.remaining() >= 8) {
118
+ val atomHead = moovAtom.position()
119
+ atomType = moovAtom.getInt(atomHead + 4)
120
+ if (!(atomType == STCO_ATOM || atomType == CO64_ATOM)) {
121
+ moovAtom.position(moovAtom.position() + 1)
122
+ continue
123
+ }
124
+ atomSize = uInt32ToLong(moovAtom.getInt(atomHead)) // 'uint32'
125
+ if (atomSize > moovAtom.remaining()) {
126
+ throw Exception("Bad atom size")
127
+ }
128
+ // Skip size (4 bytes), type (4 bytes), version (1 byte), and flags (3 bytes).
129
+ moovAtom.position(atomHead + 12)
130
+ if (moovAtom.remaining() < 4) {
131
+ throw Exception("Malformed atom")
132
+ }
133
+ // 'uint32_t', but assuming 'moovAtomSize' is in 'int32' range, so this will be in 'int32' range.
134
+ val offsetCount = uInt32ToInt(moovAtom.int)
135
+ if (atomType == STCO_ATOM) {
136
+ Log.i(tag, "Patching 'stco' atom...")
137
+ if (moovAtom.remaining() < offsetCount * 4) {
138
+ throw Exception("Bad atom size/element count")
139
+ }
140
+ for (i in 0 until offsetCount) {
141
+ val currentOffset = moovAtom.getInt(moovAtom.position())
142
+ val newOffset =
143
+ currentOffset + moovAtomSize // Calculate 'uint32' in 'int', bitwise addition.
144
+
145
+ if (currentOffset < 0 && newOffset >= 0) {
146
+ throw Exception(
147
+ "This is a bug in the original 'qt-faststart.c': " +
148
+ "'stco' atom should be extended to 'co64' atom as the new offset value overflows 'uint32', " +
149
+ "but it is not implemented."
150
+ )
151
+ }
152
+ moovAtom.putInt(newOffset)
153
+ }
154
+ } else if (atomType == CO64_ATOM) {
155
+ Log.wtf(tag, "Patching 'co64' atom...")
156
+ if (moovAtom.remaining() < offsetCount * 8) {
157
+ throw Exception("Bad atom size/element count")
158
+ }
159
+ for (i in 0 until offsetCount) {
160
+ val currentOffset = moovAtom.getLong(moovAtom.position())
161
+ moovAtom.putLong(currentOffset + moovAtomSize) // Calculate 'uint64' in 'long', bitwise addition.
162
+ }
163
+ }
164
+ }
165
+ inputFileChannel.position(startOffset) // Seek after 'ftyp' atom.
166
+ if (ftypAtom != null) {
167
+ // Dump the same 'ftyp' atom.
168
+ Log.i(tag, "Writing 'ftyp' atom...")
169
+ ftypAtom.rewind()
170
+ outputFileChannel.write(ftypAtom)
171
+ }
172
+
173
+ // Dump the new 'moov' atom.
174
+ Log.i(tag, "Writing 'moov' atom...")
175
+ moovAtom.rewind()
176
+ outputFileChannel.write(moovAtom)
177
+
178
+ // Copy the remainder of the input file, from offset 0 -> (lastOffset - startOffset) - 1.
179
+ Log.i(tag, "Copying the rest of the file...")
180
+ inputFileChannel.transferTo(startOffset, lastOffset - startOffset, outputFileChannel)
181
+ return true
182
+ }
183
+
184
+ private fun safeClose(closeable: Closeable?) {
185
+ if (closeable != null) {
186
+ try {
187
+ closeable.close()
188
+ } catch (e: IOException) {
189
+ Log.wtf(tag, "Failed to close file: ")
190
+ }
191
+ }
192
+ }
193
+
194
+ @Throws(IOException::class)
195
+ private fun readAndFill(inputFileChannel: FileChannel, buffer: ByteBuffer): Boolean {
196
+ buffer.clear()
197
+ val size = inputFileChannel.read(buffer)
198
+ buffer.flip()
199
+ return size == buffer.capacity()
200
+ }
201
+
202
+ @Throws(IOException::class)
203
+ private fun readAndFill(inputFileChannel: FileChannel, buffer: ByteBuffer, position: Long): Boolean {
204
+ buffer.clear()
205
+ val size = inputFileChannel.read(buffer, position)
206
+ buffer.flip()
207
+ return size == buffer.capacity()
208
+ }
209
+ }
@@ -0,0 +1,128 @@
1
+ package com.reactnativecompressor.Video.VideoCompressor.video
2
+
3
+ import android.opengl.EGL14
4
+ import android.opengl.EGLConfig
5
+ import android.opengl.EGLContext
6
+ import android.opengl.EGLDisplay
7
+ import android.opengl.EGLExt
8
+ import android.opengl.EGLSurface
9
+ import android.view.Surface
10
+
11
+ class InputSurface(surface: Surface?) {
12
+
13
+ private val eglRecordableAndroid = 0x3142
14
+ private val eglOpenGlES2Bit = 4
15
+ private var mEGLDisplay: EGLDisplay? = null
16
+ private var mEGLContext: EGLContext? = null
17
+ private var mEGLSurface: EGLSurface? = null
18
+ private var mSurface: Surface? = null
19
+
20
+ init {
21
+ if (surface == null) {
22
+ throw NullPointerException()
23
+ }
24
+ mSurface = surface
25
+ eglSetup()
26
+ }
27
+
28
+ private fun eglSetup() {
29
+ mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
30
+ if (mEGLDisplay === EGL14.EGL_NO_DISPLAY) {
31
+ throw RuntimeException("unable to get EGL14 display")
32
+ }
33
+ val version = IntArray(2)
34
+ if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
35
+ mEGLDisplay = null
36
+ throw RuntimeException("unable to initialize EGL14")
37
+ }
38
+ val attribList = intArrayOf(
39
+ EGL14.EGL_RED_SIZE,
40
+ 8,
41
+ EGL14.EGL_GREEN_SIZE,
42
+ 8,
43
+ EGL14.EGL_BLUE_SIZE,
44
+ 8,
45
+ EGL14.EGL_RENDERABLE_TYPE,
46
+ eglOpenGlES2Bit,
47
+ eglRecordableAndroid,
48
+ 1,
49
+ EGL14.EGL_NONE,
50
+ )
51
+ val configs = arrayOfNulls<EGLConfig>(1)
52
+ val numConfigs = IntArray(1)
53
+ if (!EGL14.eglChooseConfig(
54
+ mEGLDisplay, attribList, 0, configs, 0, configs.size,
55
+ numConfigs, 0
56
+ )
57
+ ) {
58
+ throw RuntimeException("unable to find RGB888+recordable ES2 EGL config")
59
+ }
60
+ val attrs = intArrayOf(
61
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
62
+ EGL14.EGL_NONE
63
+ )
64
+ mEGLContext =
65
+ EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, attrs, 0)
66
+ checkEglError()
67
+ if (mEGLContext == null) {
68
+ throw RuntimeException("null context")
69
+ }
70
+ val surfaceAttrs = intArrayOf(
71
+ EGL14.EGL_NONE
72
+ )
73
+ mEGLSurface = EGL14.eglCreateWindowSurface(
74
+ mEGLDisplay, configs[0], mSurface,
75
+ surfaceAttrs, 0
76
+ )
77
+ checkEglError()
78
+ if (mEGLSurface == null) {
79
+ throw RuntimeException("surface was null")
80
+ }
81
+ }
82
+
83
+ fun release() {
84
+ if (EGL14.eglGetCurrentContext() == mEGLContext) {
85
+ EGL14.eglMakeCurrent(
86
+ mEGLDisplay,
87
+ EGL14.EGL_NO_SURFACE,
88
+ EGL14.EGL_NO_SURFACE,
89
+ EGL14.EGL_NO_CONTEXT
90
+ )
91
+ }
92
+ EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface)
93
+ EGL14.eglDestroyContext(mEGLDisplay, mEGLContext)
94
+
95
+ mSurface?.release()
96
+
97
+ mEGLDisplay = null
98
+ mEGLContext = null
99
+ mEGLSurface = null
100
+
101
+ mSurface = null
102
+ }
103
+
104
+ fun makeCurrent() {
105
+ if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
106
+ throw RuntimeException("eglMakeCurrent failed")
107
+ }
108
+ }
109
+
110
+ fun swapBuffers(): Boolean =
111
+ EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface)
112
+
113
+
114
+ fun setPresentationTime(nsecs: Long) {
115
+ EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs)
116
+ }
117
+
118
+ private fun checkEglError() {
119
+ var failed = false
120
+ while (EGL14.eglGetError() != EGL14.EGL_SUCCESS) {
121
+ failed = true
122
+ }
123
+ if (failed) {
124
+ throw RuntimeException("EGL error encountered (see log)")
125
+ }
126
+ }
127
+
128
+ }