Files
gx fa6ab3069a Phase 7 audio mixing — attempt + rollback + lessons
Несколько сессий попыток реализовать audio mixing в композитор'е.
Не достигнуто sub-секундной latency со стабильным video+audio.
Откатано на parallel mode (cfc-grid video-only, live от pipeline с audio).

Полный набор выводов и pitfall'ов — docs/LESSONS-audio-mixing-attempts.md.

Главные lesson'ы для будущей попытки:
- mpegts mux libavformat авто-инсёртит h264_mp4toannexb BSF которому
  не нравится Annex-B + inline SPS/PPS — NVENC OUTPUT_SPSPPS per-frame ломает
- SPSC ring drop newest при full, не oldest (consumer's domain)
- av_new_packet (не av_malloc) для av_interleaved_write_frame ownership
- Monotonic PTS на counter (frame_idx, total_samples) — не wallclock
- mediamtx env-var path names не должны иметь '-' (parser limitation)
- Default mediamtx ReadTimeout=10s короткий для burst write'ов

Изменения в repo сохранены для будущей доработки:
- src/writer.c — mpegts backend с audio stream support
- src/audio.c — RTSP AAC consumer + lock-free SPSC ring
- include/cuframes_composer/{writer,audio}.h — public API
- examples/grid_record.c — --format=mpegts + --audio-source flags
- include/cuframes_composer/composer.h — consumer_prefix field
- docker/Dockerfile — libavformat-dev добавлен в builder/runtime

cfc-grid composer стабильно работает на видео (substantially лучше
монолитного pipeline'а с audio bag'ом). TV рекомендуется использовать
rtsp://...:554/cfc-grid + опционально rtsp://...:554/live-audio
parallel.
2026-06-03 14:29:56 +01:00

93 lines
3.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* cuframes-composer — output writer abstraction.
*
* Энкодер NVENC отдаёт сжатый H.264 bitstream (Annex-B byte stream) +
* timestamp в callback'е. Writer берёт эту последовательность и пишет
* либо как raw H.264 в файл/stdout, либо как mpegts container с правильными
* PTS/DTS в header'ах PES packet'ов.
*
* Зачем mpegts:
* Raw H.264 в pipe не содержит timestamps на container-уровне; downstream
* ffmpeg-mux вынужден синтезировать PTS из `-r` или wallclock, что вызывает
* desync с audio и drops при mux'ировании с другим потоком. MPEG-TS даёт
* нативные PTS/DTS на каждом PES packet'е → downstream mux работает
* через `-c copy` без проблем.
*
* Форматы:
* "h264" — raw H.264 Annex-B byte stream (как до Phase 6); fwrite в file
* "mpegts" — MPEG-TS container; libavformat avformat_write_header +
* av_interleaved_write_frame
*
* Path semantics:
* "/path/to/file.h264" — обычный файл
* "-" / "pipe:1" / "/dev/stdout" — stdout (для shell-pipe в downstream)
*
* Лицензия: LGPL-2.1+
*/
#ifndef CUFRAMES_COMPOSER_WRITER_H
#define CUFRAMES_COMPOSER_WRITER_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct cfc_writer cfc_writer_t;
typedef struct cfc_writer_config {
const char *path; /* "/path/file.ts", "unix:/run/.../sock", "-" */
const char *format; /* "h264" или "mpegts" */
int width, height; /* для mpegts video codecpar */
int fps_num, fps_den; /* для mpegts time_base hint */
int bitrate_kbps; /* для mpegts mux PCR pacing */
/* SPS/PPS — video codec extradata, кладётся в codecpar до write_header.
* Берётся из cfc_encoder_get_sequence_params. Обязательно для mpegts —
* mpegts muxer кладёт SPS/PPS перед первым IDR из extradata. */
const uint8_t *extradata;
size_t extradata_size;
/* Audio stream — если задан, в mpegts будет второй stream (AAC).
* extradata_audio = AudioSpecificConfig (2 байта обычно). */
int has_audio; /* 0 = video-only, 1 = add audio stream */
int audio_sample_rate; /* например 44100 */
int audio_channels; /* например 2 */
const uint8_t *audio_extradata; /* ASC bytes */
size_t audio_extradata_size;
} cfc_writer_config_t;
int cfc_writer_create(const cfc_writer_config_t *cfg, cfc_writer_t **out);
/* Записать один закодированный video frame. is_keyframe ставит флаг
* AV_PKT_FLAG_KEY. Использует av_write_frame (без интерливинг-очереди). */
int cfc_writer_write(
cfc_writer_t *w,
const uint8_t *bitstream,
size_t size,
int64_t pts_ns,
int is_keyframe
);
/* Записать audio packet (AAC ADTS либо raw — определяется extradata).
* Возвращает 0 при успехе. Thread-safe относительно write video?
* Нет — caller обязан вызывать оба write_* из одного thread'а либо
* локироваться. В нашей архитектуре video-thread drain'ит audio-ring
* и пишет сюда сам. */
int cfc_writer_write_audio(
cfc_writer_t *w,
const uint8_t *aac_data,
size_t size,
int64_t pts_ns
);
/* Закрыть writer. Для mpegts вызывает av_write_trailer. */
int cfc_writer_close(cfc_writer_t *w);
#ifdef __cplusplus
}
#endif
#endif /* CUFRAMES_COMPOSER_WRITER_H */