You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

668 lines
20KB

  1. /*
  2. * FIFO pseudo-muxer
  3. * Copyright (c) 2016 Jan Sebechlebsky
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public License
  9. * as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "libavutil/avassert.h"
  22. #include "libavutil/opt.h"
  23. #include "libavutil/time.h"
  24. #include "libavutil/thread.h"
  25. #include "libavutil/threadmessage.h"
  26. #include "avformat.h"
  27. #include "internal.h"
  28. #define FIFO_DEFAULT_QUEUE_SIZE 60
  29. #define FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS 0
  30. #define FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC 5000000 // 5 seconds
  31. typedef struct FifoContext {
  32. const AVClass *class;
  33. AVFormatContext *avf;
  34. char *format;
  35. char *format_options_str;
  36. AVDictionary *format_options;
  37. int queue_size;
  38. AVThreadMessageQueue *queue;
  39. pthread_t writer_thread;
  40. /* Return value of last write_trailer_call */
  41. int write_trailer_ret;
  42. /* Time to wait before next recovery attempt
  43. * This can refer to the time in processed stream,
  44. * or real time. */
  45. int64_t recovery_wait_time;
  46. /* Maximal number of unsuccessful successive recovery attempts */
  47. int max_recovery_attempts;
  48. /* Whether to attempt recovery from failure */
  49. int attempt_recovery;
  50. /* If >0 stream time will be used when waiting
  51. * for the recovery attempt instead of real time */
  52. int recovery_wait_streamtime;
  53. /* If >0 recovery will be attempted regardless of error code
  54. * (except AVERROR_EXIT, so exit request is never ignored) */
  55. int recover_any_error;
  56. /* Whether to drop packets in case the queue is full. */
  57. int drop_pkts_on_overflow;
  58. /* Whether to wait for keyframe when recovering
  59. * from failure or queue overflow */
  60. int restart_with_keyframe;
  61. pthread_mutex_t overflow_flag_lock;
  62. int overflow_flag_lock_initialized;
  63. /* Value > 0 signals queue overflow */
  64. volatile uint8_t overflow_flag;
  65. } FifoContext;
  66. typedef struct FifoThreadContext {
  67. AVFormatContext *avf;
  68. /* Timestamp of last failure.
  69. * This is either pts in case stream time is used,
  70. * or microseconds as returned by av_getttime_relative() */
  71. int64_t last_recovery_ts;
  72. /* Number of current recovery process
  73. * Value > 0 means we are in recovery process */
  74. int recovery_nr;
  75. /* If > 0 all frames will be dropped until keyframe is received */
  76. uint8_t drop_until_keyframe;
  77. /* Value > 0 means that the previous write_header call was successful
  78. * so finalization by calling write_trailer and ff_io_close must be done
  79. * before exiting / reinitialization of underlying muxer */
  80. uint8_t header_written;
  81. } FifoThreadContext;
  82. typedef enum FifoMessageType {
  83. FIFO_WRITE_HEADER,
  84. FIFO_WRITE_PACKET,
  85. FIFO_FLUSH_OUTPUT
  86. } FifoMessageType;
  87. typedef struct FifoMessage {
  88. FifoMessageType type;
  89. AVPacket pkt;
  90. } FifoMessage;
  91. static int fifo_thread_write_header(FifoThreadContext *ctx)
  92. {
  93. AVFormatContext *avf = ctx->avf;
  94. FifoContext *fifo = avf->priv_data;
  95. AVFormatContext *avf2 = fifo->avf;
  96. AVDictionary *format_options = NULL;
  97. int ret, i;
  98. ret = av_dict_copy(&format_options, fifo->format_options, 0);
  99. if (ret < 0)
  100. return ret;
  101. ret = ff_format_output_open(avf2, avf->filename, &format_options);
  102. if (ret < 0) {
  103. av_log(avf, AV_LOG_ERROR, "Error opening %s: %s\n", avf->filename,
  104. av_err2str(ret));
  105. goto end;
  106. }
  107. for (i = 0;i < avf2->nb_streams; i++)
  108. avf2->streams[i]->cur_dts = 0;
  109. ret = avformat_write_header(avf2, &format_options);
  110. if (!ret)
  111. ctx->header_written = 1;
  112. // Check for options unrecognized by underlying muxer
  113. if (format_options) {
  114. AVDictionaryEntry *entry = NULL;
  115. while ((entry = av_dict_get(format_options, "", entry, AV_DICT_IGNORE_SUFFIX)))
  116. av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
  117. ret = AVERROR(EINVAL);
  118. }
  119. end:
  120. av_dict_free(&format_options);
  121. return ret;
  122. }
  123. static int fifo_thread_flush_output(FifoThreadContext *ctx)
  124. {
  125. AVFormatContext *avf = ctx->avf;
  126. FifoContext *fifo = avf->priv_data;
  127. AVFormatContext *avf2 = fifo->avf;
  128. return av_write_frame(avf2, NULL);
  129. }
  130. static int fifo_thread_write_packet(FifoThreadContext *ctx, AVPacket *pkt)
  131. {
  132. AVFormatContext *avf = ctx->avf;
  133. FifoContext *fifo = avf->priv_data;
  134. AVFormatContext *avf2 = fifo->avf;
  135. AVRational src_tb, dst_tb;
  136. int ret, s_idx;
  137. if (ctx->drop_until_keyframe) {
  138. if (pkt->flags & AV_PKT_FLAG_KEY) {
  139. ctx->drop_until_keyframe = 0;
  140. av_log(avf, AV_LOG_VERBOSE, "Keyframe received, recovering...\n");
  141. } else {
  142. av_log(avf, AV_LOG_VERBOSE, "Dropping non-keyframe packet\n");
  143. av_packet_unref(pkt);
  144. return 0;
  145. }
  146. }
  147. s_idx = pkt->stream_index;
  148. src_tb = avf->streams[s_idx]->time_base;
  149. dst_tb = avf2->streams[s_idx]->time_base;
  150. av_packet_rescale_ts(pkt, src_tb, dst_tb);
  151. ret = av_write_frame(avf2, pkt);
  152. if (ret >= 0)
  153. av_packet_unref(pkt);
  154. return ret;
  155. }
  156. static int fifo_thread_write_trailer(FifoThreadContext *ctx)
  157. {
  158. AVFormatContext *avf = ctx->avf;
  159. FifoContext *fifo = avf->priv_data;
  160. AVFormatContext *avf2 = fifo->avf;
  161. int ret;
  162. if (!ctx->header_written)
  163. return 0;
  164. ret = av_write_trailer(avf2);
  165. ff_format_io_close(avf2, &avf2->pb);
  166. return ret;
  167. }
  168. static int fifo_thread_dispatch_message(FifoThreadContext *ctx, FifoMessage *msg)
  169. {
  170. int ret = AVERROR(EINVAL);
  171. if (!ctx->header_written) {
  172. ret = fifo_thread_write_header(ctx);
  173. if (ret < 0)
  174. return ret;
  175. }
  176. switch(msg->type) {
  177. case FIFO_WRITE_HEADER:
  178. av_assert0(ret >= 0);
  179. return ret;
  180. case FIFO_WRITE_PACKET:
  181. return fifo_thread_write_packet(ctx, &msg->pkt);
  182. case FIFO_FLUSH_OUTPUT:
  183. return fifo_thread_flush_output(ctx);
  184. }
  185. av_assert0(0);
  186. return AVERROR(EINVAL);
  187. }
  188. static int is_recoverable(const FifoContext *fifo, int err_no) {
  189. if (!fifo->attempt_recovery)
  190. return 0;
  191. if (fifo->recover_any_error)
  192. return err_no != AVERROR_EXIT;
  193. switch (err_no) {
  194. case AVERROR(EINVAL):
  195. case AVERROR(ENOSYS):
  196. case AVERROR_EOF:
  197. case AVERROR_EXIT:
  198. case AVERROR_PATCHWELCOME:
  199. return 0;
  200. default:
  201. return 1;
  202. }
  203. }
  204. static void free_message(void *msg)
  205. {
  206. FifoMessage *fifo_msg = msg;
  207. if (fifo_msg->type == FIFO_WRITE_PACKET)
  208. av_packet_unref(&fifo_msg->pkt);
  209. }
  210. static int fifo_thread_process_recovery_failure(FifoThreadContext *ctx, AVPacket *pkt,
  211. int err_no)
  212. {
  213. AVFormatContext *avf = ctx->avf;
  214. FifoContext *fifo = avf->priv_data;
  215. int ret;
  216. av_log(avf, AV_LOG_INFO, "Recovery failed: %s\n",
  217. av_err2str(err_no));
  218. if (fifo->recovery_wait_streamtime) {
  219. if (pkt->pts == AV_NOPTS_VALUE)
  220. av_log(avf, AV_LOG_WARNING, "Packet does not contain presentation"
  221. " timestamp, recovery will be attempted immediately");
  222. ctx->last_recovery_ts = pkt->pts;
  223. } else {
  224. ctx->last_recovery_ts = av_gettime_relative();
  225. }
  226. if (fifo->max_recovery_attempts &&
  227. ctx->recovery_nr >= fifo->max_recovery_attempts) {
  228. av_log(avf, AV_LOG_ERROR,
  229. "Maximal number of %d recovery attempts reached.\n",
  230. fifo->max_recovery_attempts);
  231. ret = err_no;
  232. } else {
  233. ret = AVERROR(EAGAIN);
  234. }
  235. return ret;
  236. }
  237. static int fifo_thread_attempt_recovery(FifoThreadContext *ctx, FifoMessage *msg, int err_no)
  238. {
  239. AVFormatContext *avf = ctx->avf;
  240. FifoContext *fifo = avf->priv_data;
  241. AVPacket *pkt = &msg->pkt;
  242. int64_t time_since_recovery;
  243. int ret;
  244. if (!is_recoverable(fifo, err_no)) {
  245. ret = err_no;
  246. goto fail;
  247. }
  248. if (ctx->header_written) {
  249. fifo->write_trailer_ret = fifo_thread_write_trailer(ctx);
  250. ctx->header_written = 0;
  251. }
  252. if (!ctx->recovery_nr) {
  253. ctx->last_recovery_ts = fifo->recovery_wait_streamtime ?
  254. AV_NOPTS_VALUE : 0;
  255. } else {
  256. if (fifo->recovery_wait_streamtime) {
  257. if (ctx->last_recovery_ts == AV_NOPTS_VALUE) {
  258. AVRational tb = avf->streams[pkt->stream_index]->time_base;
  259. time_since_recovery = av_rescale_q(pkt->pts - ctx->last_recovery_ts,
  260. tb, AV_TIME_BASE_Q);
  261. } else {
  262. /* Enforce recovery immediately */
  263. time_since_recovery = fifo->recovery_wait_time;
  264. }
  265. } else {
  266. time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
  267. }
  268. if (time_since_recovery < fifo->recovery_wait_time)
  269. return AVERROR(EAGAIN);
  270. }
  271. ctx->recovery_nr++;
  272. if (fifo->max_recovery_attempts) {
  273. av_log(avf, AV_LOG_VERBOSE, "Recovery attempt #%d/%d\n",
  274. ctx->recovery_nr, fifo->max_recovery_attempts);
  275. } else {
  276. av_log(avf, AV_LOG_VERBOSE, "Recovery attempt #%d\n",
  277. ctx->recovery_nr);
  278. }
  279. if (fifo->restart_with_keyframe && fifo->drop_pkts_on_overflow)
  280. ctx->drop_until_keyframe = 1;
  281. ret = fifo_thread_dispatch_message(ctx, msg);
  282. if (ret < 0) {
  283. if (is_recoverable(fifo, ret)) {
  284. return fifo_thread_process_recovery_failure(ctx, pkt, ret);
  285. } else {
  286. goto fail;
  287. }
  288. } else {
  289. av_log(avf, AV_LOG_INFO, "Recovery successful\n");
  290. ctx->recovery_nr = 0;
  291. }
  292. return 0;
  293. fail:
  294. free_message(msg);
  295. return ret;
  296. }
  297. static int fifo_thread_recover(FifoThreadContext *ctx, FifoMessage *msg, int err_no)
  298. {
  299. AVFormatContext *avf = ctx->avf;
  300. FifoContext *fifo = avf->priv_data;
  301. int ret;
  302. do {
  303. if (!fifo->recovery_wait_streamtime && ctx->recovery_nr > 0) {
  304. int64_t time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
  305. int64_t time_to_wait = FFMAX(0, fifo->recovery_wait_time - time_since_recovery);
  306. if (time_to_wait)
  307. av_usleep(FFMIN(10000, time_to_wait));
  308. }
  309. ret = fifo_thread_attempt_recovery(ctx, msg, err_no);
  310. } while (ret == AVERROR(EAGAIN) && !fifo->drop_pkts_on_overflow);
  311. if (ret == AVERROR(EAGAIN) && fifo->drop_pkts_on_overflow) {
  312. if (msg->type == FIFO_WRITE_PACKET)
  313. av_packet_unref(&msg->pkt);
  314. ret = 0;
  315. }
  316. return ret;
  317. }
  318. static void *fifo_consumer_thread(void *data)
  319. {
  320. AVFormatContext *avf = data;
  321. FifoContext *fifo = avf->priv_data;
  322. AVThreadMessageQueue *queue = fifo->queue;
  323. FifoMessage msg = {FIFO_WRITE_HEADER, {0}};
  324. int ret;
  325. FifoThreadContext fifo_thread_ctx;
  326. memset(&fifo_thread_ctx, 0, sizeof(FifoThreadContext));
  327. fifo_thread_ctx.avf = avf;
  328. while (1) {
  329. uint8_t just_flushed = 0;
  330. if (!fifo_thread_ctx.recovery_nr)
  331. ret = fifo_thread_dispatch_message(&fifo_thread_ctx, &msg);
  332. if (ret < 0 || fifo_thread_ctx.recovery_nr > 0) {
  333. int rec_ret = fifo_thread_recover(&fifo_thread_ctx, &msg, ret);
  334. if (rec_ret < 0) {
  335. av_thread_message_queue_set_err_send(queue, rec_ret);
  336. break;
  337. }
  338. }
  339. /* If the queue is full at the moment when fifo_write_packet
  340. * attempts to insert new message (packet) to the queue,
  341. * it sets the fifo->overflow_flag to 1 and drops packet.
  342. * Here in consumer thread, the flag is checked and if it is
  343. * set, the queue is flushed and flag cleared. */
  344. pthread_mutex_lock(&fifo->overflow_flag_lock);
  345. if (fifo->overflow_flag) {
  346. av_thread_message_flush(queue);
  347. if (fifo->restart_with_keyframe)
  348. fifo_thread_ctx.drop_until_keyframe = 1;
  349. fifo->overflow_flag = 0;
  350. just_flushed = 1;
  351. }
  352. pthread_mutex_unlock(&fifo->overflow_flag_lock);
  353. if (just_flushed)
  354. av_log(avf, AV_LOG_INFO, "FIFO queue flushed\n");
  355. ret = av_thread_message_queue_recv(queue, &msg, 0);
  356. if (ret < 0) {
  357. av_thread_message_queue_set_err_send(queue, ret);
  358. break;
  359. }
  360. }
  361. fifo->write_trailer_ret = fifo_thread_write_trailer(&fifo_thread_ctx);
  362. return NULL;
  363. }
  364. static int fifo_mux_init(AVFormatContext *avf, AVOutputFormat *oformat,
  365. const char *filename)
  366. {
  367. FifoContext *fifo = avf->priv_data;
  368. AVFormatContext *avf2;
  369. int ret = 0, i;
  370. ret = avformat_alloc_output_context2(&avf2, oformat, NULL, filename);
  371. if (ret < 0)
  372. return ret;
  373. fifo->avf = avf2;
  374. avf2->interrupt_callback = avf->interrupt_callback;
  375. avf2->max_delay = avf->max_delay;
  376. ret = av_dict_copy(&avf2->metadata, avf->metadata, 0);
  377. if (ret < 0)
  378. return ret;
  379. avf2->opaque = avf->opaque;
  380. avf2->io_close = avf->io_close;
  381. avf2->io_open = avf->io_open;
  382. avf2->flags = avf->flags;
  383. for (i = 0; i < avf->nb_streams; ++i) {
  384. AVStream *st = avformat_new_stream(avf2, NULL);
  385. if (!st)
  386. return AVERROR(ENOMEM);
  387. ret = ff_stream_encode_params_copy(st, avf->streams[i]);
  388. if (ret < 0)
  389. return ret;
  390. }
  391. return 0;
  392. }
  393. static int fifo_init(AVFormatContext *avf)
  394. {
  395. FifoContext *fifo = avf->priv_data;
  396. AVOutputFormat *oformat;
  397. int ret = 0;
  398. if (fifo->recovery_wait_streamtime && !fifo->drop_pkts_on_overflow) {
  399. av_log(avf, AV_LOG_ERROR, "recovery_wait_streamtime can be turned on"
  400. " only when drop_pkts_on_overflow is also turned on\n");
  401. return AVERROR(EINVAL);
  402. }
  403. if (fifo->format_options_str) {
  404. ret = av_dict_parse_string(&fifo->format_options, fifo->format_options_str,
  405. "=", ":", 0);
  406. if (ret < 0) {
  407. av_log(avf, AV_LOG_ERROR, "Could not parse format options list '%s'\n",
  408. fifo->format_options_str);
  409. return ret;
  410. }
  411. }
  412. oformat = av_guess_format(fifo->format, avf->filename, NULL);
  413. if (!oformat) {
  414. ret = AVERROR_MUXER_NOT_FOUND;
  415. return ret;
  416. }
  417. ret = fifo_mux_init(avf, oformat, avf->filename);
  418. if (ret < 0)
  419. return ret;
  420. ret = av_thread_message_queue_alloc(&fifo->queue, (unsigned) fifo->queue_size,
  421. sizeof(FifoMessage));
  422. if (ret < 0)
  423. return ret;
  424. av_thread_message_queue_set_free_func(fifo->queue, free_message);
  425. ret = pthread_mutex_init(&fifo->overflow_flag_lock, NULL);
  426. if (ret < 0)
  427. return AVERROR(ret);
  428. fifo->overflow_flag_lock_initialized = 1;
  429. return 0;
  430. }
  431. static int fifo_write_header(AVFormatContext *avf)
  432. {
  433. FifoContext * fifo = avf->priv_data;
  434. int ret;
  435. ret = pthread_create(&fifo->writer_thread, NULL, fifo_consumer_thread, avf);
  436. if (ret) {
  437. av_log(avf, AV_LOG_ERROR, "Failed to start thread: %s\n",
  438. av_err2str(AVERROR(ret)));
  439. ret = AVERROR(ret);
  440. }
  441. return ret;
  442. }
  443. static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt)
  444. {
  445. FifoContext *fifo = avf->priv_data;
  446. FifoMessage msg = {.type = pkt ? FIFO_WRITE_PACKET : FIFO_FLUSH_OUTPUT};
  447. int ret;
  448. if (pkt) {
  449. av_init_packet(&msg.pkt);
  450. ret = av_packet_ref(&msg.pkt,pkt);
  451. if (ret < 0)
  452. return ret;
  453. }
  454. ret = av_thread_message_queue_send(fifo->queue, &msg,
  455. fifo->drop_pkts_on_overflow ?
  456. AV_THREAD_MESSAGE_NONBLOCK : 0);
  457. if (ret == AVERROR(EAGAIN)) {
  458. uint8_t overflow_set = 0;
  459. /* Queue is full, set fifo->overflow_flag to 1
  460. * to let consumer thread know the queue should
  461. * be flushed. */
  462. pthread_mutex_lock(&fifo->overflow_flag_lock);
  463. if (!fifo->overflow_flag)
  464. fifo->overflow_flag = overflow_set = 1;
  465. pthread_mutex_unlock(&fifo->overflow_flag_lock);
  466. if (overflow_set)
  467. av_log(avf, AV_LOG_WARNING, "FIFO queue full\n");
  468. ret = 0;
  469. goto fail;
  470. } else if (ret < 0) {
  471. goto fail;
  472. }
  473. return ret;
  474. fail:
  475. if (pkt)
  476. av_packet_unref(&msg.pkt);
  477. return ret;
  478. }
  479. static int fifo_write_trailer(AVFormatContext *avf)
  480. {
  481. FifoContext *fifo= avf->priv_data;
  482. int ret;
  483. av_thread_message_queue_set_err_recv(fifo->queue, AVERROR_EOF);
  484. ret = pthread_join(fifo->writer_thread, NULL);
  485. if (ret < 0) {
  486. av_log(avf, AV_LOG_ERROR, "pthread join error: %s\n",
  487. av_err2str(AVERROR(ret)));
  488. return AVERROR(ret);
  489. }
  490. ret = fifo->write_trailer_ret;
  491. return ret;
  492. }
  493. static void fifo_deinit(AVFormatContext *avf)
  494. {
  495. FifoContext *fifo = avf->priv_data;
  496. av_dict_free(&fifo->format_options);
  497. avformat_free_context(fifo->avf);
  498. av_thread_message_queue_free(&fifo->queue);
  499. if (fifo->overflow_flag_lock_initialized)
  500. pthread_mutex_destroy(&fifo->overflow_flag_lock);
  501. }
  502. #define OFFSET(x) offsetof(FifoContext, x)
  503. static const AVOption options[] = {
  504. {"fifo_format", "Target muxer", OFFSET(format),
  505. AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
  506. {"queue_size", "Size of fifo queue", OFFSET(queue_size),
  507. AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_QUEUE_SIZE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
  508. {"format_opts", "Options to be passed to underlying muxer", OFFSET(format_options_str),
  509. AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
  510. {"drop_pkts_on_overflow", "Drop packets on fifo queue overflow not to block encoder", OFFSET(drop_pkts_on_overflow),
  511. AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  512. {"restart_with_keyframe", "Wait for keyframe when restarting output", OFFSET(restart_with_keyframe),
  513. AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  514. {"attempt_recovery", "Attempt recovery in case of failure", OFFSET(attempt_recovery),
  515. AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  516. {"max_recovery_attempts", "Maximal number of recovery attempts", OFFSET(max_recovery_attempts),
  517. AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
  518. {"recovery_wait_time", "Waiting time between recovery attempts", OFFSET(recovery_wait_time),
  519. AV_OPT_TYPE_DURATION, {.i64 = FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM},
  520. {"recovery_wait_streamtime", "Use stream time instead of real time while waiting for recovery",
  521. OFFSET(recovery_wait_streamtime), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  522. {"recover_any_error", "Attempt recovery regardless of type of the error", OFFSET(recover_any_error),
  523. AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  524. {NULL},
  525. };
  526. static const AVClass fifo_muxer_class = {
  527. .class_name = "Fifo muxer",
  528. .item_name = av_default_item_name,
  529. .option = options,
  530. .version = LIBAVUTIL_VERSION_INT,
  531. };
  532. AVOutputFormat ff_fifo_muxer = {
  533. .name = "fifo",
  534. .long_name = NULL_IF_CONFIG_SMALL("FIFO queue pseudo-muxer"),
  535. .priv_data_size = sizeof(FifoContext),
  536. .init = fifo_init,
  537. .write_header = fifo_write_header,
  538. .write_packet = fifo_write_packet,
  539. .write_trailer = fifo_write_trailer,
  540. .deinit = fifo_deinit,
  541. .priv_class = &fifo_muxer_class,
  542. .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
  543. };