mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-27 04:13:36 +00:00
Added a configurable Opus bitrate for audio transcription
This commit is contained in:
parent
557d0b1409
commit
6be958eb92
@ -4993,6 +4993,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T16
|
||||
-- Uses the provider-configured model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1760715963"] = "Uses the provider-configured model"
|
||||
|
||||
-- Higher bitrates can improve transcription accuracy, especially for quiet or noisy recordings, at the cost of a larger upload to the transcription provider. 128 kbps is recommended.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1859657826"] = "Higher bitrates can improve transcription accuracy, especially for quiet or noisy recordings, at the cost of a larger upload to the transcription provider. 128 kbps is recommended."
|
||||
|
||||
-- Add Transcription Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2066315685"] = "Add Transcription Provider"
|
||||
|
||||
@ -5005,6 +5008,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T21
|
||||
-- Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T266367750"] = "Name"
|
||||
|
||||
-- Transcription audio quality
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3103106744"] = "Transcription audio quality"
|
||||
|
||||
-- Edit
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3267849393"] = "Edit"
|
||||
|
||||
@ -10492,6 +10498,21 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::THEMESEXTENSIONS::T4107955313"]
|
||||
-- Always use light theme
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::THEMESEXTENSIONS::T534715610"] = "Always use light theme"
|
||||
|
||||
-- 128 kbps (recommended)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T2152168180"] = "128 kbps (recommended)"
|
||||
|
||||
-- 256 kbps (largest upload, highest accuracy)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3092489829"] = "256 kbps (largest upload, highest accuracy)"
|
||||
|
||||
-- Unknown
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3424652889"] = "Unknown"
|
||||
|
||||
-- 64 kbps
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3501477553"] = "64 kbps"
|
||||
|
||||
-- 32 kbps (smallest upload, lowest accuracy)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T767394292"] = "32 kbps (smallest upload, lowest accuracy)"
|
||||
|
||||
-- Use no profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::PROFILE::T2205839602"] = "Use no profile"
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@T("With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section.")
|
||||
</MudJustifiedText>
|
||||
|
||||
|
||||
<MudTable Items="@this.SettingsManager.GetAllTranscriptionProviders()" Hover="@true" GroupBy="@GROUP_CONFIG" Class="border-dashed border rounded-lg">
|
||||
<ColGroup>
|
||||
<col style="width: 12em;"/>
|
||||
@ -84,5 +84,9 @@
|
||||
}
|
||||
|
||||
<LockableButton Text="@T("Add transcription provider")" IsLocked="@(() => !this.SettingsManager.ConfigurationData.App.AllowUserToAddProvider || !this.SettingsManager.ConfigurationData.App.AllowUserToAddTranscriptionProvider)" Icon="@Icons.Material.Filled.AddRoad" OnClickAsync="@this.AddTranscriptionProvider" Class="mt-3" />
|
||||
|
||||
<MudDivider Class="mt-6 mb-3"/>
|
||||
|
||||
<ConfigurationSelect OptionDescription="@T("Transcription audio quality")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.OpusBitrate)" Data="@ConfigurationSelectDataFactory.GetTranscriptionOpusBitrateData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.OpusBitrate = selectedValue)" OptionHelp="@this.OpusBitrateHelp" IsLocked="@this.IsOpusBitrateLocked"/>
|
||||
</ExpansionPanel>
|
||||
}
|
||||
|
||||
@ -36,7 +36,11 @@ public partial class SettingsPanelTranscription : SettingsPanelProviderBase
|
||||
var modelName = provider.Model.ToString();
|
||||
return modelName.Length > MAX_LENGTH ? "[...] " + modelName[^Math.Min(MAX_LENGTH, modelName.Length)..] : modelName;
|
||||
}
|
||||
|
||||
|
||||
private string OpusBitrateHelp => T("Higher bitrates can improve transcription accuracy, especially for quiet or noisy recordings, at the cost of a larger upload to the transcription provider. 128 kbps is recommended.");
|
||||
|
||||
private bool IsOpusBitrateLocked() => ManagedConfiguration.TryGet(x => x.App, x => x.OpusBitrate, out var meta) && meta.IsLocked;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
|
||||
@ -578,6 +578,15 @@ CONFIG["SETTINGS"] = {}
|
||||
-- Please note: using an empty string ("") will lock the selection and disable dictation/transcription.
|
||||
-- CONFIG["SETTINGS"]["DataApp.UseTranscriptionProvider"] = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
-- Configure the Opus bitrate used when normalizing uploaded audio/video for transcription.
|
||||
-- Allowed values are: KBPS_32, KBPS_64, KBPS_128, KBPS_256
|
||||
-- Higher bitrates improve transcription accuracy on noisy or quiet recordings, at the cost of
|
||||
-- a larger upload to the transcription provider. KBPS_128 is recommended.
|
||||
-- CONFIG["SETTINGS"]["DataApp.OpusBitrate"] = "KBPS_32"
|
||||
--
|
||||
-- Allow the user to change the Opus bitrate even though your organization set a default above:
|
||||
-- CONFIG["SETTINGS"]["DataApp.OpusBitrate.AllowUserOverride"] = true
|
||||
|
||||
-- Configure which assistants should be hidden from the UI.
|
||||
-- Allowed values are:
|
||||
-- GRAMMAR_SPELLING_ASSISTANT, ICON_FINDER_ASSISTANT, REWRITE_ASSISTANT,
|
||||
|
||||
@ -4995,6 +4995,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T16
|
||||
-- Uses the provider-configured model
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1760715963"] = "Verwendet das vom Anbieter konfigurierte Modell"
|
||||
|
||||
-- Higher bitrates can improve transcription accuracy, especially for quiet or noisy recordings, at the cost of a larger upload to the transcription provider. 128 kbps is recommended.
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T1859657826"] = "Höhere Bitraten können die Transkriptionsgenauigkeit verbessern, besonders bei ruhigen oder lauten Aufnahmen, zum Nachteil eines größeren Uploads an den Transkriptionsdienstleister. 128 kbps wird empfohlen."
|
||||
|
||||
-- Add Transcription Provider
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2066315685"] = "Anbieter für Transkriptionen hinzufügen"
|
||||
|
||||
@ -5007,6 +5010,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T21
|
||||
-- Name
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T266367750"] = "Name"
|
||||
|
||||
-- Transcription audio quality
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3103106744"] = "Aufzeichnungsqualität für die Transkription"
|
||||
|
||||
-- Edit
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T3267849393"] = "Bearbeiten"
|
||||
|
||||
@ -10494,6 +10500,21 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::THEMESEXTENSIONS::T4107955313"]
|
||||
-- Always use light theme
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::THEMESEXTENSIONS::T534715610"] = "Immer das helle Design verwenden"
|
||||
|
||||
-- 128 kbps (recommended)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T2152168180"] = "128 kbps (empfohlen)"
|
||||
|
||||
-- 256 kbps (largest upload, highest accuracy)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3092489829"] = "256 kbps (größter Upload, höchste Genauigkeit)"
|
||||
|
||||
-- Unknown
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3424652889"] = "Bitte geben Sie den zu übersetzenden Text an."
|
||||
|
||||
-- 64 kbps
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T3501477553"] = "64 kbps"
|
||||
|
||||
-- 32 kbps (smallest upload, lowest accuracy)
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::DATAMODEL::TRANSCRIPTIONOPUSBITRATEEXTENSIONS::T767394292"] = "32 kbps (kleinster Upload, geringste Genauigkeit)"
|
||||
|
||||
-- Use no profile
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::PROFILE::T2205839602"] = "Kein Profil verwenden"
|
||||
|
||||
|
||||
@ -323,4 +323,10 @@ public static class ConfigurationSelectDataFactory
|
||||
yield return new(level.GetName(), level);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<ConfigurationSelectData<TranscriptionOpusBitrate>> GetTranscriptionOpusBitrateData()
|
||||
{
|
||||
foreach (var bitrate in Enum.GetValues<TranscriptionOpusBitrate>())
|
||||
yield return new(bitrate.GetName(), bitrate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +112,11 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
||||
/// </summary>
|
||||
public string UseTranscriptionProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.UseTranscriptionProvider, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// The Opus bitrate used when normalizing uploaded audio/video for transcription.
|
||||
/// </summary>
|
||||
public TranscriptionOpusBitrate OpusBitrate { get; set; } = ManagedConfiguration.Register(configSelection, n => n.OpusBitrate, TranscriptionOpusBitrate.KBPS_128);
|
||||
|
||||
/// <summary>
|
||||
/// The global keyboard shortcut for toggling voice recording.
|
||||
/// Uses Tauri's shortcut format, e.g., "CmdOrControl+1" (Cmd+1 on macOS, Ctrl+1 on Windows/Linux).
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public enum TranscriptionOpusBitrate
|
||||
{
|
||||
KBPS_32,
|
||||
KBPS_64,
|
||||
KBPS_128,
|
||||
KBPS_256,
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
|
||||
namespace AIStudio.Settings.DataModel;
|
||||
|
||||
public static class TranscriptionOpusBitrateExtensions
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(TranscriptionOpusBitrateExtensions).Namespace, nameof(TranscriptionOpusBitrateExtensions));
|
||||
|
||||
public static string GetName(this TranscriptionOpusBitrate bitrate) => bitrate switch
|
||||
{
|
||||
TranscriptionOpusBitrate.KBPS_32 => TB("32 kbps (smallest upload, lowest accuracy)"),
|
||||
TranscriptionOpusBitrate.KBPS_64 => TB("64 kbps"),
|
||||
TranscriptionOpusBitrate.KBPS_128 => TB("128 kbps (recommended)"),
|
||||
TranscriptionOpusBitrate.KBPS_256 => TB("256 kbps (largest upload, highest accuracy)"),
|
||||
_ => TB("Unknown"),
|
||||
};
|
||||
|
||||
public static uint GetBitsPerSecond(this TranscriptionOpusBitrate bitrate) => bitrate switch
|
||||
{
|
||||
TranscriptionOpusBitrate.KBPS_32 => 32_000,
|
||||
TranscriptionOpusBitrate.KBPS_64 => 64_000,
|
||||
TranscriptionOpusBitrate.KBPS_128 => 128_000,
|
||||
TranscriptionOpusBitrate.KBPS_256 => 256_000,
|
||||
_ => 32_000,
|
||||
};
|
||||
}
|
||||
@ -398,6 +398,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT
|
||||
// Config: transcription provider?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.UseTranscriptionProvider, Guid.Empty, this.Id, settingsTable, dryRun);
|
||||
|
||||
// Config: transcription Opus bitrate?
|
||||
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.OpusBitrate, this.Id, settingsTable, dryRun);
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4,4 +4,5 @@ namespace AIStudio.Tools.Rust;
|
||||
/// <param name="InputPath">Absolute source media path.</param>
|
||||
/// <param name="OutputPath">Absolute operation-owned output path.</param>
|
||||
/// <param name="MaxPassThroughBytes">Optional pass-through size ceiling.</param>
|
||||
public sealed record CreateMediaJobRequest(string InputPath, string OutputPath, ulong? MaxPassThroughBytes = null);
|
||||
/// <param name="OpusBitrateBps">Optional target Opus encoder bitrate in bits per second.</param>
|
||||
public sealed record CreateMediaJobRequest(string InputPath, string OutputPath, ulong? MaxPassThroughBytes = null, uint? OpusBitrateBps = null);
|
||||
@ -1,6 +1,7 @@
|
||||
using AIStudio.Chat;
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Media;
|
||||
using AIStudio.Tools.PluginSystem;
|
||||
using AIStudio.Tools.Rust;
|
||||
@ -648,7 +649,8 @@ public sealed class MediaTranscriptionService(
|
||||
{
|
||||
// The quick POST is intentionally not cancelled: losing its response could orphan a job
|
||||
// whose ID the client never received. Cancellation is applied immediately after ownership.
|
||||
var jobId = await rustService.StartMediaJobAsync(mediaPath, normalizedPath, CancellationToken.None);
|
||||
var opusBitrateBps = settingsManager.ConfigurationData.App.OpusBitrate.GetBitsPerSecond();
|
||||
var jobId = await rustService.StartMediaJobAsync(mediaPath, normalizedPath, opusBitrateBps, CancellationToken.None);
|
||||
operation.JobId = jobId;
|
||||
|
||||
try
|
||||
|
||||
@ -10,13 +10,14 @@ public partial class RustService
|
||||
/// <summary>Starts a Rust media normalization job.</summary>
|
||||
/// <param name="inputPath">Absolute source path.</param>
|
||||
/// <param name="outputPath">Absolute operation-owned output path.</param>
|
||||
/// <param name="opusBitrateBps">Target Opus encoder bitrate in bits per second.</param>
|
||||
/// <param name="token">Request cancellation token.</param>
|
||||
/// <returns>The opaque runtime job identifier.</returns>
|
||||
public async Task<string> StartMediaJobAsync(string inputPath, string outputPath, CancellationToken token = default)
|
||||
public async Task<string> StartMediaJobAsync(string inputPath, string outputPath, uint opusBitrateBps, CancellationToken token = default)
|
||||
{
|
||||
using var response = await this.http.PostAsJsonAsync(
|
||||
"/media/jobs",
|
||||
new CreateMediaJobRequest(inputPath, outputPath),
|
||||
new CreateMediaJobRequest(inputPath, outputPath, OpusBitrateBps: opusBitrateBps),
|
||||
this.jsonRustSerializerOptions,
|
||||
token);
|
||||
|
||||
|
||||
@ -44,9 +44,6 @@ const OUTPUT_SAMPLE_RATE: u32 = 48_000;
|
||||
/// Number of samples in one 20 ms Opus frame at 48 kHz.
|
||||
const OPUS_FRAME_SAMPLES: usize = 960;
|
||||
|
||||
/// Target bitrate for mono speech-oriented Opus output.
|
||||
const OPUS_BITRATE: u32 = 32_000;
|
||||
|
||||
/// Stable normalized container name returned to upload clients.
|
||||
const OUTPUT_FORMAT: &str = "webm";
|
||||
|
||||
@ -62,6 +59,9 @@ const OPUS_PRE_SKIP: u16 = 312;
|
||||
/// Default size ceiling for copying an already-normalized file unchanged.
|
||||
const DEFAULT_MAX_PASS_THROUGH_BYTES: u64 = 25 * 1024 * 1024;
|
||||
|
||||
/// Default target bitrate for mono speech-oriented Opus output, used when a request omits it.
|
||||
const DEFAULT_OPUS_BITRATE_BPS: u32 = 128_000;
|
||||
|
||||
/// Bounded input block used for streaming resampling.
|
||||
const RESAMPLE_INPUT_BLOCK_SAMPLES: usize = 2_048;
|
||||
|
||||
@ -94,6 +94,9 @@ pub struct CreateMediaJobRequest {
|
||||
|
||||
/// Optional size ceiling for pass-through files.
|
||||
pub max_pass_through_bytes: Option<u64>,
|
||||
|
||||
/// Optional target Opus encoder bitrate in bits per second.
|
||||
pub opus_bitrate_bps: Option<u32>,
|
||||
}
|
||||
|
||||
/// Response returned immediately after a media job has been registered.
|
||||
@ -348,8 +351,9 @@ pub async fn create_job(
|
||||
let started_at = Instant::now();
|
||||
log::info!("media job registered: job_id={completed_job_id}");
|
||||
let max_pass_through_bytes = request.max_pass_through_bytes.unwrap_or(DEFAULT_MAX_PASS_THROUGH_BYTES);
|
||||
let opus_bitrate_bps = request.opus_bitrate_bps.unwrap_or(DEFAULT_OPUS_BITRATE_BPS);
|
||||
let task_job = Arc::clone(&job);
|
||||
let result = tokio::task::spawn_blocking(move || normalize_media(&input_path, &output_path, max_pass_through_bytes, &task_job)).await;
|
||||
let result = tokio::task::spawn_blocking(move || normalize_media(&input_path, &output_path, max_pass_through_bytes, opus_bitrate_bps, &task_job)).await;
|
||||
match result {
|
||||
Ok(Ok(result)) => {
|
||||
log::info!("media job completed: job_id={completed_job_id}, elapsed_ms={}", started_at.elapsed().as_millis());
|
||||
@ -515,7 +519,7 @@ impl MediaSource for CancellationMediaSource {
|
||||
}
|
||||
|
||||
/// Probes, normalizes, and atomically commits one media file.
|
||||
fn normalize_media(input_path: &FilePath, output_path: &FilePath, max_pass_through_bytes: u64, job: &MediaJob) -> Result<MediaJobResult, MediaError> {
|
||||
fn normalize_media(input_path: &FilePath, output_path: &FilePath, max_pass_through_bytes: u64, opus_bitrate_bps: u32, job: &MediaJob) -> Result<MediaJobResult, MediaError> {
|
||||
check_cancelled(job)?;
|
||||
|
||||
let detected = FileFormat::from_file(input_path)
|
||||
@ -670,6 +674,7 @@ fn normalize_media(input_path: &FilePath, output_path: &FilePath, max_pass_throu
|
||||
time_base: track_time_base,
|
||||
source_progress,
|
||||
job,
|
||||
opus_bitrate_bps,
|
||||
};
|
||||
transcode(&mut *format, context)
|
||||
};
|
||||
@ -892,6 +897,9 @@ struct TranscodeContext<'a> {
|
||||
|
||||
/// Cancellation and progress state for the job.
|
||||
job: &'a MediaJob,
|
||||
|
||||
/// Target Opus encoder bitrate in bits per second.
|
||||
opus_bitrate_bps: u32,
|
||||
}
|
||||
|
||||
/// Decodes a selected track and writes timestamp-aligned 20 ms mono Opus frames.
|
||||
@ -901,7 +909,7 @@ fn transcode(
|
||||
) -> Result<MediaJobResult, MediaError> {
|
||||
let mut decoder = StreamDecoder::new(&context.params, context.track_delay)?;
|
||||
let mut opus_encoder = OpusEncoder::builder(OUTPUT_SAMPLE_RATE, OpusChannels::Mono, Application::Audio)
|
||||
.bitrate(Bitrate::Bits(OPUS_BITRATE))
|
||||
.bitrate(Bitrate::Bits(context.opus_bitrate_bps))
|
||||
.vbr(true)
|
||||
.build()
|
||||
.map_err(|error| MediaError::new(MediaErrorCode::EncoderInitFailed, error.to_string()))?;
|
||||
@ -1647,7 +1655,7 @@ mod tests {
|
||||
/// Creates a temporary output and normalizes one checked-in fixture.
|
||||
fn normalize_fixture(name: &str) -> Result<(MediaJobResult, PathBuf), MediaError> {
|
||||
let output = std::env::temp_dir().join(format!("ai-studio-fixture-{}.webm", rand::random::<u64>()));
|
||||
let result = normalize_media(&fixtures().join(name), &output, DEFAULT_MAX_PASS_THROUGH_BYTES, &MediaJob::new())?;
|
||||
let result = normalize_media(&fixtures().join(name), &output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &MediaJob::new())?;
|
||||
Ok((result, output))
|
||||
}
|
||||
|
||||
@ -1804,7 +1812,7 @@ mod tests {
|
||||
let output = directory.join("output.webm");
|
||||
fs::write(&input, wav_silence(44_100, 4_410)).unwrap();
|
||||
let job = MediaJob::new();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, &job).unwrap();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &job).unwrap();
|
||||
assert!(!result.pass_through);
|
||||
assert_eq!(result.output_format, OUTPUT_FORMAT);
|
||||
assert_eq!(result.output_codec, OUTPUT_CODEC);
|
||||
@ -1832,7 +1840,7 @@ mod tests {
|
||||
fs::write(&input, wav_silence(48_000, 960)).unwrap();
|
||||
let job = MediaJob::new();
|
||||
job.cancelled.store(true, Ordering::Relaxed);
|
||||
let error = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, &job).unwrap_err();
|
||||
let error = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &job).unwrap_err();
|
||||
assert_eq!(error.code, MediaErrorCode::Cancelled);
|
||||
assert!(!output.exists());
|
||||
assert!(!partial_path(&output).exists());
|
||||
@ -1850,7 +1858,7 @@ mod tests {
|
||||
writer.write_packet(&[0xf8, 0xff, 0xfe], 0).unwrap();
|
||||
writer.finish().unwrap();
|
||||
let job = MediaJob::new();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, &job).unwrap();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &job).unwrap();
|
||||
assert!(result.pass_through);
|
||||
assert_eq!(result.output_format, OUTPUT_FORMAT);
|
||||
assert_eq!(result.output_codec, OUTPUT_CODEC);
|
||||
@ -1867,7 +1875,7 @@ mod tests {
|
||||
let input = directory.join("input.wav");
|
||||
let output = directory.join("output.webm");
|
||||
fs::write(&input, wav_constant(48_000, 960, 1_000)).unwrap();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, &MediaJob::new()).unwrap();
|
||||
let result = normalize_media(&input, &output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &MediaJob::new()).unwrap();
|
||||
assert!(result.has_audible_signal);
|
||||
let _ = fs::remove_dir_all(directory);
|
||||
}
|
||||
@ -1914,15 +1922,15 @@ mod tests {
|
||||
#[test]
|
||||
fn fixture_errors_are_stable() {
|
||||
let damaged_output = std::env::temp_dir().join(format!("ai-studio-damaged-{}.webm", rand::random::<u64>()));
|
||||
let damaged = normalize_media(&fixtures().join("damaged.bin"), &damaged_output, DEFAULT_MAX_PASS_THROUGH_BYTES, &MediaJob::new()).unwrap_err();
|
||||
let damaged = normalize_media(&fixtures().join("damaged.bin"), &damaged_output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &MediaJob::new()).unwrap_err();
|
||||
assert!(matches!(damaged.code, MediaErrorCode::UnknownFormat | MediaErrorCode::NotMedia | MediaErrorCode::DamagedContainer));
|
||||
|
||||
let no_audio_output = std::env::temp_dir().join(format!("ai-studio-no-audio-{}.webm", rand::random::<u64>()));
|
||||
let no_audio = normalize_media(&fixtures().join("no-audio.webm"), &no_audio_output, DEFAULT_MAX_PASS_THROUGH_BYTES, &MediaJob::new()).unwrap_err();
|
||||
let no_audio = normalize_media(&fixtures().join("no-audio.webm"), &no_audio_output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &MediaJob::new()).unwrap_err();
|
||||
assert_eq!(no_audio.code, MediaErrorCode::NoAudioTrack);
|
||||
|
||||
let unknown_output = std::env::temp_dir().join(format!("ai-studio-unknown-{}.webm", rand::random::<u64>()));
|
||||
let unknown = normalize_media(&fixtures().join("unknown-codec.mkv"), &unknown_output, DEFAULT_MAX_PASS_THROUGH_BYTES, &MediaJob::new()).unwrap_err();
|
||||
let unknown = normalize_media(&fixtures().join("unknown-codec.mkv"), &unknown_output, DEFAULT_MAX_PASS_THROUGH_BYTES, DEFAULT_OPUS_BITRATE_BPS, &MediaJob::new()).unwrap_err();
|
||||
assert_eq!(unknown.code, MediaErrorCode::UnsupportedCodec);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user