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

Feature: Add settings validation to FFmpegConfigManager #95

Open
wants to merge 1 commit into
base: 60-feature-add-ffmpegcontroller-to-core
Choose a base branch
from

Conversation

andcscott
Copy link
Contributor

Hi @omeryusufyagci, hope you're doing well!

This PR relates to #56 by adding settings validation for the current FFmpeg options available in FFmpegConfigManager. More specifically, it attempts to address issues where the sample rate or number of audio channels could be unacceptable to FFmpeg. I reviewed the codecs rather thoroughly, but I did not consider other audio/video options for this PR primarily because they aren't currently needed/I'm unsure which you'd eventually like to add.

In addition to referencing the current specs for the available codecs (AAC, MP3, FLAC, Opus) I also did some testing with FFmpeg to determine when it continue even when given an option outside the spec. In this way, the config manager should only throw an error when FFmpeg would truly panic, not when it proceeds with its own set of assumptions. I also reviewed cases where FFmpeg could throw an error even though the spec states it is valid (255 Opus channels could require a very complicated rematrix!).

Last, two template functions were added to help streamline the process of adding new options later. These templates address the two primary ways in which FFmpeg conceives of a valid option: either a maximum unsigned integer, or a list of acceptable unsigned integers.

Adds settings validation for currently implemented FFmpeg options in
`FFmpegConfigManager`. Specifically, the current audio options were tested
against FFmpeg to determine which invalid settings would result in an error.
In cases where FFmpeg would proceed despite an otherwise invalid codec option
(ex. 0 channels), `MediaProcessor` will also decline to throw an error and
allow FFmpeg to set sane defaults. In cases where FFmpeg would not allow a user
to proceed we throw a runtime error with a helpful message for the user.
Copy link
Owner

@omeryusufyagci omeryusufyagci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @andcscott, thanks a lot for submitting this!

I'd say it's in a pretty good shape already. Only serious thing we should take a look at is the setInputFilePath function, that I commented on. The rest are more for general styling and usage uniformity across the core. Please take a look and let me know if you have any feedback!

Should be good to go following these changes. Thanks again !

Comment on lines 38 to 47
void FFmpegConfigManager::setInputFilePath(const fs::path inputFilePath) {
m_globalSettings.inputFilePath = inputFilePath;
int read = access((inputFilePath.string().c_str()), R_OK);
if (read < 0 && errno == EACCES) {
throw std::runtime_error("Insufficient permissions to read input file!");
} else if (read < 0) {
throw std::runtime_error("Unable to read input file, are you sure it exists?");
} else {
m_globalSettings.inputFilePath = inputFilePath;
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use the file system API here instead? this should have all you'd need to do the check.

Comment on lines +99 to +102
if (codec != VideoCodec::COPY) {
std::cerr << "Warning: Consider setting video codec to COPY if quality or conversion time are "
"unacceptable" << std::endl;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This took me a second to get it, so perhaps consider something like "Consider setting the video codec to COPY for higher quality and faster conversion times."

Comment on lines +162 to +192
// Validate Settings
void FFmpegConfigManager::validateAudio() {
switch (m_audioSettings.codec) {
case AudioCodec::AAC:
validateOption(m_audioSettings.numChannels, AAC_MAX_CHANNELS);
validateOption(m_audioSettings.sampleRate, AAC_SAMPLE_RATES);
break;
case AudioCodec::MP3:
validateOption(m_audioSettings.numChannels, MP3_MAX_CHANNELS);
validateOption(m_audioSettings.sampleRate, MP3_SAMPLE_RATES);
break;
case AudioCodec::FLAC:
validateOption(m_audioSettings.numChannels, FLAC_MAX_CHANNELS);
validateOption(m_audioSettings.sampleRate, FLAC_MAX_SAMPLE_RATE);
break;
case AudioCodec::OPUS:
validateOption(m_audioSettings.numChannels, OPUS_MAX_CHANNELS);
validateOption(m_audioSettings.sampleRate, OPUS_SAMPLE_RATES);
break;
case AudioCodec::UNKNOWN:
setAudioCodec(AudioCodec::AAC);
setAudioChannels(2);
setAudioSampleRate(48000);
std::cerr << "Warning: Audio codec cannot be UNKNOWN, default to AAC @ 48000 Hz with 2 "
"channels"
<< std::endl;
break;
default:
return;
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite okay to use switch case as we don't expect this list to grow (not too much at least), but mostly to reduce verbosity and make it more in line with the rest of the core, please consider implementing this with an unordered map, e.g. codecValidationRules that would map each codec to a struct that would hold the data needed for your validation (like max channels and sample rate).

The benefit would then be to simply try to find it in the map and handle the case it isn't found. This would also add some flexibility for when we might need to add rules for codec validation.

Comment on lines +147 to +160
template <typename T, std::size_t N>
void FFmpegConfigManager::validateOption(const T optionValue,
const std::array<T, N>& validOptions) {
for (const T& valid : validOptions) {
if (m_audioSettings.sampleRate == valid) return;
}
std::string codec = Utils::enumToString<AudioCodec>(getAudioCodec(), getAudioCodecAsString());
std::ostringstream ssError;
ssError << "Invalid " + codec + " sample rate: FFmpeg supports ";
for (const T& opt : validOptions) {
ssError << opt << " ";
}
throw std::runtime_error(ssError.str());
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go with vector here for validOptions as it would make it a bit more flexible and keep the template parameters cleaner. Vector should be almost as good, with such a small data set and likely infrequent access patterns.

@omeryusufyagci omeryusufyagci added improvement Improve existing functionality iteration Requires further iteration for approval core Media processing component, the core C++ binary. labels Feb 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Media processing component, the core C++ binary. improvement Improve existing functionality iteration Requires further iteration for approval
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants