Browse code

avformat/fifo: Add fate test

Tested-by: Michael Niedermayer <michael@niedermayer.cc>
Signed-off-by: Jan Sebechlebsky <sebechlebskyjan@gmail.com>
Signed-off-by: Marton Balint <cus@passwd.hu>

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