1+ package com.dan.videostab
2+
3+ import android.media.MediaCodec
4+ import android.media.MediaCodec.BufferInfo
5+ import android.media.MediaExtractor
6+ import android.media.MediaFormat
7+ import android.media.MediaMuxer
8+ import java.nio.ByteBuffer
9+
10+ class VideoMerge {
11+ companion object {
12+ private const val BUFFER_SIZE = 1024 * 1024 // 1 MB
13+ private const val VIDEO_MIME_PREFIX = " video/"
14+
15+ private fun addTracks ( muxer : MediaMuxer , sourceFile : String , filter : (String )-> Boolean ) {
16+ val extractor = MediaExtractor ()
17+ extractor.setDataSource(sourceFile)
18+
19+ for (sourceTrackIndex in 0 until extractor.trackCount) {
20+ val trackFormat = extractor.getTrackFormat(sourceTrackIndex)
21+ val trackMime = trackFormat.getString(MediaFormat .KEY_MIME ) ? : continue
22+ if (filter(trackMime)) muxer.addTrack(trackFormat)
23+ }
24+
25+ extractor.release()
26+ }
27+
28+ private fun copyTracks ( muxer : MediaMuxer , sourceFile : String , buffer : ByteBuffer , startDestTrackIndex : Int , filter : (String )-> Boolean ): Int {
29+ val extractor = MediaExtractor ()
30+ val bufferInfo = BufferInfo ()
31+ var destTrackIndex = startDestTrackIndex
32+
33+ extractor.setDataSource(sourceFile)
34+
35+ for (sourceTrackIndex in 0 until extractor.trackCount) {
36+ val trackFormat = extractor.getTrackFormat(sourceTrackIndex)
37+ val trackMime = trackFormat.getString(MediaFormat .KEY_MIME ) ? : continue
38+ if (! filter(trackMime)) continue
39+
40+ extractor.selectTrack(sourceTrackIndex)
41+
42+ while (true ) {
43+ val readSize = extractor.readSampleData(buffer, 0 )
44+ if (readSize <= 0 ) break
45+
46+ bufferInfo.set(
47+ 0 ,
48+ readSize,
49+ extractor.sampleTime,
50+ if (0 != (extractor.sampleFlags and MediaExtractor .SAMPLE_FLAG_SYNC )) MediaCodec .BUFFER_FLAG_KEY_FRAME else 0 )
51+ muxer.writeSampleData(destTrackIndex, buffer, bufferInfo)
52+
53+ if (! extractor.advance()) break
54+ }
55+
56+ bufferInfo.set(0 , 0 , extractor.sampleTime, MediaCodec .BUFFER_FLAG_END_OF_STREAM )
57+ muxer.writeSampleData(destTrackIndex, buffer, bufferInfo)
58+ destTrackIndex++
59+ }
60+
61+ extractor.release()
62+ return destTrackIndex
63+ }
64+
65+ fun merge ( outputFile : String , inputVideoFile : String , inputAudioFile : String ): Boolean {
66+ var success = false
67+ val buffer = ByteBuffer .allocate(BUFFER_SIZE )
68+
69+ try {
70+ val muxer = MediaMuxer (outputFile, MediaMuxer .OutputFormat .MUXER_OUTPUT_MPEG_4 )
71+ var startDestTrackIndex = 0
72+
73+ addTracks( muxer, inputVideoFile ) { mime -> mime.startsWith(VIDEO_MIME_PREFIX ) }
74+ addTracks( muxer, inputAudioFile ) { mime -> ! mime.startsWith(VIDEO_MIME_PREFIX ) }
75+
76+ muxer.start()
77+
78+ startDestTrackIndex = copyTracks( muxer, inputVideoFile, buffer, startDestTrackIndex ) { mime -> mime.startsWith(VIDEO_MIME_PREFIX ) }
79+ copyTracks( muxer, inputAudioFile, buffer, startDestTrackIndex ) { mime -> ! mime.startsWith(VIDEO_MIME_PREFIX ) }
80+
81+ muxer.stop()
82+ muxer.release()
83+ success = true
84+ } catch (e: Exception ) {
85+ e.printStackTrace()
86+ }
87+
88+ return success
89+ }
90+ }
91+ }
0 commit comments