/* 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 #include #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 */