Вы записываете аудио в мобильном приложении, отправляете на OpenAI Whisper API — и получаете ошибку 413 Request Entity Too Large или SocketTimeoutException из-за лимита 25 МБ и долгой обработки. Решение — чанкование и правильная настройка параметров. Наши инженеры, имеющие сертификаты Apple и Google, за 5 лет и более 50 проектов отладили процесс для iOS (Swift) и Android (Kotlin): от нарезки аудио до мультиязычной транскрипции с временными метками. Whisper API — Whisper (речевая система) — открытая модель OpenAI, доступная через REST API. Её точность на русском языке достигает 94% (WER ~6%), что близко к человеку. Однако интеграция требует учёта ограничений и специфики платформ: работа с аудиопотоками, форматами и фоновыми задачами — основа.
Обход лимита 25 МБ
Лимит POST /v1/audio/transcriptions — 25 МБ. Минута MP3 в 128 kbps занимает ~1 МБ, поэтому фрагмент до 25 минут. Для более длинных записей нужна нарезка. На прошлом проекте с аудиозаписями лекций чанкование сократило общее время транскрипции с 15 до 4 минут.
iOS: AVAssetExportSession с timeRange. Пример:
let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A)
exportSession?.timeRange = CMTimeRange(start: startTime, duration: chunkDuration)
Android: MediaExtractor + MediaMuxer — нарезка без перекодирования, если исходный кодек совместим (AAC в MP4). Для других кодеков — конвертация через MediaCodec в PCM → WAV.
Whisper API возвращает 503 при перегрузке. Экспоненциальный backoff с 3 попытками решает проблему в 99% случаев. На Android используйте OkHttp с перехватчиком retry, на iOS — URLSession с делегатом.
Форматы аудио и конвертация
Оптимальны MP3 и M4A (AAC). Убедитесь, что кодек внутри контейнера поддерживается: после AVAssetExportSession с пресетом AppleM4A всегда AAC, на Android безопаснее конвертировать в WAV. Параметр response_format=verbose_json возвращает текст с временными метками — это необходимо для синхронизации.
Какие параметры улучшают точность?
| Параметр | Рекомендация | Эффект |
|---|---|---|
| prompt | до 224 токенов контекста (например, термины предметной области) | Снижает WER на специализированных словах |
| temperature | 0 | Детерминированный вывод |
| language | явно указывать (например, "ru") | Ускоряет обработку |
Сравнение с альтернативами: Whisper API в 3 раза дешевле in-house модели при сопоставимом качестве, а по сравнению с Google Speech-to-Text экономит до 40% на облачных расходах.
Почему чанкование критично для мобильной транскрипции?
Без чанкования невозможно обработать длинные записи — лекции, интервью, диктофонные заметки. Нарезка на фрагменты по 25 МБ с перекрытием (overlap) в 1–2 секунды гарантирует, что на стыке не потеряются слова. Мы используем параллельную отправку фрагментов с помощью DispatchGroup на iOS и CoroutineScope на Android, что сокращает общее время транскрипции на 30%.
| Платформа | Инструмент нарезки | Перекрытие |
|---|---|---|
| iOS | AVAssetExportSession с timeRange | 1 секунда |
| Android | MediaExtractor + MediaMuxer | 2 секунды (зависит от кодека) |
Что входит в интеграцию
- Проектирование архитектуры записи и отправки
- Реализация на iOS (Swift) и Android (Kotlin)
- Обработка ошибок, ретраи, чанкование
- Настройка языка, промптов, формата verbose_json
- Документация и код-ревью
- Тестирование под нагрузкой
Этапы работы
- Анализ требований — определяем сценарии использования, частоту транскрипций, размер аудио.
- Прототип — реализуем базовую интеграцию на одной платформе за 2–3 дня.
- Интеграция и тестирование — добавляем чанкование, ретраи, обработку ошибок, мультиязычность.
- Деплой и мониторинг — настраиваем логирование, алерты при падении точности, обновляем промпты.
Реализация на iOS (Swift)
struct WhisperService {
private let apiKey: String
private let session = URLSession.shared
func transcribe(audioURL: URL, language: String = "ru") async throws -> String {
var request = URLRequest(url: URL(string: "https://api.openai.com/v1/audio/transcriptions")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"audio.m4a\"\r\n".data(using: .utf8)!)
body.append("Content-Type: audio/m4a\r\n\r\n".data(using: .utf8)!)
body.append(try Data(contentsOf: audioURL))
body.append("\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"model\"\r\n\r\nwhisper-1\r\n".data(using: .utf8)!)
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"language\"\r\n\r\n\(language)\r\n".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, _) = try await session.data(for: request)
let response = try JSONDecoder().decode(TranscriptionResponse.self, from: data)
return response.text
}
}
Реализация на Android (Kotlin)
suspend fun transcribe(file: File, language: String = "ru"): String {
val client = OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.build()
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.name, file.asRequestBody("audio/mp4".toMediaType()))
.addFormDataPart("model", "whisper-1")
.addFormDataPart("language", language)
.build()
val request = Request.Builder()
.url("https://api.openai.com/v1/audio/transcriptions")
.header("Authorization", "Bearer $apiKey")
.post(requestBody)
.build()
return withContext(Dispatchers.IO) {
client.newCall(request).execute().use { response ->
val json = response.body!!.string()
JSONObject(json).getString("text")
}
}
}
Типичные ошибки при интеграции
Загрузка Data(contentsOf:) целиком в память — на 100 МБ файле это OOM на бюджетных Android. Используйте file.asRequestBody() в OkHttp или InputStream-based upload в iOS. Отсутствие retry-логики: Whisper API периодически возвращает 503 — экспоненциальный backoff с 3 попытками решает проблему. Хранение API-ключа в клиенте — ключ должен передаваться через бэкенд.
Сроки и процесс
Базовая интеграция на одной платформе — 3–5 дней. С чанкованием, verbose_json и retry — 8–13 дней. Мультиязычность — отдельный этап. Свяжитесь с нами для оценки вашего проекта — подберём оптимальную архитектуру за 1 день. Получите консультацию по интеграции Whisper API уже сегодня.
Обработка аудио в фоне
Для длительных записей важно выполнять транскрипцию в фоновом режиме. На iOS используйте BGTaskScheduler, на Android — ForegroundService. Это позволяет пользователю сворачивать приложение без потери данных. Мы гарантируем стабильную работу даже при слабом интернете — если запрос не удался, мы автоматически повторяем его с экспоненциальной задержкой и уведомляем пользователя о прогрессе. Для запуска пилота свяжитесь с нами — предоставим тестовый доступ к API и поможем настроить архитектуру под ваш сценарий.







