Skip to content

Commit

Permalink
Merge pull request #239 from amosproj/gui/feature/video-selection-imp…
Browse files Browse the repository at this point in the history
…rovements

Improvements to the Video Selections Process
  • Loading branch information
akriese authored Jan 31, 2024
2 parents 6c4c014 + 9d64f18 commit f50e541
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 30 deletions.
17 changes: 17 additions & 0 deletions DifferenceGenerator/src/main/kotlin/AcceptedCodecs.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.bytedeco.javacv.FFmpegFrameGrabber

/**
* A globally accessible object that contains a list of all accepted codecs. This list can be
* expanded to include more codecs.
Expand All @@ -17,5 +19,20 @@ class AcceptedCodecs {
"Uncompressed YUV 422 8-bit",
"FFV1 YUV 422 8-bit",
)

/**
* Checks if the given file is in an accepted codec.
*/
public fun checkFile(path: String): Boolean {
val grabber = FFmpegFrameGrabber(path)
grabber.start()
val codecName = grabber.videoMetadata["encoder"] ?: grabber.videoCodecName
for (codec in ACCEPTED_CODECS) {
if (codecName.contains(codec, ignoreCase = true)) {
return true
}
}
return false
}
}
}
16 changes: 1 addition & 15 deletions DifferenceGenerator/src/main/kotlin/DifferenceGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class DifferenceGenerator(
* @throws DifferenceGeneratorDimensionException if the videos' dimensions don't match.
*/
init {
if (!isLosslessCodec(videoReferenceGrabber) || !isLosslessCodec(videoCurrentGrabber)) {
if (!AcceptedCodecs.checkFile(videoReferencePath) || !AcceptedCodecs.checkFile(videoCurrentPath)) {
throw DifferenceGeneratorCodecException("Videos must be in a lossless codec")
}

Expand All @@ -73,20 +73,6 @@ class DifferenceGenerator(
avutil.av_log_set_level(avutil.AV_LOG_QUIET)
}

/**
* Determines whether the given video file is encoded using one of the
* [AcceptedCodecs.ACCEPTED_CODECS].
*
* @param grabber [MaskedImageGrabber] of the video to check
* @return true if the video file is encoded using one of the [AcceptedCodecs.ACCEPTED_CODECS],
* false otherwise
*/
private fun isLosslessCodec(grabber: MaskedImageGrabber): Boolean {
grabber.start()
val codecName = grabber.videoMetadata["encoder"] ?: grabber.videoCodecName
return codecName in AcceptedCodecs.ACCEPTED_CODECS
}

/**
* Generates a difference video from the two videos given in the constructor.
*
Expand Down
7 changes: 7 additions & 0 deletions GUI/src/main/kotlin/ui/components/general/FileSelector.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ui.components.general

import javax.swing.JFileChooser
import javax.swing.filechooser.FileNameExtensionFilter

/**
* Opens a file chooser dialog and returns the selected file path.
Expand All @@ -12,10 +13,16 @@ import javax.swing.JFileChooser
fun openFileChooserAndGetPath(
directoryPath: String?,
onResult: (String) -> Unit,
allowedFileExtensions: Array<String>? = null,
) {
val fileChooser = JFileChooser()
// set the current directory to the given directory path or if null the user's home directory
fileChooser.currentDirectory = java.io.File(directoryPath ?: System.getProperty("user.home"))
if (allowedFileExtensions != null) {
fileChooser.isAcceptAllFileFilterUsed = false
val filter = FileNameExtensionFilter(allowedFileExtensions.joinToString(", "), *allowedFileExtensions)
fileChooser.addChoosableFileFilter(filter)
}
val result = fileChooser.showOpenDialog(null)
if (JFileChooser.APPROVE_OPTION == result) {
onResult(fileChooser.selectedFile.absolutePath)
Expand Down
8 changes: 7 additions & 1 deletion GUI/src/main/kotlin/ui/components/general/ProjectManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ fun ProjectMenu(
onClick = {
openScope.launch(
Dispatchers.IO,
) { openFileChooserAndGetPath(state.value.openProjectPath) { path -> handleOpenProject(state, path, errorDialogText) } }
) {
openFileChooserAndGetPath(
directoryPath = state.value.openProjectPath,
onResult = { path -> handleOpenProject(state, path, errorDialogText) },
allowedFileExtensions = arrayOf("mkv"),
)
}
expanded = false
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,19 @@ fun RowScope.FileSelectorButton(
onUpdateResult: (String) -> Unit,
tooltipText: String? = null,
directoryPath: String? = null,
buttonDescription: String? = null,
allowedFileExtensions: Array<String>? = null,
) {
val scope = rememberCoroutineScope()
Button(
modifier = Modifier.weight(1f).padding(8.dp).fillMaxHeight(1f),
onClick = {
scope.launch(Dispatchers.IO) {
openFileChooserAndGetPath(directoryPath) { path -> onUpdateResult(path) }
openFileChooserAndGetPath(
directoryPath,
{ path -> onUpdateResult(path) },
allowedFileExtensions,
)
}
},
shape = MaterialTheme.shapes.medium,
Expand All @@ -66,10 +72,17 @@ fun RowScope.FileSelectorButton(
InfoIconWithHover(tooltipText)
}
}
// row to display the button text
Row(modifier = Modifier.weight(0.15f)) {
AutoSizeText(text = buttonText, minimalFontSize = 27)

if (buttonDescription != null) {
// row to display the button text
Row(modifier = Modifier.weight(0.1f)) { AutoSizeText(text = buttonText, minimalFontSize = 27) }
// row to display the button description
Row(modifier = Modifier.weight(0.05f)) { AutoSizeText(text = buttonDescription) }
} else {
// row to display the button text
Row(modifier = Modifier.weight(0.15f)) { AutoSizeText(text = buttonText, minimalFontSize = 27) }
}

// row to display the selected file path
Row(modifier = Modifier.weight(0.1f)) {
AutoSizeText(text = buttonPath ?: "No file selected", minimalFontSize = 25)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fun RowScope.MaskSelectorButton(
) {
val scope = rememberCoroutineScope()
Button(
onClick = { scope.launch(Dispatchers.IO) { openFileChooserAndGetPath(directoryPath) { path -> onUpdateResult(path) } } },
onClick = { scope.launch(Dispatchers.IO) { openFileChooserAndGetPath(directoryPath, { path -> onUpdateResult(path) }) } },
shape = MaterialTheme.shapes.medium,
modifier = Modifier.padding(16.dp).weight(0.6f).fillMaxHeight(0.9f),
) {
Expand Down
63 changes: 54 additions & 9 deletions GUI/src/main/kotlin/ui/screens/SelectVideoScreen.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package ui.screens

import AcceptedCodecs
import algorithms.AlgorithmExecutionState
import androidx.compose.foundation.layout.*
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import models.AppState
import ui.components.general.ErrorDialog
import ui.components.general.HelpMenu
import ui.components.general.ProjectMenu
import ui.components.selectVideoScreen.AdvancedSettingsButton
Expand All @@ -29,6 +27,8 @@ fun SelectVideoScreen(state: MutableState<AppState>) {
val scope = rememberCoroutineScope()
val showLoadingDialog = remember { mutableStateOf(false) }

val errorDialogText = remember { mutableStateOf<String?>(null) }

Column(modifier = Modifier.fillMaxSize()) {
// menu bar
CenterAlignedTopAppBar(
Expand All @@ -48,27 +48,46 @@ fun SelectVideoScreen(state: MutableState<AppState>) {
HelpMenu()
},
)

// video selection
Row(modifier = Modifier.weight(0.85f)) {
FileSelectorButton(
buttonText = "Select Reference Video",
buttonPath = state.value.videoReferencePath,
onUpdateResult = { selectedFilePath ->
state.value = state.value.copy(videoReferencePath = selectedFilePath)
checkVideoFormatAndCodec(
selectedFilePath,
state,
errorDialogText,
true,
)
},
directoryPath = state.value.videoReferencePath,
buttonDescription = "Please upload a video with format mkv or mov.",
allowedFileExtensions = arrayOf("mkv", "mov"),
)

if (errorDialogText.value != null) {
ErrorDialog(
onCloseRequest = { errorDialogText.value = null },
text = errorDialogText.value!!,
)
}
FileSelectorButton(
buttonText = "Select Current Video",
buttonPath = state.value.videoCurrentPath,
onUpdateResult = { selectedFilePath ->
state.value = state.value.copy(videoCurrentPath = selectedFilePath)
checkVideoFormatAndCodec(
selectedFilePath,
state,
errorDialogText,
false,
)
},
directoryPath = state.value.videoCurrentPath,
buttonDescription = "Please upload a video with format mkv or mov.",
allowedFileExtensions = arrayOf("mkv", "mov"),
)
}

// screen switch buttons
Row(modifier = Modifier.weight(0.15f)) {
ComputeDifferencesButton(state, scope, showLoadingDialog)
Expand All @@ -82,3 +101,29 @@ fun SelectVideoScreen(state: MutableState<AppState>) {
})
}
}

/**
* Checks if the selected file is in the correct format and codec.
*/
private fun checkVideoFormatAndCodec(
selectedFilePath: String,
state: MutableState<AppState>,
errorDialogText: MutableState<String?>,
isReference: Boolean,
) {
if (!selectedFilePath.endsWith(".mkv") && !selectedFilePath.endsWith(".mov")) {
errorDialogText.value =
"Uploaded Video is not in the correct format. Please upload a video with format mkv or mov."
return
}
if (!AcceptedCodecs.checkFile(selectedFilePath)) {
errorDialogText.value =
"Uploaded Video is not in the correct codec. Please upload a video encoded with ffv1."
return
}
if (isReference) {
state.value = state.value.copy(videoReferencePath = selectedFilePath)
} else {
state.value = state.value.copy(videoCurrentPath = selectedFilePath)
}
}

0 comments on commit f50e541

Please sign in to comment.