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