/
MuxerImpl.kt
177 lines (161 loc) · 5.31 KB
/
MuxerImpl.kt
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
/*
* Copyright (c) 2018-present, lmyooyo@gmail.com.
*
* This source code is licensed under the GPL license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.lmy.codec.muxer.impl
import android.media.MediaCodec
import android.media.MediaFormat
import android.media.MediaMuxer
import com.lmy.codec.encoder.Encoder
import com.lmy.codec.encoder.impl.AudioEncoderImpl
import com.lmy.codec.entity.CodecContext
import com.lmy.codec.entity.Sample
import com.lmy.codec.muxer.Muxer
import com.lmy.codec.pipeline.impl.EventPipeline
import com.lmy.codec.util.debug_e
import com.lmy.codec.util.debug_i
import java.io.File
import java.nio.ByteBuffer
import java.util.*
/**
* Created by lmyooyo@gmail.com on 2018/3/28.
*/
class MuxerImpl(private val context: CodecContext) : Muxer {
private val mQueue = LinkedList<Sample>()
private val mWriteSyn = Any()
private var mAudioPipeline = EventPipeline.create("AudioWritePipeline")
private var mVideoPipeline = EventPipeline.create("VideoWritePipeline")
private var muxer: MediaMuxer? = null
var videoTrack: Int = 0
var audioTrack: Int = 0
private var mFrameCount: Int = 0
private var mVideoTrackReady: Boolean = false
private var mAudioTrackReady: Boolean = false
private var mStart: Boolean = false
override var onMuxerListener: Muxer.OnMuxerListener? = null
init {
start()
}
private fun start() {
mFrameCount = 0
mVideoTrackReady = false
mAudioTrackReady = false
mStart = false
//删除已存在的文件
val file = File(context.ioContext.path!!)
if (file.exists()) file.delete()
muxer = MediaMuxer(file.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
}
override fun reset() {
mVideoPipeline.queueEvent(Runnable {
stop()
start()
})
}
override fun onFormatChanged(encoder: Encoder, format: MediaFormat) {
if (encoder is AudioEncoderImpl) {
debug_e("Add audio track")
addAudioTrack(format)
} else {
addVideoTrack(format)
}
}
/**
* 编码后的帧数据
* For VideoEncoderImpl
*/
override fun onSample(encoder: Encoder, info: MediaCodec.BufferInfo, data: ByteBuffer) {
if (encoder is AudioEncoderImpl) {
writeAudioSample(Sample.wrap(info, data))
} else {
writeVideoSample(Sample.wrap(info, data))
}
}
private fun checkStart() {
if ((mVideoTrackReady && context.audio.silence) || (mVideoTrackReady && mAudioTrackReady)) {
muxer?.start()
mStart = true
debug_e("Muxer start")
onMuxerListener?.onStart()
}
}
override fun addVideoTrack(format: MediaFormat) {
try {
videoTrack = muxer!!.addTrack(format)
} catch (e: Exception) {
debug_e("Add video track failed")
onMuxerListener?.onError(ERROR_ADD_TRACK, "Add video track failed")
e.printStackTrace()
return
}
mVideoTrackReady = true
debug_e("Muxer video start")
checkStart()
}
override fun addAudioTrack(format: MediaFormat) {
try {
audioTrack = muxer!!.addTrack(format)
debug_i("addAudioTrack ${format.getByteBuffer("csd-0").capacity()}")
} catch (e: Exception) {
debug_e("Add audio track failed")
onMuxerListener?.onError(ERROR_ADD_TRACK, "Add audio track failed")
e.printStackTrace()
return
}
mAudioTrackReady = true
debug_e("Muxer audio start")
checkStart()
}
override fun writeVideoSample(sample: Sample) {
if (!mStart) return
// debug_e("writeVideoSample ${sample.bufferInfo.presentationTimeUs}")
++mFrameCount
synchronized(mWriteSyn) {
mQueue.push(sample)
}
mVideoPipeline.queueEvent(Runnable {
synchronized(mWriteSyn) {
while (!mQueue.isEmpty()) {
writeSample(videoTrack, mQueue.poll())
}
}
})
}
override fun writeAudioSample(sample: Sample) {
if (!mStart) return
// debug_i("writeAudioSample ${sample.bufferInfo.presentationTimeUs}")
mAudioPipeline.queueEvent(Runnable {
writeSample(audioTrack, sample)
})
}
private fun writeSample(track: Int, sample: Sample) {
try {
// debug_e("write${if (videoTrack == track) "Video" else "Audio"}" +
// "Sample($mFrameCount, ${sample.bufferInfo.presentationTimeUs}): ${sample.bufferInfo.size}")
muxer?.writeSampleData(track, sample.sample, sample.bufferInfo)
} catch (e: Exception) {
onMuxerListener?.onError(ERROR_WRITE, "Write sample failed")
e.printStackTrace()
}
}
override fun release() {
debug_e("Muxer release")
mVideoPipeline.quit()
mAudioPipeline.quit()
stop()
}
private fun stop() {
mStart = false
try {
muxer?.release()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
companion object {
const val ERROR_ADD_TRACK = 0x200
const val ERROR_WRITE = 0x201
}
}