@@ -3,6 +3,7 @@ package com.otaliastudios.transcoder.internal.codec
3
3
import android.media.MediaCodec.*
4
4
import android.media.MediaFormat
5
5
import android.view.Surface
6
+ import com.otaliastudios.transcoder.common.trackType
6
7
import com.otaliastudios.transcoder.internal.data.ReaderChannel
7
8
import com.otaliastudios.transcoder.internal.data.ReaderData
8
9
import com.otaliastudios.transcoder.internal.media.MediaCodecBuffers
@@ -11,7 +12,11 @@ import com.otaliastudios.transcoder.internal.pipeline.Channel
11
12
import com.otaliastudios.transcoder.internal.pipeline.QueuedStep
12
13
import com.otaliastudios.transcoder.internal.pipeline.State
13
14
import com.otaliastudios.transcoder.internal.utils.Logger
15
+ import com.otaliastudios.transcoder.internal.utils.trackMapOf
14
16
import java.nio.ByteBuffer
17
+ import java.util.concurrent.atomic.AtomicInteger
18
+ import kotlin.properties.Delegates
19
+ import kotlin.properties.Delegates.observable
15
20
16
21
17
22
internal open class DecoderData (
@@ -30,32 +35,52 @@ internal class Decoder(
30
35
continuous : Boolean , // relevant if the source sends no-render chunks. should we compensate or not?
31
36
) : QueuedStep<ReaderData, ReaderChannel, DecoderData, DecoderChannel>(), ReaderChannel {
32
37
33
- private val log = Logger (" Decoder" )
38
+ companion object {
39
+ private val ID = trackMapOf(AtomicInteger (0 ), AtomicInteger (0 ))
40
+ }
41
+
42
+ private val log = Logger (" Decoder(${format.trackType} ,${ID [format.trackType].getAndIncrement()} )" )
34
43
override val channel = this
44
+
35
45
private val codec = createDecoderByType(format.getString(MediaFormat .KEY_MIME )!! )
36
46
private val buffers by lazy { MediaCodecBuffers (codec) }
37
47
private var info = BufferInfo ()
38
48
private val dropper = DecoderDropper (continuous)
39
49
50
+ private var dequeuedInputs by observable(0 ) { _, _, _ -> printDequeued() }
51
+ private var dequeuedOutputs by observable(0 ) { _, _, _ -> printDequeued() }
52
+ private fun printDequeued () {
53
+ // log.v("dequeuedInputs=$dequeuedInputs dequeuedOutputs=$dequeuedOutputs")
54
+ }
55
+
40
56
override fun initialize (next : DecoderChannel ) {
41
57
super .initialize(next)
58
+ log.i(" initialize()" )
42
59
val surface = next.handleSourceFormat(format)
43
60
codec.configure(format, surface, null , 0 )
44
61
codec.start()
45
62
}
46
63
47
64
override fun buffer (): Pair <ByteBuffer , Int >? {
48
65
val id = codec.dequeueInputBuffer(0 )
49
- log.v(" buffer(): id=$id " )
50
- return if (id >= 0 ) buffers.getInputBuffer(id) to id else null
66
+ return if (id >= 0 ) {
67
+ dequeuedInputs++
68
+ buffers.getInputBuffer(id) to id
69
+ } else {
70
+ log.i(" buffer() failed. dequeuedInputs=$dequeuedInputs dequeuedOutputs=$dequeuedOutputs " )
71
+ null
72
+ }
51
73
}
52
74
53
75
override fun enqueueEos (data : ReaderData ) {
76
+ log.i(" enqueueEos()!" )
77
+ dequeuedInputs--
54
78
val flag = BUFFER_FLAG_END_OF_STREAM
55
79
codec.queueInputBuffer(data.id, 0 , 0 , 0 , flag)
56
80
}
57
81
58
82
override fun enqueue (data : ReaderData ) {
83
+ dequeuedInputs--
59
84
val (chunk, id) = data
60
85
val flag = if (chunk.keyframe) BUFFER_FLAG_SYNC_FRAME else 0
61
86
codec.queueInputBuffer(id, chunk.buffer.position(), chunk.buffer.remaining(), chunk.timeUs, flag)
@@ -65,36 +90,43 @@ internal class Decoder(
65
90
override fun drain (): State <DecoderData > {
66
91
val result = codec.dequeueOutputBuffer(info, 0 )
67
92
return when (result) {
68
- INFO_TRY_AGAIN_LATER -> State .Wait
93
+ INFO_TRY_AGAIN_LATER -> {
94
+ log.i(" drain(): got INFO_TRY_AGAIN_LATER, waiting." )
95
+ State .Wait
96
+ }
69
97
INFO_OUTPUT_FORMAT_CHANGED -> {
98
+ log.i(" drain(): got INFO_OUTPUT_FORMAT_CHANGED, handling format and retrying. format=${codec.outputFormat} " )
70
99
next.handleRawFormat(codec.outputFormat)
71
100
State .Retry
72
101
}
73
102
INFO_OUTPUT_BUFFERS_CHANGED -> {
103
+ log.i(" drain(): got INFO_OUTPUT_BUFFERS_CHANGED, retrying." )
74
104
buffers.onOutputBuffersChanged()
75
105
State .Retry
76
106
}
77
107
else -> {
78
108
val isEos = info.flags and BUFFER_FLAG_END_OF_STREAM != 0
79
109
val timeUs = if (isEos) 0 else dropper.output(info.presentationTimeUs)
80
110
if (timeUs != null /* && (isEos || info.size > 0) */ ) {
111
+ dequeuedOutputs++
81
112
val buffer = buffers.getOutputBuffer(result)
82
113
val data = DecoderData (buffer, timeUs) {
83
114
codec.releaseOutputBuffer(result, it)
115
+ dequeuedOutputs--
84
116
}
85
- // log.w("TDBG isEos=$isEos timeUs=$timeUs")
86
117
if (isEos) State .Eos (data) else State .Ok (data)
87
118
} else {
88
119
codec.releaseOutputBuffer(result, false )
89
120
State .Wait
121
+ }.also {
122
+ log.v(" drain(): returning $it " )
90
123
}
91
124
}
92
- }.also {
93
- log.v(" Returning $it " )
94
125
}
95
126
}
96
127
97
128
override fun release () {
129
+ log.i(" release(): releasing codec. dequeuedInputs=$dequeuedInputs dequeuedOutputs=$dequeuedOutputs " )
98
130
codec.stop()
99
131
codec.release()
100
132
}
0 commit comments