1
0

flv-output.c 18 KB

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