-
Notifications
You must be signed in to change notification settings - Fork 181
Expand file tree
/
Copy pathIssuesTests.kt
More file actions
242 lines (227 loc) · 10.9 KB
/
IssuesTests.kt
File metadata and controls
242 lines (227 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package com.otaliastudios.transcoder.integration
import android.media.MediaFormat
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
import android.media.MediaMetadataRetriever
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.otaliastudios.transcoder.Transcoder
import com.otaliastudios.transcoder.TranscoderListener
import com.otaliastudios.transcoder.TranscoderOptions
import com.otaliastudios.transcoder.common.TrackType
import com.otaliastudios.transcoder.internal.utils.Logger
import com.otaliastudios.transcoder.resize.AspectRatioResizer
import com.otaliastudios.transcoder.source.AssetFileDescriptorDataSource
import com.otaliastudios.transcoder.source.BlankAudioDataSource
import com.otaliastudios.transcoder.source.ClipDataSource
import com.otaliastudios.transcoder.source.FileDescriptorDataSource
import com.otaliastudios.transcoder.source.FilePathDataSource
import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy
import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy
import com.otaliastudios.transcoder.validator.WriteAlwaysValidator
import org.junit.Assume
import org.junit.AssumptionViolatedException
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import kotlin.math.roundToInt
@RunWith(AndroidJUnit4::class)
class IssuesTests {
class Helper(val issue: Int) {
val log = Logger("Issue$issue")
val context = InstrumentationRegistry.getInstrumentation().context
fun output(
name: String = System.currentTimeMillis().toString(),
extension: String = "mp4"
) = File(context.cacheDir, "$name.$extension").also { it.parentFile!!.mkdirs() }
fun input(filename: String) = AssetFileDescriptorDataSource(
context.assets.openFd("issue_$issue/$filename")
)
fun transcode(
output: File = output(),
assertTranscoded: Boolean = true,
assertDuration: Boolean = true,
builder: TranscoderOptions.Builder.() -> Unit,
): File = runCatching {
Logger.setLogLevel(Logger.LEVEL_VERBOSE)
val transcoder = Transcoder.into(output.absolutePath)
transcoder.apply(builder)
transcoder.setListener(object : TranscoderListener {
override fun onTranscodeCanceled() = Unit
override fun onTranscodeCompleted(successCode: Int) {
if (assertTranscoded) {
require(successCode == Transcoder.SUCCESS_TRANSCODED)
}
}
override fun onTranscodeFailed(exception: Throwable) = Unit
override fun onTranscodeProgress(progress: Double) = Unit
})
transcoder.transcode().get()
if (assertDuration) {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(output.absolutePath)
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)!!.toLong()
log.e("Video duration is $duration")
assert(duration > 0L)
retriever.release()
}
return output
}.getOrElse {
if (it.toString().contains("c2.android.avc.encoder was unable to create the input surface (1x1)")) {
log.w("Hit known emulator bug. Skipping the test.")
throw AssumptionViolatedException("Hit known emulator bug.")
}
throw it
}
fun getRotation(file:File) : Int {
val outputDataSource = FilePathDataSource(file.absolutePath)
var displayRotation = 0
try{
outputDataSource.initialize()
val mediaFormat = outputDataSource.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
displayRotation = mediaFormat.getInteger(MediaFormat.KEY_ROTATION)
}catch (_:Exception){
}finally {
outputDataSource.deinitialize()
}
return displayRotation
}
fun getRotation(fd:AssetFileDescriptorDataSource) : Int {
var displayRotation = 0
try{
fd.initialize()
val mediaFormat = fd.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
displayRotation = mediaFormat.getInteger(MediaFormat.KEY_ROTATION)
}catch (_:Exception){
}finally {
fd.deinitialize()
}
return displayRotation
}
fun getFrameSize(fd:AssetFileDescriptorDataSource): Pair<Int, Int>{
var outputWidth = 0
var outputHeight = 0
try{
fd.initialize()
val mediaFormat = fd.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
outputWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
outputHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
}catch (ex:Exception){
throw Exception("Get video size\n${ex.localizedMessage}")
}finally {
fd.deinitialize()
}
return Pair(outputWidth, outputHeight)
}
fun getFrameSize(file:File) : Pair<Int, Int> {
val outputDataSource = FilePathDataSource(file.absolutePath)
var outputWidth = 0
var outputHeight = 0
try{
outputDataSource.initialize()
val mediaFormat = outputDataSource.getTrackFormat(TrackType.VIDEO) ?: throw NullPointerException("MediaFormat is null")
outputWidth = mediaFormat.getInteger(MediaFormat.KEY_WIDTH)
outputHeight = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)
}catch (ex:Exception){
throw Exception("Get video size\n${ex.localizedMessage}")
}finally {
outputDataSource.deinitialize()
}
return Pair(outputWidth, outputHeight)
}
}
@Test(timeout = 16000)
fun issue137() = with(Helper(137)) {
transcode {
addDataSource(ClipDataSource(input("main.mp3"), 0L, 1000_000L))
addDataSource(input("0.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 2000_000L, 3000_000L))
addDataSource(input("1.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 4000_000L, 5000_000L))
addDataSource(input("2.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 6000_000L, 7000_000L))
addDataSource(input("3.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 8000_000L, 9000_000L))
addDataSource(input("4.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 10000_000L, 11000_000L))
addDataSource(input("5.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 12000_000L, 13000_000L))
addDataSource(input("6.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 14000_000L, 15000_000L))
addDataSource(input("7.amr"))
addDataSource(ClipDataSource(input("main.mp3"), 16000_000L, 17000_000L))
addDataSource(input("8.amr"))
}
Unit
}
@Test(timeout = 16000)
fun issue184() = with(Helper(184)) {
transcode {
addDataSource(TrackType.VIDEO, input("transcode.3gp"))
setVideoTrackStrategy(DefaultVideoStrategy.exact(400, 400).build())
}
Unit
}
@Test(timeout = 16000)
fun issue102() = with(Helper(102)) {
transcode {
addDataSource(input("sample.mp4"))
setValidator(WriteAlwaysValidator())
}
Unit
}
@Test(timeout = 16000)
fun issue180() = with(Helper(180)) {
transcode {
val vds = input("party.mp4")
val duration = run {
vds.initialize()
vds.durationUs.also { vds.deinitialize() }
}
check(duration > 0L) { "Invalid duration: $duration" }
addDataSource(TrackType.VIDEO, vds)
addDataSource(TrackType.AUDIO, BlankAudioDataSource(duration))
}
Unit
}
@Test(timeout = 16000)
fun issue75_workaround() = with(Helper(75)) {
transcode {
val vds = input("bbb_720p_30mb.mp4")
addDataSource(ClipDataSource(vds, 0, 500_000))
setVideoTrackStrategy(DefaultVideoStrategy.exact(300, 300).build())
// API 23:
// This video seems to have wrong number of channels in metadata (6) wrt MediaCodec decoder output (2)
// so when using DefaultAudioStrategy.CHANNELS_AS_INPUT we target 6 and try to upscale from 2 to 6, which fails
// The workaround below explicitly sets a number of channels different than CHANNELS_AS_INPUT to make it work
// setAudioTrackStrategy(DefaultAudioStrategy.builder().channels(1).build())
}
Unit
}
@Test
fun issue215() = with(Helper(215)){
//Display rotation 0, 90, 180, 270
val rotations = listOf(0,270,180,90)
val videoNames = listOf("bbb_720p_1sec.mp4", "bbb_720p_1sec_r90.mp4", "bbb_720p_1sec_r180.mp4", "bbb_720p_1sec_r270.mp4")
videoNames.forEachIndexed { i, videoName ->
val inputVideoSize = getFrameSize(input(videoName))
val inputRatio = inputVideoSize.first.toFloat() / inputVideoSize.second.toFloat()
check(inputRatio > 1f){"Input video ($videoName) ratio is: $inputRatio but expected: greater then 1"}
val inputRotation = getRotation(input(videoName))
check(inputRotation == rotations[i]) {"Input video ($videoName) display rotation is: $inputRotation but expected: ${rotations[i]}"}
val expectedOutputRatio = 0.5f
val outputVideo = transcode {
val vds = input(videoName)
addDataSource(vds)
val videoTrackStrategy = DefaultVideoStrategy.Builder()
videoTrackStrategy.addResizer(AspectRatioResizer(expectedOutputRatio))
setVideoTrackStrategy(videoTrackStrategy.build())
}
val outputRotation = getRotation(outputVideo)
val outputVideoSize = getFrameSize(outputVideo)
val outputRatio = ((outputVideoSize.first.toFloat() / outputVideoSize.second.toFloat()) * 10).roundToInt() / 10f
check(outputRatio == expectedOutputRatio) {"Output ratio is: $outputRatio but expects: $expectedOutputRatio (input video[$i]: $videoName)"}
check(outputRotation == 0) {"Output video display rotation is: $outputRotation but expected: 0 (input video[$i]: $videoName)"}
println("$videoName (rotation: $outputRotation expected: 0), inputRatio: $inputRatio (${inputVideoSize.first}x${inputVideoSize.second}) expectedOutputRatio: $expectedOutputRatio (${outputVideoSize.first}x${outputVideoSize.second}) OK")
}
}
}