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.

448 lines
13KB

  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 <stdlib.h>
  22. #include "libavutil/opt.h"
  23. #include "libavutil/time.h"
  24. #include "libavutil/avassert.h"
  25. #include "libavformat/avformat.h"
  26. #include "libavformat/url.h"
  27. #include "libavformat/network.h"
  28. #define MAX_TST_PACKETS 128
  29. #define SLEEPTIME_50_MS 50000
  30. #define SLEEPTIME_10_MS 10000
  31. /* Implementation of mock muxer to simulate real muxer failures */
  32. /* This is structure of data sent in packets to
  33. * failing muxer */
  34. typedef struct FailingMuxerPacketData {
  35. int ret; /* return value of write_packet call*/
  36. int recover_after; /* set ret to zero after this number of recovery attempts */
  37. unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */
  38. } FailingMuxerPacketData;
  39. typedef struct FailingMuxerContext {
  40. AVClass *class;
  41. int write_header_ret;
  42. int write_trailer_ret;
  43. /* If non-zero, summary of processed packets will be printed in deinit */
  44. int print_deinit_summary;
  45. int flush_count;
  46. int pts_written[MAX_TST_PACKETS];
  47. int pts_written_nr;
  48. } FailingMuxerContext;
  49. static int failing_write_header(AVFormatContext *avf)
  50. {
  51. FailingMuxerContext *ctx = avf->priv_data;
  52. return ctx->write_header_ret;
  53. }
  54. static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt)
  55. {
  56. FailingMuxerContext *ctx = avf->priv_data;
  57. int ret = 0;
  58. if (!pkt) {
  59. ctx->flush_count++;
  60. } else {
  61. FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data;
  62. if (!data->recover_after) {
  63. data->ret = 0;
  64. } else {
  65. data->recover_after--;
  66. }
  67. ret = data->ret;
  68. if (data->sleep_time) {
  69. int64_t slept = 0;
  70. while (slept < data->sleep_time) {
  71. if (ff_check_interrupt(&avf->interrupt_callback))
  72. return AVERROR_EXIT;
  73. av_usleep(SLEEPTIME_10_MS);
  74. slept += SLEEPTIME_10_MS;
  75. }
  76. }
  77. if (!ret) {
  78. ctx->pts_written[ctx->pts_written_nr++] = pkt->pts;
  79. av_packet_unref(pkt);
  80. }
  81. }
  82. return ret;
  83. }
  84. static int failing_write_trailer(AVFormatContext *avf)
  85. {
  86. FailingMuxerContext *ctx = avf->priv_data;
  87. return ctx->write_trailer_ret;
  88. }
  89. static void failing_deinit(AVFormatContext *avf)
  90. {
  91. int i;
  92. FailingMuxerContext *ctx = avf->priv_data;
  93. if (!ctx->print_deinit_summary)
  94. return;
  95. printf("flush count: %d\n", ctx->flush_count);
  96. printf("pts seen nr: %d\n", ctx->pts_written_nr);
  97. printf("pts seen: ");
  98. for (i = 0; i < ctx->pts_written_nr; ++i ) {
  99. printf(i ? ",%d" : "%d", ctx->pts_written[i]);
  100. }
  101. printf("\n");
  102. }
  103. #define OFFSET(x) offsetof(FailingMuxerContext, x)
  104. static const AVOption options[] = {
  105. {"write_header_ret", "write_header() return value", OFFSET(write_header_ret),
  106. AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
  107. {"write_trailer_ret", "write_trailer() return value", OFFSET(write_trailer_ret),
  108. AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
  109. {"print_deinit_summary", "print summary when deinitializing muxer", OFFSET(print_deinit_summary),
  110. AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
  111. {NULL}
  112. };
  113. static const AVClass failing_muxer_class = {
  114. .class_name = "Failing test muxer",
  115. .item_name = av_default_item_name,
  116. .option = options,
  117. .version = LIBAVUTIL_VERSION_INT,
  118. };
  119. AVOutputFormat tst_failing_muxer = {
  120. .name = "fail",
  121. .long_name = NULL_IF_CONFIG_SMALL("Failing test muxer"),
  122. .priv_data_size = sizeof(FailingMuxerContext),
  123. .write_header = failing_write_header,
  124. .write_packet = failing_write_packet,
  125. .write_trailer = failing_write_trailer,
  126. .deinit = failing_deinit,
  127. .priv_class = &failing_muxer_class,
  128. .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
  129. };
  130. static int prepare_packet(AVPacket *pkt,const FailingMuxerPacketData *pkt_data, int64_t pts)
  131. {
  132. int ret;
  133. FailingMuxerPacketData *data = av_malloc(sizeof(*data));
  134. if (!data) {
  135. return AVERROR(ENOMEM);
  136. }
  137. memcpy(data, pkt_data, sizeof(FailingMuxerPacketData));
  138. ret = av_packet_from_data(pkt, (uint8_t*) data, sizeof(*data));
  139. pkt->pts = pkt->dts = pts;
  140. pkt->duration = 1;
  141. return ret;
  142. }
  143. static int initialize_fifo_tst_muxer_chain(AVFormatContext **oc)
  144. {
  145. int ret = 0;
  146. AVStream *s;
  147. ret = avformat_alloc_output_context2(oc, NULL, "fifo", "-");
  148. if (ret) {
  149. fprintf(stderr, "Failed to create format context: %s\n",
  150. av_err2str(ret));
  151. return EXIT_FAILURE;
  152. }
  153. s = avformat_new_stream(*oc, NULL);
  154. if (!s) {
  155. fprintf(stderr, "Failed to create stream: %s\n",
  156. av_err2str(ret));
  157. ret = AVERROR(ENOMEM);
  158. }
  159. return ret;
  160. }
  161. static int fifo_basic_test(AVFormatContext *oc, AVDictionary **opts,
  162. const FailingMuxerPacketData *pkt_data)
  163. {
  164. int ret = 0, i;
  165. AVPacket pkt;
  166. av_init_packet(&pkt);
  167. ret = avformat_write_header(oc, opts);
  168. if (ret) {
  169. fprintf(stderr, "Unexpected write_header failure: %s\n",
  170. av_err2str(ret));
  171. goto fail;
  172. }
  173. for (i = 0; i < 15; i++ ) {
  174. ret = prepare_packet(&pkt, pkt_data, i);
  175. if (ret < 0) {
  176. fprintf(stderr, "Failed to prepare test packet: %s\n",
  177. av_err2str(ret));
  178. goto write_trailer_and_fail;
  179. }
  180. ret = av_write_frame(oc, &pkt);
  181. av_packet_unref(&pkt);
  182. if (ret < 0) {
  183. fprintf(stderr, "Unexpected write_frame error: %s\n",
  184. av_err2str(ret));
  185. goto write_trailer_and_fail;
  186. }
  187. }
  188. ret = av_write_frame(oc, NULL);
  189. if (ret < 0) {
  190. fprintf(stderr, "Unexpected write_frame error during flushing: %s\n",
  191. av_err2str(ret));
  192. goto write_trailer_and_fail;
  193. }
  194. ret = av_write_trailer(oc);
  195. if (ret < 0) {
  196. fprintf(stderr, "Unexpected write_trailer error during flushing: %s\n",
  197. av_err2str(ret));
  198. goto fail;
  199. }
  200. return ret;
  201. write_trailer_and_fail:
  202. av_write_trailer(oc);
  203. fail:
  204. return ret;
  205. }
  206. static int fifo_write_header_err_tst(AVFormatContext *oc, AVDictionary **opts,
  207. const FailingMuxerPacketData *pkt_data)
  208. {
  209. int ret = 0, i;
  210. AVPacket pkt;
  211. av_init_packet(&pkt);
  212. ret = avformat_write_header(oc, opts);
  213. if (ret) {
  214. fprintf(stderr, "Unexpected write_header failure: %s\n",
  215. av_err2str(ret));
  216. goto fail;
  217. }
  218. for (i = 0; i < MAX_TST_PACKETS; i++ ) {
  219. ret = prepare_packet(&pkt, pkt_data, i);
  220. if (ret < 0) {
  221. fprintf(stderr, "Failed to prepare test packet: %s\n",
  222. av_err2str(ret));
  223. goto write_trailer_and_fail;
  224. }
  225. ret = av_write_frame(oc, &pkt);
  226. av_packet_unref(&pkt);
  227. if (ret < 0) {
  228. break;
  229. }
  230. }
  231. if (!ret) {
  232. fprintf(stderr, "write_packet not failed when supposed to.\n");
  233. goto fail;
  234. } else if (ret != -1) {
  235. fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret));
  236. goto fail;
  237. }
  238. ret = av_write_trailer(oc);
  239. if (ret < 0)
  240. fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret));
  241. return ret;
  242. write_trailer_and_fail:
  243. av_write_trailer(oc);
  244. fail:
  245. return ret;
  246. }
  247. static int fifo_overflow_drop_test(AVFormatContext *oc, AVDictionary **opts,
  248. const FailingMuxerPacketData *data)
  249. {
  250. int ret = 0, i;
  251. int64_t write_pkt_start, write_pkt_end, duration;
  252. AVPacket pkt;
  253. av_init_packet(&pkt);
  254. ret = avformat_write_header(oc, opts);
  255. if (ret) {
  256. fprintf(stderr, "Unexpected write_header failure: %s\n",
  257. av_err2str(ret));
  258. return ret;
  259. }
  260. write_pkt_start = av_gettime_relative();
  261. for (i = 0; i < 6; i++ ) {
  262. ret = prepare_packet(&pkt, data, i);
  263. if (ret < 0) {
  264. fprintf(stderr, "Failed to prepare test packet: %s\n",
  265. av_err2str(ret));
  266. goto fail;
  267. }
  268. ret = av_write_frame(oc, &pkt);
  269. av_packet_unref(&pkt);
  270. if (ret < 0) {
  271. break;
  272. }
  273. }
  274. write_pkt_end = av_gettime_relative();
  275. duration = write_pkt_end - write_pkt_start;
  276. if (duration > (SLEEPTIME_50_MS*6)/2) {
  277. fprintf(stderr, "Writing packets to fifo muxer took too much time while testing"
  278. "buffer overflow with drop_pkts_on_overflow was on.\n");
  279. ret = AVERROR_BUG;
  280. goto fail;
  281. }
  282. if (ret) {
  283. fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret));
  284. goto fail;
  285. }
  286. ret = av_write_trailer(oc);
  287. if (ret < 0)
  288. fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret));
  289. return ret;
  290. fail:
  291. av_write_trailer(oc);
  292. return ret;
  293. }
  294. typedef struct TestCase {
  295. int (*test_func)(AVFormatContext *, AVDictionary **,const FailingMuxerPacketData *pkt_data);
  296. const char *test_name;
  297. const char *options;
  298. uint8_t print_summary_on_deinit;
  299. int write_header_ret;
  300. int write_trailer_ret;
  301. FailingMuxerPacketData pkt_data;
  302. } TestCase;
  303. #define BUFFER_SIZE 64
  304. static int run_test(const TestCase *test)
  305. {
  306. AVDictionary *opts = NULL;
  307. AVFormatContext *oc = NULL;
  308. char buffer[BUFFER_SIZE];
  309. int ret, ret1;
  310. ret = initialize_fifo_tst_muxer_chain(&oc);
  311. if (ret < 0) {
  312. fprintf(stderr, "Muxer initialization failed: %s\n", av_err2str(ret));
  313. goto end;
  314. }
  315. if (test->options) {
  316. ret = av_dict_parse_string(&opts, test->options, "=", ":", 0);
  317. if (ret < 0) {
  318. fprintf(stderr, "Failed to parse options: %s\n", av_err2str(ret));
  319. goto end;
  320. }
  321. }
  322. snprintf(buffer, BUFFER_SIZE,
  323. "print_deinit_summary=%d:write_header_ret=%d:write_trailer_ret=%d",
  324. (int)test->print_summary_on_deinit, test->write_header_ret,
  325. test->write_trailer_ret);
  326. ret = av_dict_set(&opts, "format_opts", buffer, 0);
  327. ret1 = av_dict_set(&opts, "fifo_format", "fail", 0);
  328. if (ret < 0 || ret1 < 0) {
  329. fprintf(stderr, "Failed to set options for test muxer: %s\n",
  330. av_err2str(ret));
  331. goto end;
  332. }
  333. ret = test->test_func(oc, &opts, &test->pkt_data);
  334. end:
  335. printf("%s: %s\n", test->test_name, ret < 0 ? "fail" : "ok");
  336. avformat_free_context(oc);
  337. av_dict_free(&opts);
  338. return ret;
  339. }
  340. const TestCase tests[] = {
  341. /* Simple test in packet-non-dropping mode, we expect to get on the output
  342. * exactly what was on input */
  343. {fifo_basic_test, "nonfail test", NULL,1, 0, 0, {0, 0, 0}},
  344. /* Test that we receive delayed write_header error from one of the write_packet
  345. * calls. */
  346. {fifo_write_header_err_tst, "write header error test", NULL, 0, -1, 0, {0, 0, 0}},
  347. /* Each write_packet will fail 3 times before operation is successful. If recovery
  348. * Since recovery is on, fifo muxer should not return any errors. */
  349. {fifo_basic_test, "recovery test", "attempt_recovery=1:recovery_wait_time=0",
  350. 0, 0, 0, {AVERROR(ETIMEDOUT), 3, 0}},
  351. /* By setting low queue_size and sending packets with longer processing time,
  352. * this test will cause queue to overflow, since drop_pkts_on_overflow is off
  353. * by default, all packets should be processed and fifo should block on full
  354. * queue. */
  355. {fifo_basic_test, "overflow without packet dropping","queue_size=3",
  356. 1, 0, 0, {0, 0, SLEEPTIME_10_MS}},
  357. /* The test as the upper one, except that drop_on_overflow is turned on. In this case
  358. * fifo should not block when the queue is full and slow down producer, so the test
  359. * measures time producer spends on write_packet calls which should be significantly
  360. * less than number_of_pkts * 50 MS.
  361. */
  362. {fifo_overflow_drop_test, "overflow with packet dropping", "queue_size=3:drop_pkts_on_overflow=1",
  363. 0, 0, 0, {0, 0, SLEEPTIME_50_MS}},
  364. {NULL}
  365. };
  366. int main(int argc, char *argv[])
  367. {
  368. int i, ret, ret_all = 0;
  369. av_register_all();
  370. av_register_output_format(&tst_failing_muxer);
  371. for (i = 0; tests[i].test_func; i++) {
  372. ret = run_test(&tests[i]);
  373. if (!ret_all && ret < 0)
  374. ret_all = ret;
  375. }
  376. return ret;
  377. }