Дороблення відеоплеєра ffmpeg
Реалізація
Код цього прикладу вийшов досить великий, я вирішив не наводити його цілком. Покажу лише важливі моменти. Весь код можна переглянути за посиланням наприкінці статті.
Насамперед об'єднаємо всі основні змінні в один загальний контекст:
Відтворення аудіо
Звук на комп'ютері є безперервний потіксемплів. Кожен семпл це значення форми хвилі. Звуки записуються з певною частотою дискретизації, і відтворюватися повинні з такою ж частотою.Частота дискретизаціїє число семплів в секунду. Наприклад, 44100 семпла на секунду - частота дискретизації аудіо CD. Крім того, аудіо можуть містити декілька каналів. Наприклад, для стерео семпли приходитимуть по два за раз. При отриманні даних із файлу невідомо скільки семплів буде отримано, але FFmpeg не віддаватиме неповних семплів. Також це означає, що FFmpeg не буде розділяти стерео-семпли.
Насамперед треба налаштувати SDL для виведення аудіо. У функцію ініціалізації необхідно додати прапорSDL_INIT_AUDIO. Потім заповнити структуруSDL_AudioSpecі передати її у функціюSDL_OpenAudio:
SDL для виведення аудіо використовує виклик callback функції. Структура має такі параметри:
- freq: Частота дискретизації.
- format: Формат даних, що передаються. Символ "S" в "AUDIO_S16SYS" означає, що дані будуть знакові, 16 - розмір семпла дорівнює 16-ти бітам, "SYS" - використовується системний порядок байтів. Саме у цьому форматі FFmpeg повертає декодовані дані.
- channels: Кількість аудіо каналів.
- silence: Значення "тиші". Для знакових даних зазвичай використовується 0.
- samples: Розмір аудіо-буфераSDL. Нормальними значеннями вважаються значення від 512 до 8192 байт. Ми будемо використовувати 1024.
- callback: callback-функція заповнення буфера даними.
- userdata: Користувальницькі дані, що передаються у функцію зворотного дзвінка. Використовуємо наш основний контекст.
Декодування аудіо
Як такий буфер будемо використовувати кільцевий буфер. Прототипи основних функцій:
Призначення аргументів має бути зрозуміле з назви. Аргументblockвказує, чи повинна функція блокуватися у разі нестачі місця в буфері або відсутності даних для читання.
Отже, повністю функція декодування виглядає так:
Декодування аудіо-пакета здійснюється функцієюavcodec_decode_audio4, якщо декодований цілий кадр (прапорgot_frame), визначаємо розмір буфера в байтах за допомогою функціїav_samples_get_buffer_sizeі записи наш кільцевий буфер.
Відтворення аудіо
Залишилося зовсім трохи, а саме відтворити декодовані семпли. Це робиться в callback-функціїaudio_callback:
Тут все просто. Дістаємо з буфераlenбайт і зберігаємо в наданому буфері SDL.
Синхронізація
До нашого основного контексту додамо такі поля:
У потоці декодування аудіо збережемо частоту аудіо, щоб потім синхронізуватися до неї:
Ну і «серце» нашої синхронізації - це функціяcompute_delay:
Спочатку розраховуємо затримку між попереднім та поточним кадром та зберігаємо поточні значення. Після цього враховуємо можливу розсинхронізаціюз аудіо та розраховуємо тривалість необхідної затримки до наступного кадру.
Висновок
У цій частині ми закінчили розробку найпростішого плеєра, покращили структуру програми, додали відтворення аудіо та синхронізацію. З нерозглянутих тем залишилися: перемотування, прискорене/уповільнене відтворення, інші варіанти синхронізації. Є ще зовсім окрема тема кодування та мультиплексування. Можливо, її я й намагатимусь розглянути в наступній статті.
Всім дякую за увагу!
Хардкорна конфа за С++. Ми запрошуємо лише профі.