Skip to content

Commit 6861ac7

Browse files
committed
Allow to copy audio tracks from the original video.
1 parent e2e6253 commit 6861ac7

File tree

5 files changed

+109
-5
lines changed

5 files changed

+109
-5
lines changed

app/src/main/java/com/dan/videostab/MainFragment.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,15 @@ class MainFragment(activity: MainActivity) : AppFragment(activity) {
245245
val outputPath = getOutputPath()
246246

247247
try {
248-
val inputStream = File(tmpOutputVideo).inputStream()
249-
val outputStream = File(outputPath).outputStream()
250-
inputStream.copyTo(outputStream)
251-
inputStream.close()
252-
outputStream.close()
248+
if (settings.keepAudio) {
249+
VideoMerge.merge(outputPath, tmpOutputVideo, tmpInputVideo)
250+
} else {
251+
val inputStream = File(tmpOutputVideo).inputStream()
252+
val outputStream = File(outputPath).outputStream()
253+
inputStream.copyTo(outputStream)
254+
inputStream.close()
255+
outputStream.close()
256+
}
253257

254258
//Add it to gallery
255259
MediaScannerConnection.scanFile(context, arrayOf(outputPath), null, null)

app/src/main/java/com/dan/videostab/Settings.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class Settings( private val activity: Activity) {
4242
var algorithm: Int = ALGORITHM_GENERIC
4343
var strength: Int = 1
4444
var encoder: Int = ENCODER_H265
45+
var keepAudio = false
4546

4647
init {
4748
loadProperties()

app/src/main/java/com/dan/videostab/SettingsFragment.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ class SettingsFragment(activity: MainActivity ) : AppFragment(activity) {
1919

2020
override fun onBack(homeButton: Boolean) {
2121
settings.encoder = binding.videoEncoder.selectedItemPosition
22+
settings.keepAudio = binding.switchKeepAudio.isChecked
2223
settings.saveProperties()
2324
}
2425

2526
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
2627
binding = SettingsFragmentBinding.inflate( inflater )
2728

2829
binding.videoEncoder.setSelection(settings.encoder)
30+
binding.switchKeepAudio.isChecked = settings.keepAudio
2931

3032
return binding.root
3133
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
}

app/src/main/res/layout/settings_fragment.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
android:entries="@array/video_encoder"
3838
android:spinnerMode="dropdown" />
3939
</LinearLayout>
40+
41+
<Switch
42+
android:id="@+id/switchKeepAudio"
43+
android:layout_width="match_parent"
44+
android:layout_height="wrap_content"
45+
android:text="Keep audio" />
4046
</LinearLayout>
4147
</ScrollView>
4248
</LinearLayout>

0 commit comments

Comments
 (0)