Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev compress #873

Merged
merged 44 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
28bf6fe
Initial implementation of video compression for Android (including ca…
mikedawson Apr 2, 2024
13b51a0
Implement support for compression level presets on CompressVideoUseCa…
mikedawson Apr 2, 2024
1e0cd59
Change video compression on Android to use Jetpack media3 transformer.
mikedawson Apr 3, 2024
98a0618
Remove use of kapt and databinding on app-android
mikedawson Apr 3, 2024
601243e
Fix use of compression levels for VideoContentImporterCommonJvm
mikedawson Apr 3, 2024
b84bd9f
Update compression presets for video on Android.
mikedawson Apr 5, 2024
084ff23
Add display of ContentEntry size (including original size where it wa…
mikedawson Apr 5, 2024
770a63e
Update video compressor on Android to skip compressing video when not…
mikedawson Apr 5, 2024
4c7a780
Show import error messages to the user.
mikedawson Apr 5, 2024
5f576c3
Move mediainfo metadata extraction into its own use case (so it can b…
mikedawson Apr 5, 2024
584f53d
Initial rough implementation of using VLC command line to compress vi…
mikedawson Apr 5, 2024
e6913a8
Update CompressVideoUseCaseJvmVlc to use h264/aac encoding.
mikedawson Apr 5, 2024
ad7d8dc
Set width and height when compressing video with VLC.
mikedawson Apr 7, 2024
9b9a5c4
Add suspending await function for Process to support cancellation.
mikedawson Apr 7, 2024
9457b76
Add compress video use case for JVM that uses Handbrake.
mikedawson Apr 7, 2024
0ee54a3
Add progress monitoring support for CompressVideoUseCaseHandbrake
mikedawson Apr 7, 2024
2d5f906
Implement cancellation support for video conversion on Desktop.
mikedawson Apr 8, 2024
32ddc8e
Improve compression presets
mikedawson Apr 9, 2024
85f3b35
Compression implementation updates
mikedawson Apr 9, 2024
bd1d676
Fix conveyor.conf inclusion of windows app-resources.
mikedawson Apr 9, 2024
dabccf5
Update INSTALL.md documentation to reflect updated media dependencies.
mikedawson Apr 9, 2024
49c5273
Remove server side usage of ffmpeg and ffprobe.
mikedawson Apr 9, 2024
1822632
Add video compression to server
mikedawson Apr 9, 2024
186493b
Add support for showing the progress of content entry import jobs tha…
mikedawson Apr 10, 2024
78c82ca
Implement job control for entries processed on the server (issue #750)
mikedawson Apr 10, 2024
989bb79
Implement dismiss error message for content entry import jobs running…
mikedawson Apr 10, 2024
ba5d98f
Add AV1 support for Pre-Android10 devices.
mikedawson Apr 11, 2024
f985e36
Add mediainfo output
mikedawson Apr 11, 2024
c01d8d6
Update ExtractMediaMetadataUseCaseMediaInfo to handle improve handlin…
mikedawson Apr 11, 2024
64dcba5
Add CompressVideoUseCaseHandbrakeTest debug output
mikedawson Apr 11, 2024
3c32f16
Add support for use of HandBrakeCLI FlatPak
mikedawson Apr 12, 2024
5b58423
Update cypress end to end test to handle video compression
mikedawson Apr 12, 2024
627cd96
Initial implement of compression of Xapi zip contents. Compress all v…
mikedawson Apr 12, 2024
28a13fd
Additional compression implementations for Android.
mikedawson Apr 15, 2024
7f5759c
Fix picture saving: CompressImageUseCase implementations need to use …
mikedawson Apr 16, 2024
5235b4d
Fix picture saving: CompressImageUseCase implementations need to use …
mikedawson Apr 16, 2024
c24d02d
Fix UmRestApplication: GetStoragePathUseCase dependency should not be…
mikedawson Apr 16, 2024
707f266
Implement content compression for H5P imports.
mikedawson Apr 16, 2024
9dd9af0
Implement compression for epub content.
mikedawson Apr 16, 2024
cc56f9c
Add note on compression type regardling lack of support for Brotli.
mikedawson Apr 16, 2024
c20aa90
Add (optional) PDF compression for JVM that will be used when the gho…
mikedawson Apr 17, 2024
5dfbcd1
Add Content-Length-Original header when compression so that TransferJ…
mikedawson Apr 17, 2024
af83d2f
Merge branch 'primary' into dev-compress
mikedawson Apr 17, 2024
6bcbb5d
Bump version
mikedawson Apr 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 21 additions & 3 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,33 @@ using apt-get on Ubuntu or MSI/EXE for Windows).

On Ubuntu:
```
apt-get install openjdk-18-jdk ffmpeg
apt-get install openjdk-18-jdk mediainfo sox libsox-fmt-mp3
```
Note: if you have other Java versions, make sure you run the server jar using JDK17+. You can use ``sudo update-alternatives --config java``
to set the default java version to run.

If using Ubuntu 23.10+, you can use the HandBrakeCLI from the Ubuntu package manager (1.6.1):
```
apt-get install handbrake-cli
```

Previous versions (including 22.04 LTS) package HandBrake 1.5 (which is not supported due to lack of
AV1 support). You can install the latest HandBrake CLI using flatpak as per [HandBrake website](https://handbrake.fr/downloads2.php):
```
apt-get install flatpak
flatpak install /path/where/downloaded/HandBrakeCLI-1.7.3-x86_64.flatpak
```

On Windows:
* Download and install Java (JDK17+) if not already installed from the Java site [https://www.oracle.com/java/technologies/downloads/#jdk17-windows](https://www.oracle.com/java/technologies/downloads/#jdk17-windows)
* FFMPEG is required. If you don't already have it in your path, the server can download it for you
when you run it for the first time.
* Use Winget to download and install MediaInfo and HandBrakeCLI:
```
winget install -e --id MediaArea.MediaInfo
winget install -e --id HandBrake.HandBrake.CLI
```
* Download and install from the [Sox website](https://sourceforge.net/projects/sox/files/sox/14.4.2/)
(the Winget package does not work because it does not get added to the path).


### 3. Unzip ustad-server.zip and start server

Expand Down
65 changes: 55 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,47 @@ entire project.
* __Step 1: Download and install Android Studio__: If you don't already have the latest version, download
from [https://developer.android.com/studio](https://developer.android.com/studio).

* __Step 2: Make sure that java is on your system path__: If you already have OpenJDK17, you can
use that, otherwise you need to download from the Java website or install using your system package
manager.

Supported JDK Version: JDK17 (only). JDK21 not supported yet due to Proguard issues on app-desktop.
* __Step 2: Install dependencies__

Development requirements are:
* JDK17 (only): JDK21 not supported yet due to Proguard issues on app-desktop.
* MediaInfo: MediaInfo is used by the server and desktop version to validate media files and extract
metadata
* VLC (3.0.0)+ : VLC is used on the desktop version (via VLC4J) to play videos.
* HandBrakeCLI (1.6.0+): HandBrake (Command Line Interface) is used by the server and desktop version
to compress videos.
* SOX (14+) : Sox is used to transcode audio files

Linux:

Install OpenJDK17, ffmpeg, and mediainfo using the system package manager e.g.
If you already have JDK 17 installed, you can use it.

OpenJDK17, mediainfo, and vlc, can be installed using the system package manager e.g.

```
sudo apt-get install openjdk-17-jdk mediainfo vlc sox libsox-fmt-mp3
```

If using Ubuntu 23.10+, you can use the HandBrakeCLI from the Ubuntu package manager (1.6.1):
```
apt-get install handbrake-cli
```

Previous versions (including 22.04 LTS) has HandBrake 1.5 (which is not supported due to lack of
AV1 support). You can install the latest HandBrake CLI using flatpak as per [HandBrake website](https://handbrake.fr/downloads2.php):
```
apt-get install flatpak
flatpak install /path/where/downloaded/HandBrakeCLI-1.7.3-x86_64.flatpak
```

Ghostscript (optional): The ghostscript (gs) command can be used to compress PDFs.
```
sudo apt-get install openjdk-17-jdk ffmpeg mediainfo
apt-get install ghostscript
```

Windows:

__JDK__
Download and install Java (JDK17) if not already installed from the Java site
[https://www.oracle.com/java/technologies/downloads/#jdk17-windows](https://www.oracle.com/java/technologies/downloads/#jdk17-windows)

Expand All @@ -96,8 +121,28 @@ Now find the PATH variable. Append ```;%JAVA_HOME%\bin``` to the value and save

Further details: see the [Java website](https://www.java.com/en/download/help/path.html).

If you don't have ffmpeg installed, the server can download it for you when you run it for the first
time.
__VLC__
Download and install from [https://www.videolan.org/](https://www.videolan.org/). Make sure to choose
a 64bit version (using a non-64bit version will fail). VLC is used by the Desktop version to play
videos via VLCJ.

__MediaInfo__
Download and install such that the MediaInfo command is in the PATH. This can be done using winget:

```
winget install -e --id MediaArea.MediaInfo
```

__HandBrakeCLI__
Download and install such that the HandBrakeCLI command is in the PATH. This can be done using winget:

```
winget install -e --id HandBrake.HandBrake.CLI
```

__Sox__
Download and install from the [Sox website](https://sourceforge.net/projects/sox/files/sox/14.4.2/) (the Winget package does not work because it does not get
added to the path).

* __Step 3: Import the project in Android Studio__: Select File, New, Project from Version Control. Enter
https://github.com/UstadMobile/UstadMobile.git and wait for the project to import. Switch to the
Expand Down Expand Up @@ -157,7 +202,7 @@ Code is contained (mostly) in the following modules:
* [core](core/) : Contains view models, ui state, core business logic.
* [sharedse](sharedse/): Contains some shared implementations for operating systems with a disk (JVM/Android)
* [lib-database](lib-database/): contains the database: DAOs (e.g. SQL queries), and entity classes.
* [lib-ui-compose](lib-ui-compose/): contains Compose multiplatform UI code used by app-android and app-desktop
* [lib-ui-compose](lib-ui-compose/): contains Compose multiplatform UI code used by app-android and app-desktop.
* [lib-util](lib-util/): Small utility functions
* [test-end-to-end](test-end-to-end/) End-to-end tests that run the app and server.
* [testserver-controller](testserver-controller/) An HTTP server that can control starting and
Expand Down
18 changes: 7 additions & 11 deletions app-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.plugin.serialization'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.kapt'
id 'kotlin-parcelize'
alias(libs.plugins.jetbrains.compose)
alias(libs.plugins.license)
Expand All @@ -18,7 +17,6 @@ if(keyStoreFile == null) {

def keyStorePropertiesExists = rootProject.file(keyStoreFile).exists()
def keystoreProperties = new Properties()
def baseAppId = "com.toughra.ustadmobile"

if (keyStorePropertiesExists) {
keystoreProperties.load(new FileInputStream(rootProject.file(keyStoreFile)))
Expand All @@ -36,17 +34,9 @@ configurations.all {
resolutionStrategy.force 'org.objenesis:objenesis:2.6'
}

kapt {
javacOptions {
// Increase the max count of errors from annotation processors.
// Default is 100.
option("-Xmaxerrs", 500)
}
}

android {
buildFeatures {
dataBinding true
dataBinding false
}

//APK splitting would interfere with sideloading the app between different targets and the size
Expand Down Expand Up @@ -332,6 +322,12 @@ dependencies {

implementation libs.androidx.browser
implementation libs.androidx.core.splashscreen


androidTestImplementation libs.androidx.test.runner
androidTestImplementation libs.junit
androidTestImplementation libs.androidx.test.core
androidTestUtil libs.androidx.test.orchestrator
}

licenseReport {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.ustadmobile.core.domain.compress.audio

import android.content.Context
import android.media.MediaCodecList
import androidx.core.net.toUri
import androidx.test.core.app.ApplicationProvider
import com.ustadmobile.core.uri.UriHelperAndroid
import com.ustadmobile.door.DoorUri
import com.ustadmobile.door.ext.toFile
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class CompressAudioUseCaseAndroidTest {


@JvmField
@Rule
var tempFolder = TemporaryFolder()

@Test
fun initTest() {
val context = ApplicationProvider.getApplicationContext<Context>()

val codecList = MediaCodecList(MediaCodecList.ALL_CODECS)
val audioEncoders = codecList.codecInfos.filter { codecInfo ->
codecInfo.isEncoder && codecInfo.supportedTypes.any { it.startsWith("audio") }
}
println(audioEncoders.flatMap { it.supportedTypes.toList() }.joinToString())

val compressAudioUseCase = CompressAudioUseCaseAndroid(
appContext = context,
uriHelper = UriHelperAndroid(context)
)
val audioFile = tempFolder.newFile()
this::class.java.getResourceAsStream("/river.mp3")!!.use { inStream ->
audioFile.outputStream().use {
inStream.copyTo(it)
it.flush()
}
}

val result = runBlocking {
compressAudioUseCase(
fromUri = audioFile.toUri().toString(),
)
}

Assert.assertNotNull(result)

val resultFile = DoorUri.parse(result!!.uri).toFile()
assertTrue(resultFile.exists())
assertTrue(resultFile.length() > 0)
println("result for is ${resultFile.length()}")
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,25 @@ package com.ustadmobile.core.domain.compress.image

import android.content.Context
import android.net.Uri
import android.os.Build
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.core.app.ApplicationProvider
import com.ustadmobile.core.domain.compress.CompressParams
import org.junit.Assert.assertTrue
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
@Config(sdk = intArrayOf(Build.VERSION_CODES.TIRAMISU))
class CompressUseCaseAndroidTest {
class CompressImageUseCaseAndroidTest {

@JvmField
@Rule
val tempFolder = TemporaryFolder()

@Test
fun givenInputImage_whenInvoked_thenWillCompress(){
val context = getApplicationContext<Context>()
val context = ApplicationProvider.getApplicationContext<Context>()
val tmpFile = tempFolder.newFile()
val outFile = tempFolder.newFile()
javaClass.getResourceAsStream("/image/testfile1.png")!!.use { assetIn ->
Expand All @@ -42,14 +36,15 @@ class CompressUseCaseAndroidTest {
fromUri = tmpFile.toUri().toString(),
toUri = outFile.toUri().toString(),
params = CompressParams(
maxWidth = 300,
maxHeight = 300,
maxWidth = 800,
maxHeight = 800,
)
)
val resultUri = Uri.parse(result.uri)
val resultUri = Uri.parse(result!!.uri)
val resultFile = resultUri.toFile()
assertTrue(resultUri.toFile().exists())
assertTrue(resultFile.length() > 0)
Assert.assertTrue(resultUri.toFile().exists())
Assert.assertTrue(resultFile.length() > 0)
Assert.assertTrue(resultFile.length() < tmpFile.length())
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.ustadmobile.core.domain.compress.video

import android.content.Context
import android.net.Uri
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.test.core.app.ApplicationProvider
import com.ustadmobile.core.domain.compress.CompressParams
import com.ustadmobile.core.domain.compress.CompressionLevel
import com.ustadmobile.core.uri.UriHelperAndroid
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.io.File
import java.io.FileOutputStream

class CompressVideoUseCaseAndroidTest {

@JvmField
@Rule
var tempFolder = TemporaryFolder()

lateinit var videoFile: File

lateinit var useCase: CompressVideoUseCaseAndroid

@Before
fun setup() {
val context = ApplicationProvider.getApplicationContext<Context>()
val uriHelper = UriHelperAndroid(context)
useCase = CompressVideoUseCaseAndroid(context, uriHelper)

videoFile = tempFolder.newFile("bus.mp4")
this::class.java.getResourceAsStream("/bus.mp4")!!.use { assetIn ->
FileOutputStream(videoFile).use { fileOut ->
assetIn.copyTo(fileOut)
fileOut.flush()
}
}
}

private fun compressAndAssert(compressionLevel: CompressionLevel) {
runBlocking {
val result = useCase(
fromUri = videoFile.toUri().toString(),
params = CompressParams(
compressionLevel = compressionLevel
)
)

val outFile = Uri.parse(result!!.uri).toFile()
Assert.assertTrue(outFile.length() > 0)
}
}

@Test
fun givenValidVideo_whenCompressLow_thenWillCompress() {
compressAndAssert(CompressionLevel.LOW)
}

@Test
fun givenValidVideo_whenCompressMedium_thenWillCompress() {
compressAndAssert(CompressionLevel.MEDIUM)
}

@Test
fun givenValidVideo_whenCompressHigh_thenWillCompress() {
compressAndAssert(CompressionLevel.HIGH)
}


}
Binary file added app-android/src/androidTest/resources/bunny.mp4
Binary file not shown.
Binary file added app-android/src/androidTest/resources/bus.mp4
Binary file not shown.
Binary file added app-android/src/androidTest/resources/river.mp3
Binary file not shown.