Skip to content

Commit 0811d8d

Browse files
committed
[feat] #62 compress video
1 parent cc2e33f commit 0811d8d

File tree

6 files changed

+115
-18
lines changed

6 files changed

+115
-18
lines changed

app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
56
<uses-permission android:name="android.permission.INTERNET"/>
67
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" />
78
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" />

feature/upload/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ dependencies {
1010
implementation("com.google.accompanist:accompanist-permissions:0.34.0")
1111
implementation("com.google.accompanist:accompanist-insets:0.24.13-rc")
1212
implementation("com.google.accompanist:accompanist-systemuicontroller:0.24.13-rc")
13+
implementation("com.github.AbedElazizShe:LightCompressor:1.3.2")
1314
implementation(projects.domain.upload)
1415
}

feature/upload/src/main/java/com/record/upload/SelectedVideoScreen.kt

+101-9
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,43 @@ package com.record.upload
22

33
import android.content.ContentUris
44
import android.content.Context
5+
import android.media.MediaMetadataRetriever
56
import android.net.Uri
67
import android.provider.MediaStore
78
import android.util.Log
89
import androidx.compose.foundation.Image
910
import androidx.compose.foundation.layout.Box
1011
import androidx.compose.foundation.layout.PaddingValues
1112
import androidx.compose.foundation.layout.fillMaxSize
13+
import androidx.compose.foundation.layout.height
1214
import androidx.compose.foundation.layout.padding
1315
import androidx.compose.foundation.layout.size
16+
import androidx.compose.foundation.layout.width
17+
import androidx.compose.material3.Text
1418
import androidx.compose.runtime.Composable
19+
import androidx.compose.ui.Alignment
1520
import androidx.compose.ui.Modifier
1621
import androidx.compose.ui.draw.clip
1722
import androidx.compose.ui.graphics.RectangleShape
1823
import androidx.compose.ui.layout.ContentScale
1924
import androidx.compose.ui.platform.LocalContext
25+
import androidx.compose.ui.text.style.TextAlign
2026
import androidx.compose.ui.unit.dp
27+
import androidx.compose.ui.unit.sp
2128
import coil.ImageLoader
2229
import coil.compose.rememberAsyncImagePainter
2330
import coil.decode.VideoFrameDecoder
31+
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
32+
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
33+
import com.abedelazizshe.lightcompressorlibrary.VideoQuality
34+
import com.abedelazizshe.lightcompressorlibrary.config.Configuration
35+
import com.abedelazizshe.lightcompressorlibrary.config.SaveLocation
36+
import com.abedelazizshe.lightcompressorlibrary.config.SharedStorageConfiguration
37+
import com.record.designsystem.theme.White
2438
import com.record.ui.extension.customClickable
39+
import kotlinx.coroutines.CoroutineScope
40+
import kotlinx.coroutines.Dispatchers
41+
import kotlinx.coroutines.launch
2542

2643
@Composable
2744
fun SelectedVideoRoute(
@@ -32,7 +49,6 @@ fun SelectedVideoRoute(
3249

3350
@Composable
3451
fun SelectedVideoScreen() {
35-
Log.d("images", "${getAllVideos(10, null, LocalContext.current)}")
3652
val a = getAllVideos(10, null, LocalContext.current)
3753
}
3854

@@ -53,11 +69,10 @@ fun VideoThumbnail(
5369
model = video.filepath,
5470
imageLoader = imageLoader,
5571
)
56-
5772
Box(
5873
modifier = Modifier
59-
.padding(8.dp)
60-
.size(85.dp),
74+
.width(100.dp)
75+
.height(100.dp),
6176
) {
6277
Image(
6378
painter = painter,
@@ -66,9 +81,16 @@ fun VideoThumbnail(
6681
modifier = Modifier
6782
.fillMaxSize()
6883
.clip(RectangleShape)
69-
.customClickable(
70-
onClick = { setVideo(video) },
71-
),
84+
.customClickable { setVideo(video) },
85+
)
86+
Text(
87+
text = formatDuration(video.duration),
88+
fontSize = 12.sp,
89+
color = White,
90+
modifier = Modifier
91+
.align(Alignment.BottomEnd)
92+
.padding(bottom = 4.dp, end = 6.dp),
93+
textAlign = TextAlign.End,
7294
)
7395
}
7496
}
@@ -116,9 +138,8 @@ fun getAllVideos(
116138
val name = cursor.getString(nameColumn)
117139
val size = cursor.getInt(sizeColumn)
118140
val date = cursor.getString(dateColumn)
119-
120141
val contentUri = ContentUris.withAppendedId(uriExternal, id)
121-
Log.d("gallery contentUri", "$contentUri")
142+
val duration = getVideoDuration(context, contentUri)
122143
galleryVideoList.add(
123144
GalleryVideo(
124145
id,
@@ -127,6 +148,7 @@ fun getAllVideos(
127148
name = name,
128149
date = date ?: "",
129150
size = size,
151+
duration = duration,
130152
),
131153
)
132154
} while (cursor.moveToNext())
@@ -143,4 +165,74 @@ data class GalleryVideo(
143165
val name: String,
144166
val date: String,
145167
val size: Int,
168+
val duration: Long,
146169
)
170+
171+
fun getVideoDuration(context: Context, uri: Uri): Long {
172+
val retriever = MediaMetadataRetriever()
173+
return try {
174+
retriever.setDataSource(context, uri)
175+
val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
176+
val timeInMillisec = time?.toLongOrNull() ?: 0L
177+
timeInMillisec
178+
} catch (e: Exception) {
179+
e.printStackTrace()
180+
0L
181+
} finally {
182+
retriever.release()
183+
}
184+
}
185+
186+
fun compressVideo(context: Context, videoUri: Uri, name: String) {
187+
CoroutineScope(Dispatchers.IO).launch {
188+
VideoCompressor.start(
189+
context = context,
190+
uris = listOf(videoUri),
191+
isStreamable = false,
192+
sharedStorageConfiguration = SharedStorageConfiguration(
193+
saveAt = SaveLocation.movies,
194+
subFolderName = "compressed_videos",
195+
),
196+
configureWith = Configuration(
197+
quality = VideoQuality.MEDIUM,
198+
isMinBitrateCheckEnabled = true,
199+
videoBitrateInMbps = 5,
200+
disableAudio = false,
201+
keepOriginalResolution = false,
202+
videoNames = listOf(name),
203+
),
204+
listener = object : CompressionListener {
205+
override fun onProgress(index: Int, percent: Float) {
206+
// 압축 진행 상황 업데이트
207+
Log.d("video onProgress", "$index , $percent")
208+
}
209+
210+
override fun onStart(index: Int) {
211+
// 압축 시작 시 호출
212+
Log.d("video onStart", "$index")
213+
}
214+
215+
override fun onSuccess(index: Int, size: Long, path: String?) {
216+
// 압축 성공 시 호출
217+
Log.d("video onSuccess", "$index $size $path")
218+
}
219+
220+
override fun onFailure(index: Int, failureMessage: String) {
221+
// 압축 실패 시 호출
222+
Log.d("video onFailure", "onFailure")
223+
}
224+
225+
override fun onCancelled(index: Int) {
226+
// 압축 취소 시 호출
227+
Log.d("video onCancelled", "onCancelled")
228+
}
229+
},
230+
)
231+
}
232+
}
233+
234+
fun formatDuration(durationMillis: Long): String {
235+
val minutes = (durationMillis / 1000) / 60
236+
val seconds = (durationMillis / 1000) % 60
237+
return String.format("%d:%02d", minutes, seconds)
238+
}

feature/upload/src/main/java/com/record/upload/VideoPickerScreen.kt

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ fun VideoPickerScreen(
161161
modifier = Modifier
162162
.padding(horizontal = 16.dp),
163163
) {
164+
// if (state.video!=null) compressVideo(context,state.video!!.uri, state.video.name)
164165
Text(
165166
text = "영상",
166167
color = RecordyTheme.colors.white,

feature/upload/src/main/java/com/record/upload/component/bottomsheet/SelectedVideoBottomSheet.kt

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.record.upload.component.bottomsheet
22

33
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.Column
67
import androidx.compose.foundation.layout.fillMaxSize
@@ -19,7 +20,6 @@ import androidx.compose.ui.Modifier
1920
import androidx.compose.ui.text.style.TextAlign
2021
import androidx.compose.ui.unit.dp
2122
import com.record.designsystem.component.bottomsheet.RecordyBottomSheet
22-
import com.record.designsystem.component.button.RecordyButton
2323
import com.record.designsystem.component.navbar.TopNavigationBar
2424
import com.record.designsystem.theme.Background
2525
import com.record.designsystem.theme.Gray03
@@ -56,24 +56,25 @@ fun SelectedVideoBottomSheet(
5656
color = Gray03,
5757
style = RecordyTheme.typography.caption2,
5858
maxLines = 1,
59-
modifier = Modifier.fillMaxWidth(),
59+
modifier = Modifier.fillMaxWidth().padding(bottom = 22.dp),
6060
textAlign = TextAlign.Center,
6161
)
6262
LazyVerticalGrid(
6363
columns = GridCells.Fixed(4),
64-
modifier = Modifier.padding(8.dp),
64+
horizontalArrangement = Arrangement.spacedBy(1.dp),
65+
verticalArrangement = Arrangement.spacedBy(1.dp),
6566
) {
6667
items(galleyVideos) { video ->
6768
VideoThumbnail(video = video, setVideo = setVideo)
6869
}
6970
}
7071
}
71-
RecordyButton(
72-
modifier = Modifier.align(Alignment.BottomCenter),
73-
text = "다음",
74-
enabled = true,
75-
onClick = onDismissRequest,
76-
)
72+
// RecordyButton(
73+
// modifier = Modifier.align(Alignment.BottomCenter),
74+
// text = "다음",
75+
// enabled = true,
76+
// onClick = onDismissRequest,
77+
// )
7778
}
7879
}
7980
}

settings.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencyResolutionManagement {
1818
repositories {
1919
google()
2020
mavenCentral()
21+
maven(url = "https://jitpack.io")
2122
maven(url = "https://devrepo.kakao.com/nexus/content/groups/public/")
2223
}
2324
}

0 commit comments

Comments
 (0)