| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 | 
							- /******************************************************************************
 
-     Copyright (C) 2023 by Lain Bailey <[email protected]>
 
-     This program is free software: you can redistribute it and/or modify
 
-     it under the terms of the GNU General Public License as published by
 
-     the Free Software Foundation, either version 2 of the License, or
 
-     (at your option) any later version.
 
-     This program is distributed in the hope that it will be useful,
 
-     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
-     GNU General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
 
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
- ******************************************************************************/
 
- #include <stdio.h>
 
- #include <obs-module.h>
 
- #include <obs-avc.h>
 
- #ifdef ENABLE_HEVC
 
- #include "rtmp-hevc.h"
 
- #include <obs-hevc.h>
 
- #endif
 
- #include "rtmp-av1.h"
 
- #include <util/platform.h>
 
- #include <util/dstr.h>
 
- #include <util/threading.h>
 
- #include <inttypes.h>
 
- #include "flv-mux.h"
 
- #define do_log(level, format, ...)                \
 
- 	blog(level, "[flv output: '%s'] " format, \
 
- 	     obs_output_get_name(stream->output), ##__VA_ARGS__)
 
- #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
 
- #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
 
- struct flv_output {
 
- 	obs_output_t *output;
 
- 	struct dstr path;
 
- 	FILE *file;
 
- 	volatile bool active;
 
- 	volatile bool stopping;
 
- 	uint64_t stop_ts;
 
- 	bool sent_headers;
 
- 	int64_t last_packet_ts;
 
- 	enum audio_id_t audio_codec[MAX_OUTPUT_AUDIO_ENCODERS];
 
- 	enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS];
 
- 	pthread_mutex_t mutex;
 
- 	bool got_first_packet;
 
- 	int32_t start_dts_offset;
 
- };
 
- /* Adapted from FFmpeg's libavutil/pixfmt.h
 
-  *
 
-  * Renamed to make it apparent that these are not imported as this module does
 
-  * not use or link against FFmpeg.
 
-  */
 
- /* clang-format off */
 
- /**
 
-  * Chromaticity Coordinates of the Source Primaries
 
-  * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.1 and ITU-T H.273.
 
-  */
 
- enum OBSColorPrimaries {
 
- 	OBSCOL_PRI_RESERVED0    =  0,
 
- 	OBSCOL_PRI_BT709        =  1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
 
- 	OBSCOL_PRI_UNSPECIFIED  =  2,
 
- 	OBSCOL_PRI_RESERVED     =  3,
 
- 	OBSCOL_PRI_BT470M       =  4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
 
- 	OBSCOL_PRI_BT470BG      =  5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM
 
- 	OBSCOL_PRI_SMPTE170M    =  6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
 
- 	OBSCOL_PRI_SMPTE240M    =  7, ///< identical to above, also called "SMPTE C" even though it uses D65
 
- 	OBSCOL_PRI_FILM         =  8, ///< colour filters using Illuminant C
 
- 	OBSCOL_PRI_BT2020       =  9, ///< ITU-R BT2020
 
- 	OBSCOL_PRI_SMPTE428     = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ)
 
- 	OBSCOL_PRI_SMPTEST428_1 = OBSCOL_PRI_SMPTE428,
 
- 	OBSCOL_PRI_SMPTE431     = 11, ///< SMPTE ST 431-2 (2011) / DCI P3
 
- 	OBSCOL_PRI_SMPTE432     = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3
 
- 	OBSCOL_PRI_EBU3213      = 22, ///< EBU Tech. 3213-E (nothing there) / one of JEDEC P22 group phosphors
 
- 	OBSCOL_PRI_JEDEC_P22    = OBSCOL_PRI_EBU3213,
 
- 	OBSCOL_PRI_NB                 ///< Not part of ABI
 
- };
 
- /**
 
-  * Color Transfer Characteristic
 
-  * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.2.
 
-  */
 
- enum OBSColorTransferCharacteristic {
 
- 	OBSCOL_TRC_RESERVED0    =  0,
 
- 	OBSCOL_TRC_BT709        =  1, ///< also ITU-R BT1361
 
- 	OBSCOL_TRC_UNSPECIFIED  =  2,
 
- 	OBSCOL_TRC_RESERVED     =  3,
 
- 	OBSCOL_TRC_GAMMA22      =  4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
 
- 	OBSCOL_TRC_GAMMA28      =  5, ///< also ITU-R BT470BG
 
- 	OBSCOL_TRC_SMPTE170M    =  6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
 
- 	OBSCOL_TRC_SMPTE240M    =  7,
 
- 	OBSCOL_TRC_LINEAR       =  8, ///< "Linear transfer characteristics"
 
- 	OBSCOL_TRC_LOG          =  9, ///< "Logarithmic transfer characteristic (100:1 range)"
 
- 	OBSCOL_TRC_LOG_SQRT     = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
 
- 	OBSCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4
 
- 	OBSCOL_TRC_BT1361_ECG   = 12, ///< ITU-R BT1361 Extended Colour Gamut
 
- 	OBSCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC)
 
- 	OBSCOL_TRC_BT2020_10    = 14, ///< ITU-R BT2020 for 10-bit system
 
- 	OBSCOL_TRC_BT2020_12    = 15, ///< ITU-R BT2020 for 12-bit system
 
- 	OBSCOL_TRC_SMPTE2084    = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems
 
- 	OBSCOL_TRC_SMPTEST2084  = OBSCOL_TRC_SMPTE2084,
 
- 	OBSCOL_TRC_SMPTE428     = 17, ///< SMPTE ST 428-1
 
- 	OBSCOL_TRC_SMPTEST428_1 = OBSCOL_TRC_SMPTE428,
 
- 	OBSCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma"
 
- 	OBSCOL_TRC_NB                 ///< Not part of ABI
 
- };
 
- /**
 
-  * YUV Colorspace Type
 
-  * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.3.
 
-  */
 
- enum OBSColorSpace {
 
- 	OBSCOL_SPC_RGB                =  0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
 
- 	OBSCOL_SPC_BT709              =  1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / derived in SMPTE RP 177 Annex B
 
- 	OBSCOL_SPC_UNSPECIFIED        =  2,
 
- 	OBSCOL_SPC_RESERVED           =  3, ///< reserved for future use by ITU-T and ISO/IEC just like 15-255 are
 
- 	OBSCOL_SPC_FCC                =  4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
 
- 	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
 
- 	OBSCOL_SPC_SMPTE170M          =  6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
 
- 	OBSCOL_SPC_SMPTE240M          =  7, ///< derived from 170M primaries and D65 white point, 170M is derived from BT470 System M's primaries
 
- 	OBSCOL_SPC_YCGCO              =  8, ///< used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
 
- 	OBSCOL_SPC_YCOCG              =  OBSCOL_SPC_YCGCO,
 
- 	OBSCOL_SPC_BT2020_NCL         =  9, ///< ITU-R BT2020 non-constant luminance system
 
- 	OBSCOL_SPC_BT2020_CL          = 10, ///< ITU-R BT2020 constant luminance system
 
- 	OBSCOL_SPC_SMPTE2085          = 11, ///< SMPTE 2085, Y'D'zD'x
 
- 	OBSCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system
 
- 	OBSCOL_SPC_CHROMA_DERIVED_CL  = 13, ///< Chromaticity-derived constant luminance system
 
- 	OBSCOL_SPC_ICTCP              = 14, ///< ITU-R BT.2100-0, ICtCp
 
- 	OBSCOL_SPC_NB                       ///< Not part of ABI
 
- };
 
- /* clang-format on */
 
- static inline bool stopping(struct flv_output *stream)
 
- {
 
- 	return os_atomic_load_bool(&stream->stopping);
 
- }
 
- static inline bool active(struct flv_output *stream)
 
- {
 
- 	return os_atomic_load_bool(&stream->active);
 
- }
 
- static const char *flv_output_getname(void *unused)
 
- {
 
- 	UNUSED_PARAMETER(unused);
 
- 	return obs_module_text("FLVOutput");
 
- }
 
- static void flv_output_stop(void *data, uint64_t ts);
 
- static void flv_output_destroy(void *data)
 
- {
 
- 	struct flv_output *stream = data;
 
- 	pthread_mutex_destroy(&stream->mutex);
 
- 	dstr_free(&stream->path);
 
- 	bfree(stream);
 
- }
 
- static void *flv_output_create(obs_data_t *settings, obs_output_t *output)
 
- {
 
- 	struct flv_output *stream = bzalloc(sizeof(struct flv_output));
 
- 	stream->output = output;
 
- 	pthread_mutex_init(&stream->mutex, NULL);
 
- 	UNUSED_PARAMETER(settings);
 
- 	return stream;
 
- }
 
- static int write_packet(struct flv_output *stream,
 
- 			struct encoder_packet *packet, bool is_header)
 
- {
 
- 	uint8_t *data;
 
- 	size_t size;
 
- 	int ret = 0;
 
- 	stream->last_packet_ts = get_ms_time(packet, packet->dts);
 
- 	flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, &data,
 
- 		       &size, is_header);
 
- 	fwrite(data, 1, size, stream->file);
 
- 	bfree(data);
 
- 	return ret;
 
- }
 
- static int write_packet_ex(struct flv_output *stream,
 
- 			   struct encoder_packet *packet, bool is_header,
 
- 			   bool is_footer, size_t idx)
 
- {
 
- 	uint8_t *data;
 
- 	size_t size = 0;
 
- 	int ret = 0;
 
- 	if (is_header) {
 
- 		flv_packet_start(packet, stream->video_codec[idx], &data, &size,
 
- 				 idx);
 
- 	} else if (is_footer) {
 
- 		flv_packet_end(packet, stream->video_codec[idx], &data, &size,
 
- 			       idx);
 
- 	} else {
 
- 		flv_packet_frames(packet, stream->video_codec[idx],
 
- 				  stream->start_dts_offset, &data, &size, idx);
 
- 	}
 
- 	fwrite(data, 1, size, stream->file);
 
- 	bfree(data);
 
- 	// manually created packets
 
- 	if (is_header || is_footer)
 
- 		bfree(packet->data);
 
- 	else
 
- 		obs_encoder_packet_release(packet);
 
- 	return ret;
 
- }
 
- static int write_audio_packet_ex(struct flv_output *stream,
 
- 				 struct encoder_packet *packet, bool is_header,
 
- 				 size_t idx)
 
- {
 
- 	uint8_t *data;
 
- 	size_t size = 0;
 
- 	int ret = 0;
 
- 	if (is_header) {
 
- 		flv_packet_audio_start(packet, stream->audio_codec[idx], &data,
 
- 				       &size, idx);
 
- 	} else {
 
- 		flv_packet_audio_frames(packet, stream->audio_codec[idx],
 
- 					stream->start_dts_offset, &data, &size,
 
- 					idx);
 
- 	}
 
- 	fwrite(data, 1, size, stream->file);
 
- 	bfree(data);
 
- 	return ret;
 
- }
 
- static void write_meta_data(struct flv_output *stream)
 
- {
 
- 	uint8_t *meta_data;
 
- 	size_t meta_data_size;
 
- 	flv_meta_data(stream->output, &meta_data, &meta_data_size, true);
 
- 	fwrite(meta_data, 1, meta_data_size, stream->file);
 
- 	bfree(meta_data);
 
- }
 
- static bool write_audio_header(struct flv_output *stream, size_t idx)
 
- {
 
- 	obs_output_t *context = stream->output;
 
- 	obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, idx);
 
- 	struct encoder_packet packet = {.type = OBS_ENCODER_AUDIO,
 
- 					.timebase_den = 1};
 
- 	if (!aencoder)
 
- 		return false;
 
- 	if (obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) {
 
- 		if (idx == 0) {
 
- 			write_packet(stream, &packet, true);
 
- 		} else {
 
- 			write_audio_packet_ex(stream, &packet, true, idx);
 
- 		}
 
- 	}
 
- 	return true;
 
- }
 
- static bool write_video_header(struct flv_output *stream, size_t idx)
 
- {
 
- 	obs_output_t *context = stream->output;
 
- 	obs_encoder_t *vencoder = obs_output_get_video_encoder2(context, idx);
 
- 	uint8_t *header;
 
- 	size_t size;
 
- 	struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO,
 
- 					.timebase_den = 1,
 
- 					.keyframe = true};
 
- 	if (!vencoder)
 
- 		return false;
 
- 	if (!obs_encoder_get_extra_data(vencoder, &header, &size))
 
- 		return false;
 
- 	switch (stream->video_codec[idx]) {
 
- 	case CODEC_NONE:
 
- 		do_log(LOG_ERROR,
 
- 		       "Codec not initialized for track %zu while sending header",
 
- 		       idx);
 
- 		return false;
 
- 	case CODEC_H264:
 
- 		packet.size = obs_parse_avc_header(&packet.data, header, size);
 
- 		// Always send H.264 on track 0 as old style for compatibility.
 
- 		if (idx == 0) {
 
- 			write_packet(stream, &packet, true);
 
- 		} else {
 
- 			write_packet_ex(stream, &packet, true, false, idx);
 
- 		}
 
- 		return true;
 
- 	case CODEC_HEVC:
 
- #ifdef ENABLE_HEVC
 
- 		packet.size = obs_parse_hevc_header(&packet.data, header, size);
 
- 		break;
 
- #else
 
- 		return false;
 
- #endif
 
- 	case CODEC_AV1:
 
- 		packet.size = obs_parse_av1_header(&packet.data, header, size);
 
- 		break;
 
- 	}
 
- 	write_packet_ex(stream, &packet, true, false, idx);
 
- 	return true;
 
- }
 
- // only returns false if there's an error, not if no metadata needs to be sent
 
- static bool write_video_metadata(struct flv_output *stream, size_t idx)
 
- {
 
- 	// send metadata only if HDR
 
- 	obs_encoder_t *encoder =
 
- 		obs_output_get_video_encoder2(stream->output, idx);
 
- 	if (!encoder)
 
- 		return false;
 
- 	video_t *video = obs_encoder_video(encoder);
 
- 	if (!video)
 
- 		return false;
 
- 	const struct video_output_info *info = video_output_get_info(video);
 
- 	enum video_colorspace colorspace = info->colorspace;
 
- 	if (!(colorspace == VIDEO_CS_2100_PQ ||
 
- 	      colorspace == VIDEO_CS_2100_HLG))
 
- 		return true;
 
- 	// Y2023 spec
 
- 	if (stream->video_codec[idx] == CODEC_H264)
 
- 		return true;
 
- 	uint8_t *data;
 
- 	size_t size;
 
- 	enum video_format format = info->format;
 
- 	int bits_per_raw_sample;
 
- 	switch (format) {
 
- 	case VIDEO_FORMAT_I010:
 
- 	case VIDEO_FORMAT_P010:
 
- 	case VIDEO_FORMAT_I210:
 
- 		bits_per_raw_sample = 10;
 
- 		break;
 
- 	case VIDEO_FORMAT_I412:
 
- 	case VIDEO_FORMAT_YA2L:
 
- 		bits_per_raw_sample = 12;
 
- 		break;
 
- 	default:
 
- 		bits_per_raw_sample = 8;
 
- 	}
 
- 	int pri = 0;
 
- 	int trc = 0;
 
- 	int spc = 0;
 
- 	switch (colorspace) {
 
- 	case VIDEO_CS_601:
 
- 		pri = OBSCOL_PRI_SMPTE170M;
 
- 		trc = OBSCOL_PRI_SMPTE170M;
 
- 		spc = OBSCOL_PRI_SMPTE170M;
 
- 		break;
 
- 	case VIDEO_CS_DEFAULT:
 
- 	case VIDEO_CS_709:
 
- 		pri = OBSCOL_PRI_BT709;
 
- 		trc = OBSCOL_PRI_BT709;
 
- 		spc = OBSCOL_PRI_BT709;
 
- 		break;
 
- 	case VIDEO_CS_SRGB:
 
- 		pri = OBSCOL_PRI_BT709;
 
- 		trc = OBSCOL_TRC_IEC61966_2_1;
 
- 		spc = OBSCOL_PRI_BT709;
 
- 		break;
 
- 	case VIDEO_CS_2100_PQ:
 
- 		pri = OBSCOL_PRI_BT2020;
 
- 		trc = OBSCOL_TRC_SMPTE2084;
 
- 		spc = OBSCOL_SPC_BT2020_NCL;
 
- 		break;
 
- 	case VIDEO_CS_2100_HLG:
 
- 		pri = OBSCOL_PRI_BT2020;
 
- 		trc = OBSCOL_TRC_ARIB_STD_B67;
 
- 		spc = OBSCOL_SPC_BT2020_NCL;
 
- 	}
 
- 	int max_luminance = 0;
 
- 	if (trc == OBSCOL_TRC_ARIB_STD_B67)
 
- 		max_luminance = 1000;
 
- 	else if (trc == OBSCOL_TRC_SMPTE2084)
 
- 		max_luminance = (int)obs_get_video_hdr_nominal_peak_level();
 
- 	flv_packet_metadata(stream->video_codec[idx], &data, &size,
 
- 			    bits_per_raw_sample, pri, trc, spc, 0,
 
- 			    max_luminance, idx);
 
- 	fwrite(data, 1, size, stream->file);
 
- 	bfree(data);
 
- 	return true;
 
- }
 
- static void write_headers(struct flv_output *stream)
 
- {
 
- 	for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
 
- 		obs_encoder_t *enc =
 
- 			obs_output_get_audio_encoder(stream->output, i);
 
- 		if (!enc)
 
- 			break;
 
- 		const char *codec = obs_encoder_get_codec(enc);
 
- 		stream->audio_codec[i] = to_audio_type(codec);
 
- 	}
 
- 	for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
 
- 		obs_encoder_t *enc =
 
- 			obs_output_get_video_encoder2(stream->output, i);
 
- 		if (!enc)
 
- 			break;
 
- 		const char *codec = obs_encoder_get_codec(enc);
 
- 		stream->video_codec[i] = to_video_type(codec);
 
- 	}
 
- 	write_meta_data(stream);
 
- 	write_audio_header(stream, 0);
 
- 	for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
 
- 		obs_encoder_t *enc =
 
- 			obs_output_get_video_encoder2(stream->output, i);
 
- 		if (!enc)
 
- 			continue;
 
- 		if (!write_video_header(stream, i) ||
 
- 		    !write_video_metadata(stream, i))
 
- 			return;
 
- 	}
 
- 	for (size_t i = 1; write_audio_header(stream, i); i++)
 
- 		;
 
- }
 
- static bool write_video_footer(struct flv_output *stream, size_t idx)
 
- {
 
- 	struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO,
 
- 					.timebase_den = 1,
 
- 					.keyframe = false};
 
- 	packet.size = 0;
 
- 	return write_packet_ex(stream, &packet, false, true, idx) >= 0;
 
- }
 
- static inline bool write_footers(struct flv_output *stream)
 
- {
 
- 	for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
 
- 		obs_encoder_t *encoder =
 
- 			obs_output_get_video_encoder2(stream->output, i);
 
- 		if (!encoder)
 
- 			continue;
 
- 		if (i == 0 && stream->video_codec[i] == CODEC_H264)
 
- 			continue;
 
- 		if (!write_video_footer(stream, i))
 
- 			return false;
 
- 	}
 
- 	return true;
 
- }
 
- static bool flv_output_start(void *data)
 
- {
 
- 	struct flv_output *stream = data;
 
- 	obs_data_t *settings;
 
- 	const char *path;
 
- 	if (!obs_output_can_begin_data_capture(stream->output, 0))
 
- 		return false;
 
- 	if (!obs_output_initialize_encoders(stream->output, 0))
 
- 		return false;
 
- 	stream->got_first_packet = false;
 
- 	stream->sent_headers = false;
 
- 	os_atomic_set_bool(&stream->stopping, false);
 
- 	/* get path */
 
- 	settings = obs_output_get_settings(stream->output);
 
- 	path = obs_data_get_string(settings, "path");
 
- 	dstr_copy(&stream->path, path);
 
- 	obs_data_release(settings);
 
- 	stream->file = os_fopen(stream->path.array, "wb");
 
- 	if (!stream->file) {
 
- 		warn("Unable to open FLV file '%s'", stream->path.array);
 
- 		return false;
 
- 	}
 
- 	/* write headers and start capture */
 
- 	os_atomic_set_bool(&stream->active, true);
 
- 	obs_output_begin_data_capture(stream->output, 0);
 
- 	info("Writing FLV file '%s'...", stream->path.array);
 
- 	return true;
 
- }
 
- static void flv_output_stop(void *data, uint64_t ts)
 
- {
 
- 	struct flv_output *stream = data;
 
- 	stream->stop_ts = ts / 1000;
 
- 	os_atomic_set_bool(&stream->stopping, true);
 
- }
 
- static void flv_output_actual_stop(struct flv_output *stream, int code)
 
- {
 
- 	os_atomic_set_bool(&stream->active, false);
 
- 	if (stream->file) {
 
- 		write_footers(stream);
 
- 		write_file_info(stream->file, stream->last_packet_ts,
 
- 				os_ftelli64(stream->file));
 
- 		fclose(stream->file);
 
- 	}
 
- 	if (code) {
 
- 		obs_output_signal_stop(stream->output, code);
 
- 	} else {
 
- 		obs_output_end_data_capture(stream->output);
 
- 	}
 
- 	info("FLV file output complete");
 
- }
 
- static void flv_output_data(void *data, struct encoder_packet *packet)
 
- {
 
- 	struct flv_output *stream = data;
 
- 	struct encoder_packet parsed_packet;
 
- 	pthread_mutex_lock(&stream->mutex);
 
- 	if (!active(stream))
 
- 		goto unlock;
 
- 	if (!packet) {
 
- 		flv_output_actual_stop(stream, OBS_OUTPUT_ENCODE_ERROR);
 
- 		goto unlock;
 
- 	}
 
- 	if (stopping(stream)) {
 
- 		if (packet->sys_dts_usec >= (int64_t)stream->stop_ts) {
 
- 			flv_output_actual_stop(stream, 0);
 
- 			goto unlock;
 
- 		}
 
- 	}
 
- 	if (!stream->sent_headers) {
 
- 		write_headers(stream);
 
- 		stream->sent_headers = true;
 
- 	}
 
- 	if (packet->type == OBS_ENCODER_VIDEO) {
 
- 		if (!stream->got_first_packet) {
 
- 			stream->start_dts_offset =
 
- 				get_ms_time(packet, packet->dts);
 
- 			stream->got_first_packet = true;
 
- 		}
 
- 		switch (stream->video_codec[packet->track_idx]) {
 
- 		case CODEC_NONE:
 
- 			do_log(LOG_ERROR, "Codec not initialized for track %zu",
 
- 			       packet->track_idx);
 
- 			goto unlock;
 
- 		case CODEC_H264:
 
- 			obs_parse_avc_packet(&parsed_packet, packet);
 
- 			break;
 
- 		case CODEC_HEVC:
 
- #ifdef ENABLE_HEVC
 
- 			obs_parse_hevc_packet(&parsed_packet, packet);
 
- 			break;
 
- #else
 
- 			goto unlock;
 
- #endif
 
- 		case CODEC_AV1:
 
- 			obs_parse_av1_packet(&parsed_packet, packet);
 
- 			break;
 
- 		}
 
- 		if (stream->video_codec[packet->track_idx] != CODEC_H264 ||
 
- 		    (stream->video_codec[packet->track_idx] == CODEC_H264 &&
 
- 		     packet->track_idx != 0)) {
 
- 			write_packet_ex(stream, &parsed_packet, false, false,
 
- 					packet->track_idx);
 
- 		} else {
 
- 			write_packet(stream, &parsed_packet, false);
 
- 		}
 
- 		obs_encoder_packet_release(&parsed_packet);
 
- 	} else {
 
- 		if (!stream->got_first_packet) {
 
- 			stream->start_dts_offset =
 
- 				get_ms_time(packet, packet->dts);
 
- 			stream->got_first_packet = true;
 
- 		}
 
- 		if (packet->track_idx != 0) {
 
- 			write_audio_packet_ex(stream, packet, false,
 
- 					      packet->track_idx);
 
- 		} else {
 
- 			write_packet(stream, packet, false);
 
- 		}
 
- 	}
 
- unlock:
 
- 	pthread_mutex_unlock(&stream->mutex);
 
- }
 
- static obs_properties_t *flv_output_properties(void *unused)
 
- {
 
- 	UNUSED_PARAMETER(unused);
 
- 	obs_properties_t *props = obs_properties_create();
 
- 	obs_properties_add_text(props, "path",
 
- 				obs_module_text("FLVOutput.FilePath"),
 
- 				OBS_TEXT_DEFAULT);
 
- 	return props;
 
- }
 
- struct obs_output_info flv_output_info = {
 
- 	.id = "flv_output",
 
- 	.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_MULTI_TRACK_AV,
 
- #ifdef ENABLE_HEVC
 
- 	.encoded_video_codecs = "h264;hevc;av1",
 
- #else
 
- 	.encoded_video_codecs = "h264;av1",
 
- #endif
 
- 	.encoded_audio_codecs = "aac",
 
- 	.get_name = flv_output_getname,
 
- 	.create = flv_output_create,
 
- 	.destroy = flv_output_destroy,
 
- 	.start = flv_output_start,
 
- 	.stop = flv_output_stop,
 
- 	.encoded_packet = flv_output_data,
 
- 	.get_properties = flv_output_properties,
 
- };
 
 
  |