- Implemented.

Safari/iPhone recording now uses MP4/AAC when WebM is unavailable.
WebM remains the default when supported and FFmpeg is disabled.
Added optional FFmpeg profiles: recommended M4A/AAC-LC or requested 
MP4/H.264 + AAC.
Added MIME-aware, playsinline audio playback.
Added dashboard controls under Plans & Platform → Voice Transcoding / 
FFmpeg.
Added Apache/Nginx media MIME configuration and documentation.
Core implementation: voice_transcoder.php (line 72), messages.php (line 
86), and cyberchat-app.js (line 635).
This commit is contained in:
Ty Clifford
2026-06-09 02:21:42 -04:00
parent ae0fb45c48
commit 6217a216a7
13 changed files with 325 additions and 21 deletions
+34
View File
@@ -2,6 +2,8 @@
function handleAdminPlatformAction(string $action): ?string {
if ($action === 'save_platform_services') {
$voiceProfile = (string)($_POST['voice_transcoding_profile'] ?? 'm4a_aac');
if (!in_array($voiceProfile, ['m4a_aac', 'mp4_h264_aac'], true)) $voiceProfile = 'm4a_aac';
setConfigValues([
'subscriptions.enabled' => !empty($_POST['subscriptions_enabled']),
'mailgun.api_key' => trim((string)($_POST['mailgun_api_key'] ?? '')),
@@ -13,6 +15,12 @@ function handleAdminPlatformAction(string $action): ?string {
'stripe.success_url' => trim((string)($_POST['stripe_success_url'] ?? '')),
'stripe.cancel_url' => trim((string)($_POST['stripe_cancel_url'] ?? '')),
'stripe.portal_return_url' => trim((string)($_POST['stripe_portal_return_url'] ?? '')),
'voice.transcoding.enabled' => !empty($_POST['voice_transcoding_enabled']),
'voice.transcoding.ffmpeg_path' => trim((string)($_POST['voice_ffmpeg_path'] ?? 'ffmpeg')) ?: 'ffmpeg',
'voice.transcoding.profile' => $voiceProfile,
'voice.transcoding.audio_bitrate_kbps' => max(48, min(320, (int)($_POST['voice_audio_bitrate'] ?? 128))),
'voice.transcoding.timeout_seconds' => max(5, min(300, (int)($_POST['voice_transcoding_timeout'] ?? 45))),
'voice.transcoding.fallback_to_source' => !empty($_POST['voice_transcoding_fallback']),
'database.mysql.enabled' => !empty($_POST['mysql_enabled']),
'database.mysql.auto_import' => !empty($_POST['mysql_auto_import']),
'database.mysql.dsn' => trim((string)($_POST['mysql_dsn'] ?? '')),
@@ -158,6 +166,32 @@ function renderAdminPlatformTab(): void {
<p class="dim">Webhook endpoint: <span class="mono"><?= esc(appBaseUrl() . '/api/stripe-webhook.php') ?></span></p>
</div></div>
<div class="panel"><div class="panel-head"><span class="panel-title">// VOICE TRANSCODING / FFMPEG</span></div><div class="panel-body">
<label class="cb-row"><input type="checkbox" name="voice_transcoding_enabled"
<?= getConfigVal('voice.transcoding.enabled', false) ? 'checked' : '' ?>>
<span>Transcode uploaded voice clips for broader playback compatibility</span></label>
<div class="form-row3">
<div class="field"><label>FFmpeg Binary</label><input name="voice_ffmpeg_path"
value="<?= esc(getConfigVal('voice.transcoding.ffmpeg_path', 'ffmpeg')) ?>" placeholder="/usr/bin/ffmpeg"></div>
<div class="field"><label>Output Profile</label><select name="voice_transcoding_profile">
<?php $voiceProfile = getConfigVal('voice.transcoding.profile', 'm4a_aac'); ?>
<option value="m4a_aac" <?= $voiceProfile === 'm4a_aac' ? 'selected' : '' ?>>M4A / AAC-LC (recommended)</option>
<option value="mp4_h264_aac" <?= $voiceProfile === 'mp4_h264_aac' ? 'selected' : '' ?>>MP4 / H.264 + AAC</option>
</select></div>
<div class="field"><label>Audio Bitrate (kbps)</label><input type="number" min="48" max="320"
name="voice_audio_bitrate" value="<?= (int)getConfigVal('voice.transcoding.audio_bitrate_kbps', 128) ?>"></div>
</div>
<div class="form-row">
<div class="field"><label>Conversion Timeout (seconds)</label><input type="number" min="5" max="300"
name="voice_transcoding_timeout" value="<?= (int)getConfigVal('voice.transcoding.timeout_seconds', 45) ?>"></div>
<label class="cb-row"><input type="checkbox" name="voice_transcoding_fallback"
<?= getConfigVal('voice.transcoding.fallback_to_source', true) ? 'checked' : '' ?>>
<span>Keep the original recording if FFmpeg fails</span></label>
</div>
<p class="dim">AAC-LC in an M4A container is the recommended audio-only profile for iPhone, Safari, Android, and desktop browsers.
The H.264 profile creates a tiny black video track plus AAC audio for systems that require a conventional MP4 file.</p>
</div></div>
<div class="panel"><div class="panel-head"><span class="panel-title">// OPTIONAL MYSQL / MARIADB MIRROR</span></div><div class="panel-body">
<label class="cb-row"><input type="checkbox" name="mysql_enabled" <?= getConfigVal('database.mysql.enabled', false) ? 'checked' : '' ?>><span>Enable MySQL mirror</span></label>
<label class="cb-row"><input type="checkbox" name="mysql_auto_import" <?= getConfigVal('database.mysql.auto_import', false) ? 'checked' : '' ?>><span>Automatically import all SQLite databases on the configured interval</span></label>