flv-output.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /******************************************************************************
  2. Copyright (C) 2023 by Lain Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include <stdio.h>
  15. #include <obs-module.h>
  16. #include <obs-avc.h>
  17. #ifdef ENABLE_HEVC
  18. #include "rtmp-hevc.h"
  19. #include <obs-hevc.h>
  20. #endif
  21. #include "rtmp-av1.h"
  22. #include <util/platform.h>
  23. #include <util/dstr.h>
  24. #include <util/threading.h>
  25. #include <inttypes.h>
  26. #include "flv-mux.h"
  27. #define do_log(level, format, ...) \
  28. blog(level, "[flv output: '%s'] " format, \
  29. obs_output_get_name(stream->output), ##__VA_ARGS__)
  30. #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
  31. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
  32. struct flv_output {
  33. obs_output_t *output;
  34. struct dstr path;
  35. FILE *file;
  36. volatile bool active;
  37. volatile bool stopping;
  38. uint64_t stop_ts;
  39. bool sent_headers;
  40. int64_t last_packet_ts;
  41. enum audio_id_t audio_codec[MAX_OUTPUT_AUDIO_ENCODERS];
  42. enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS];
  43. pthread_mutex_t mutex;
  44. bool got_first_video;
  45. int32_t start_dts_offset;
  46. };
  47. /* Adapted from FFmpeg's libavutil/pixfmt.h
  48. *
  49. * Renamed to make it apparent that these are not imported as this module does
  50. * not use or link against FFmpeg.
  51. */
  52. /* clang-format off */
  53. /**
  54. * Chromaticity Coordinates of the Source Primaries
  55. * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.1 and ITU-T H.273.
  56. */
  57. enum OBSColorPrimaries {
  58. OBSCOL_PRI_RESERVED0 = 0,
  59. OBSCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
  60. OBSCOL_PRI_UNSPECIFIED = 2,
  61. OBSCOL_PRI_RESERVED = 3,
  62. OBSCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
  63. OBSCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM
  64. OBSCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
  65. OBSCOL_PRI_SMPTE240M = 7, ///< identical to above, also called "SMPTE C" even though it uses D65
  66. OBSCOL_PRI_FILM = 8, ///< colour filters using Illuminant C
  67. OBSCOL_PRI_BT2020 = 9, ///< ITU-R BT2020
  68. OBSCOL_PRI_SMPTE428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ)
  69. OBSCOL_PRI_SMPTEST428_1 = OBSCOL_PRI_SMPTE428,
  70. OBSCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3
  71. OBSCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3
  72. OBSCOL_PRI_EBU3213 = 22, ///< EBU Tech. 3213-E (nothing there) / one of JEDEC P22 group phosphors
  73. OBSCOL_PRI_JEDEC_P22 = OBSCOL_PRI_EBU3213,
  74. OBSCOL_PRI_NB ///< Not part of ABI
  75. };
  76. /**
  77. * Color Transfer Characteristic
  78. * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.2.
  79. */
  80. enum OBSColorTransferCharacteristic {
  81. OBSCOL_TRC_RESERVED0 = 0,
  82. OBSCOL_TRC_BT709 = 1, ///< also ITU-R BT1361
  83. OBSCOL_TRC_UNSPECIFIED = 2,
  84. OBSCOL_TRC_RESERVED = 3,
  85. OBSCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
  86. OBSCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG
  87. OBSCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
  88. OBSCOL_TRC_SMPTE240M = 7,
  89. OBSCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics"
  90. OBSCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)"
  91. OBSCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
  92. OBSCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4
  93. OBSCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut
  94. OBSCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC)
  95. OBSCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system
  96. OBSCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system
  97. OBSCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems
  98. OBSCOL_TRC_SMPTEST2084 = OBSCOL_TRC_SMPTE2084,
  99. OBSCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1
  100. OBSCOL_TRC_SMPTEST428_1 = OBSCOL_TRC_SMPTE428,
  101. OBSCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma"
  102. OBSCOL_TRC_NB ///< Not part of ABI
  103. };
  104. /**
  105. * YUV Colorspace Type
  106. * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.3.
  107. */
  108. enum OBSColorSpace {
  109. OBSCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
  110. OBSCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / derived in SMPTE RP 177 Annex B
  111. OBSCOL_SPC_UNSPECIFIED = 2,
  112. OBSCOL_SPC_RESERVED = 3, ///< reserved for future use by ITU-T and ISO/IEC just like 15-255 are
  113. OBSCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
  114. OBSCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
  115. OBSCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
  116. OBSCOL_SPC_SMPTE240M = 7, ///< derived from 170M primaries and D65 white point, 170M is derived from BT470 System M's primaries
  117. OBSCOL_SPC_YCGCO = 8, ///< used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
  118. OBSCOL_SPC_YCOCG = OBSCOL_SPC_YCGCO,
  119. OBSCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system
  120. OBSCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system
  121. OBSCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x
  122. OBSCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system
  123. OBSCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system
  124. OBSCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp
  125. OBSCOL_SPC_NB ///< Not part of ABI
  126. };
  127. /* clang-format on */
  128. static inline bool stopping(struct flv_output *stream)
  129. {
  130. return os_atomic_load_bool(&stream->stopping);
  131. }
  132. static inline bool active(struct flv_output *stream)
  133. {
  134. return os_atomic_load_bool(&stream->active);
  135. }
  136. static const char *flv_output_getname(void *unused)
  137. {
  138. UNUSED_PARAMETER(unused);
  139. return obs_module_text("FLVOutput");
  140. }
  141. static void flv_output_stop(void *data, uint64_t ts);
  142. static void flv_output_destroy(void *data)
  143. {
  144. struct flv_output *stream = data;
  145. pthread_mutex_destroy(&stream->mutex);
  146. dstr_free(&stream->path);
  147. bfree(stream);
  148. }
  149. static void *flv_output_create(obs_data_t *settings, obs_output_t *output)
  150. {
  151. struct flv_output *stream = bzalloc(sizeof(struct flv_output));
  152. stream->output = output;
  153. pthread_mutex_init(&stream->mutex, NULL);
  154. UNUSED_PARAMETER(settings);
  155. return stream;
  156. }
  157. static int write_packet(struct flv_output *stream,
  158. struct encoder_packet *packet, bool is_header)
  159. {
  160. uint8_t *data;
  161. size_t size;
  162. int ret = 0;
  163. stream->last_packet_ts = get_ms_time(packet, packet->dts);
  164. flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, &data,
  165. &size, is_header);
  166. fwrite(data, 1, size, stream->file);
  167. bfree(data);
  168. return ret;
  169. }
  170. static int write_packet_ex(struct flv_output *stream,
  171. struct encoder_packet *packet, bool is_header,
  172. bool is_footer, size_t idx)
  173. {
  174. uint8_t *data;
  175. size_t size = 0;
  176. int ret = 0;
  177. if (is_header) {
  178. flv_packet_start(packet, stream->video_codec[idx], &data, &size,
  179. idx);
  180. } else if (is_footer) {
  181. flv_packet_end(packet, stream->video_codec[idx], &data, &size,
  182. idx);
  183. } else {
  184. flv_packet_frames(packet, stream->video_codec[idx],
  185. stream->start_dts_offset, &data, &size, idx);
  186. }
  187. fwrite(data, 1, size, stream->file);
  188. bfree(data);
  189. // manually created packets
  190. if (is_header || is_footer)
  191. bfree(packet->data);
  192. else
  193. obs_encoder_packet_release(packet);
  194. return ret;
  195. }
  196. static int write_audio_packet_ex(struct flv_output *stream,
  197. struct encoder_packet *packet, bool is_header,
  198. size_t idx)
  199. {
  200. uint8_t *data;
  201. size_t size = 0;
  202. int ret = 0;
  203. if (is_header) {
  204. flv_packet_audio_start(packet, stream->audio_codec[idx], &data,
  205. &size, idx);
  206. } else {
  207. flv_packet_audio_frames(packet, stream->audio_codec[idx],
  208. stream->start_dts_offset, &data, &size,
  209. idx);
  210. }
  211. fwrite(data, 1, size, stream->file);
  212. bfree(data);
  213. return ret;
  214. }
  215. static void write_meta_data(struct flv_output *stream)
  216. {
  217. uint8_t *meta_data;
  218. size_t meta_data_size;
  219. flv_meta_data(stream->output, &meta_data, &meta_data_size, true);
  220. fwrite(meta_data, 1, meta_data_size, stream->file);
  221. bfree(meta_data);
  222. }
  223. static bool write_audio_header(struct flv_output *stream, size_t idx)
  224. {
  225. obs_output_t *context = stream->output;
  226. obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, idx);
  227. struct encoder_packet packet = {.type = OBS_ENCODER_AUDIO,
  228. .timebase_den = 1};
  229. if (!aencoder)
  230. return false;
  231. if (obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) {
  232. if (idx == 0) {
  233. write_packet(stream, &packet, true);
  234. } else {
  235. write_audio_packet_ex(stream, &packet, true, idx);
  236. }
  237. }
  238. return true;
  239. }
  240. static bool write_video_header(struct flv_output *stream, size_t idx)
  241. {
  242. obs_output_t *context = stream->output;
  243. obs_encoder_t *vencoder = obs_output_get_video_encoder2(context, idx);
  244. uint8_t *header;
  245. size_t size;
  246. struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO,
  247. .timebase_den = 1,
  248. .keyframe = true};
  249. if (!vencoder)
  250. return false;
  251. if (!obs_encoder_get_extra_data(vencoder, &header, &size))
  252. return false;
  253. switch (stream->video_codec[idx]) {
  254. case CODEC_NONE:
  255. do_log(LOG_ERROR,
  256. "Codec not initialized for track %zu while sending header",
  257. idx);
  258. return false;
  259. case CODEC_H264:
  260. packet.size = obs_parse_avc_header(&packet.data, header, size);
  261. // Always send H.264 on track 0 as old style for compatibility.
  262. if (idx == 0) {
  263. write_packet(stream, &packet, true);
  264. } else {
  265. write_packet_ex(stream, &packet, true, false, idx);
  266. }
  267. return true;
  268. case CODEC_HEVC:
  269. #ifdef ENABLE_HEVC
  270. packet.size = obs_parse_hevc_header(&packet.data, header, size);
  271. break;
  272. #else
  273. return false;
  274. #endif
  275. case CODEC_AV1:
  276. packet.size = obs_parse_av1_header(&packet.data, header, size);
  277. break;
  278. }
  279. write_packet_ex(stream, &packet, true, false, idx);
  280. return true;
  281. }
  282. // only returns false if there's an error, not if no metadata needs to be sent
  283. static bool write_video_metadata(struct flv_output *stream, size_t idx)
  284. {
  285. // send metadata only if HDR
  286. obs_encoder_t *encoder =
  287. obs_output_get_video_encoder2(stream->output, idx);
  288. if (!encoder)
  289. return false;
  290. video_t *video = obs_encoder_video(encoder);
  291. if (!video)
  292. return false;
  293. const struct video_output_info *info = video_output_get_info(video);
  294. enum video_colorspace colorspace = info->colorspace;
  295. if (!(colorspace == VIDEO_CS_2100_PQ ||
  296. colorspace == VIDEO_CS_2100_HLG))
  297. return true;
  298. // Y2023 spec
  299. if (stream->video_codec[idx] == CODEC_H264)
  300. return true;
  301. uint8_t *data;
  302. size_t size;
  303. enum video_format format = info->format;
  304. int bits_per_raw_sample;
  305. switch (format) {
  306. case VIDEO_FORMAT_I010:
  307. case VIDEO_FORMAT_P010:
  308. case VIDEO_FORMAT_I210:
  309. bits_per_raw_sample = 10;
  310. break;
  311. case VIDEO_FORMAT_I412:
  312. case VIDEO_FORMAT_YA2L:
  313. bits_per_raw_sample = 12;
  314. break;
  315. default:
  316. bits_per_raw_sample = 8;
  317. }
  318. int pri = 0;
  319. int trc = 0;
  320. int spc = 0;
  321. switch (colorspace) {
  322. case VIDEO_CS_601:
  323. pri = OBSCOL_PRI_SMPTE170M;
  324. trc = OBSCOL_PRI_SMPTE170M;
  325. spc = OBSCOL_PRI_SMPTE170M;
  326. break;
  327. case VIDEO_CS_DEFAULT:
  328. case VIDEO_CS_709:
  329. pri = OBSCOL_PRI_BT709;
  330. trc = OBSCOL_PRI_BT709;
  331. spc = OBSCOL_PRI_BT709;
  332. break;
  333. case VIDEO_CS_SRGB:
  334. pri = OBSCOL_PRI_BT709;
  335. trc = OBSCOL_TRC_IEC61966_2_1;
  336. spc = OBSCOL_PRI_BT709;
  337. break;
  338. case VIDEO_CS_2100_PQ:
  339. pri = OBSCOL_PRI_BT2020;
  340. trc = OBSCOL_TRC_SMPTE2084;
  341. spc = OBSCOL_SPC_BT2020_NCL;
  342. break;
  343. case VIDEO_CS_2100_HLG:
  344. pri = OBSCOL_PRI_BT2020;
  345. trc = OBSCOL_TRC_ARIB_STD_B67;
  346. spc = OBSCOL_SPC_BT2020_NCL;
  347. }
  348. int max_luminance = 0;
  349. if (trc == OBSCOL_TRC_ARIB_STD_B67)
  350. max_luminance = 1000;
  351. else if (trc == OBSCOL_TRC_SMPTE2084)
  352. max_luminance = (int)obs_get_video_hdr_nominal_peak_level();
  353. flv_packet_metadata(stream->video_codec[idx], &data, &size,
  354. bits_per_raw_sample, pri, trc, spc, 0,
  355. max_luminance, idx);
  356. fwrite(data, 1, size, stream->file);
  357. bfree(data);
  358. return true;
  359. }
  360. static void write_headers(struct flv_output *stream)
  361. {
  362. for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
  363. obs_encoder_t *enc =
  364. obs_output_get_audio_encoder(stream->output, i);
  365. if (!enc)
  366. break;
  367. const char *codec = obs_encoder_get_codec(enc);
  368. stream->audio_codec[i] = to_audio_type(codec);
  369. }
  370. for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
  371. obs_encoder_t *enc =
  372. obs_output_get_video_encoder2(stream->output, i);
  373. if (!enc)
  374. break;
  375. const char *codec = obs_encoder_get_codec(enc);
  376. stream->video_codec[i] = to_video_type(codec);
  377. }
  378. write_meta_data(stream);
  379. write_audio_header(stream, 0);
  380. for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
  381. obs_encoder_t *enc =
  382. obs_output_get_video_encoder2(stream->output, i);
  383. if (!enc)
  384. continue;
  385. if (!write_video_header(stream, i) ||
  386. !write_video_metadata(stream, i))
  387. return;
  388. }
  389. for (size_t i = 1; write_audio_header(stream, i); i++)
  390. ;
  391. }
  392. static bool write_video_footer(struct flv_output *stream, size_t idx)
  393. {
  394. struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO,
  395. .timebase_den = 1,
  396. .keyframe = false};
  397. packet.size = 0;
  398. return write_packet_ex(stream, &packet, false, true, idx) >= 0;
  399. }
  400. static inline bool write_footers(struct flv_output *stream)
  401. {
  402. for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
  403. obs_encoder_t *encoder =
  404. obs_output_get_video_encoder2(stream->output, i);
  405. if (!encoder)
  406. continue;
  407. if (i == 0 && stream->video_codec[i] == CODEC_H264)
  408. continue;
  409. if (!write_video_footer(stream, i))
  410. return false;
  411. }
  412. return true;
  413. }
  414. static bool flv_output_start(void *data)
  415. {
  416. struct flv_output *stream = data;
  417. obs_data_t *settings;
  418. const char *path;
  419. if (!obs_output_can_begin_data_capture(stream->output, 0))
  420. return false;
  421. if (!obs_output_initialize_encoders(stream->output, 0))
  422. return false;
  423. stream->got_first_video = false;
  424. stream->sent_headers = false;
  425. os_atomic_set_bool(&stream->stopping, false);
  426. /* get path */
  427. settings = obs_output_get_settings(stream->output);
  428. path = obs_data_get_string(settings, "path");
  429. dstr_copy(&stream->path, path);
  430. obs_data_release(settings);
  431. stream->file = os_fopen(stream->path.array, "wb");
  432. if (!stream->file) {
  433. warn("Unable to open FLV file '%s'", stream->path.array);
  434. return false;
  435. }
  436. /* write headers and start capture */
  437. os_atomic_set_bool(&stream->active, true);
  438. obs_output_begin_data_capture(stream->output, 0);
  439. info("Writing FLV file '%s'...", stream->path.array);
  440. return true;
  441. }
  442. static void flv_output_stop(void *data, uint64_t ts)
  443. {
  444. struct flv_output *stream = data;
  445. stream->stop_ts = ts / 1000;
  446. os_atomic_set_bool(&stream->stopping, true);
  447. }
  448. static void flv_output_actual_stop(struct flv_output *stream, int code)
  449. {
  450. os_atomic_set_bool(&stream->active, false);
  451. if (stream->file) {
  452. write_footers(stream);
  453. write_file_info(stream->file, stream->last_packet_ts,
  454. os_ftelli64(stream->file));
  455. fclose(stream->file);
  456. }
  457. if (code) {
  458. obs_output_signal_stop(stream->output, code);
  459. } else {
  460. obs_output_end_data_capture(stream->output);
  461. }
  462. info("FLV file output complete");
  463. }
  464. static void flv_output_data(void *data, struct encoder_packet *packet)
  465. {
  466. struct flv_output *stream = data;
  467. struct encoder_packet parsed_packet;
  468. pthread_mutex_lock(&stream->mutex);
  469. if (!active(stream))
  470. goto unlock;
  471. if (!packet) {
  472. flv_output_actual_stop(stream, OBS_OUTPUT_ENCODE_ERROR);
  473. goto unlock;
  474. }
  475. if (stopping(stream)) {
  476. if (packet->sys_dts_usec >= (int64_t)stream->stop_ts) {
  477. flv_output_actual_stop(stream, 0);
  478. goto unlock;
  479. }
  480. }
  481. if (!stream->sent_headers) {
  482. write_headers(stream);
  483. stream->sent_headers = true;
  484. }
  485. if (packet->type == OBS_ENCODER_VIDEO) {
  486. if (!stream->got_first_video) {
  487. stream->start_dts_offset =
  488. get_ms_time(packet, packet->dts);
  489. stream->got_first_video = true;
  490. }
  491. switch (stream->video_codec[packet->track_idx]) {
  492. case CODEC_NONE:
  493. do_log(LOG_ERROR, "Codec not initialized for track %zu",
  494. packet->track_idx);
  495. goto unlock;
  496. case CODEC_H264:
  497. obs_parse_avc_packet(&parsed_packet, packet);
  498. break;
  499. case CODEC_HEVC:
  500. #ifdef ENABLE_HEVC
  501. obs_parse_hevc_packet(&parsed_packet, packet);
  502. break;
  503. #else
  504. goto unlock;
  505. #endif
  506. case CODEC_AV1:
  507. obs_parse_av1_packet(&parsed_packet, packet);
  508. break;
  509. }
  510. if (stream->video_codec[packet->track_idx] != CODEC_H264 ||
  511. (stream->video_codec[packet->track_idx] == CODEC_H264 &&
  512. packet->track_idx != 0)) {
  513. write_packet_ex(stream, &parsed_packet, false, false,
  514. packet->track_idx);
  515. } else {
  516. write_packet(stream, &parsed_packet, false);
  517. }
  518. obs_encoder_packet_release(&parsed_packet);
  519. } else {
  520. if (packet->track_idx != 0) {
  521. write_audio_packet_ex(stream, packet, false,
  522. packet->track_idx);
  523. } else {
  524. write_packet(stream, packet, false);
  525. }
  526. }
  527. unlock:
  528. pthread_mutex_unlock(&stream->mutex);
  529. }
  530. static obs_properties_t *flv_output_properties(void *unused)
  531. {
  532. UNUSED_PARAMETER(unused);
  533. obs_properties_t *props = obs_properties_create();
  534. obs_properties_add_text(props, "path",
  535. obs_module_text("FLVOutput.FilePath"),
  536. OBS_TEXT_DEFAULT);
  537. return props;
  538. }
  539. struct obs_output_info flv_output_info = {
  540. .id = "flv_output",
  541. .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_MULTI_TRACK_AV,
  542. #ifdef ENABLE_HEVC
  543. .encoded_video_codecs = "h264;hevc;av1",
  544. #else
  545. .encoded_video_codecs = "h264;av1",
  546. #endif
  547. .encoded_audio_codecs = "aac",
  548. .get_name = flv_output_getname,
  549. .create = flv_output_create,
  550. .destroy = flv_output_destroy,
  551. .start = flv_output_start,
  552. .stop = flv_output_stop,
  553. .encoded_packet = flv_output_data,
  554. .get_properties = flv_output_properties,
  555. };