1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * FIFO pseudo-muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2016 Jan Sebechlebsky
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public License
9cabdff1aSopenharmony_ci * as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cabdff1aSopenharmony_ci * GNU Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public License
18cabdff1aSopenharmony_ci * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
19cabdff1aSopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include <stdlib.h>
23cabdff1aSopenharmony_ci#include "libavutil/opt.h"
24cabdff1aSopenharmony_ci#include "libavutil/time.h"
25cabdff1aSopenharmony_ci#include "libavformat/avformat.h"
26cabdff1aSopenharmony_ci#include "libavformat/url.h"
27cabdff1aSopenharmony_ci#include "libavformat/network.h"
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_ci#define MAX_TST_PACKETS 128
30cabdff1aSopenharmony_ci#define SLEEPTIME_50_MS 50000
31cabdff1aSopenharmony_ci#define SLEEPTIME_10_MS 10000
32cabdff1aSopenharmony_ci
33cabdff1aSopenharmony_ci/* This is structure of data sent in packets to
34cabdff1aSopenharmony_ci * failing muxer */
35cabdff1aSopenharmony_citypedef struct FailingMuxerPacketData {
36cabdff1aSopenharmony_ci    int ret;             /* return value of write_packet call*/
37cabdff1aSopenharmony_ci    int recover_after;   /* set ret to zero after this number of recovery attempts */
38cabdff1aSopenharmony_ci    unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */
39cabdff1aSopenharmony_ci} FailingMuxerPacketData;
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_cistatic int prepare_packet(AVPacket *pkt, const FailingMuxerPacketData *pkt_data, int64_t pts)
42cabdff1aSopenharmony_ci{
43cabdff1aSopenharmony_ci    int ret = av_new_packet(pkt, sizeof(*pkt_data));
44cabdff1aSopenharmony_ci    if (ret < 0)
45cabdff1aSopenharmony_ci        return ret;
46cabdff1aSopenharmony_ci    memcpy(pkt->data, pkt_data, sizeof(*pkt_data));
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_ci    pkt->pts = pkt->dts = pts;
49cabdff1aSopenharmony_ci    pkt->duration = 1;
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_ci    return 0;
52cabdff1aSopenharmony_ci}
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_cistatic int initialize_fifo_tst_muxer_chain(AVFormatContext **oc, AVPacket **pkt)
55cabdff1aSopenharmony_ci{
56cabdff1aSopenharmony_ci    int ret = 0;
57cabdff1aSopenharmony_ci    AVStream *s;
58cabdff1aSopenharmony_ci
59cabdff1aSopenharmony_ci    ret = avformat_alloc_output_context2(oc, NULL, "fifo", "-");
60cabdff1aSopenharmony_ci    if (ret) {
61cabdff1aSopenharmony_ci        fprintf(stderr, "Failed to create format context: %s\n",
62cabdff1aSopenharmony_ci                av_err2str(ret));
63cabdff1aSopenharmony_ci        return EXIT_FAILURE;
64cabdff1aSopenharmony_ci    }
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci    s = avformat_new_stream(*oc, NULL);
67cabdff1aSopenharmony_ci    if (!s) {
68cabdff1aSopenharmony_ci        fprintf(stderr, "Failed to create stream: %s\n",
69cabdff1aSopenharmony_ci                av_err2str(ret));
70cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
71cabdff1aSopenharmony_ci    }
72cabdff1aSopenharmony_ci
73cabdff1aSopenharmony_ci    *pkt = av_packet_alloc();
74cabdff1aSopenharmony_ci    if (!*pkt)
75cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    return 0;
78cabdff1aSopenharmony_ci}
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_cistatic int fifo_basic_test(AVFormatContext *oc, AVDictionary **opts,
81cabdff1aSopenharmony_ci                           AVPacket *pkt, const FailingMuxerPacketData *pkt_data)
82cabdff1aSopenharmony_ci{
83cabdff1aSopenharmony_ci    int ret = 0, i;
84cabdff1aSopenharmony_ci
85cabdff1aSopenharmony_ci    ret = avformat_write_header(oc, opts);
86cabdff1aSopenharmony_ci    if (ret) {
87cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_header failure: %s\n",
88cabdff1aSopenharmony_ci                av_err2str(ret));
89cabdff1aSopenharmony_ci        goto fail;
90cabdff1aSopenharmony_ci    }
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_ci    for (i = 0; i < 15; i++ ) {
93cabdff1aSopenharmony_ci        ret = prepare_packet(pkt, pkt_data, i);
94cabdff1aSopenharmony_ci        if (ret < 0) {
95cabdff1aSopenharmony_ci            fprintf(stderr, "Failed to prepare test packet: %s\n",
96cabdff1aSopenharmony_ci                    av_err2str(ret));
97cabdff1aSopenharmony_ci            goto write_trailer_and_fail;
98cabdff1aSopenharmony_ci        }
99cabdff1aSopenharmony_ci        ret = av_write_frame(oc, pkt);
100cabdff1aSopenharmony_ci        av_packet_unref(pkt);
101cabdff1aSopenharmony_ci        if (ret < 0) {
102cabdff1aSopenharmony_ci            fprintf(stderr, "Unexpected write_frame error: %s\n",
103cabdff1aSopenharmony_ci                    av_err2str(ret));
104cabdff1aSopenharmony_ci            goto write_trailer_and_fail;
105cabdff1aSopenharmony_ci        }
106cabdff1aSopenharmony_ci    }
107cabdff1aSopenharmony_ci
108cabdff1aSopenharmony_ci    ret = av_write_frame(oc, NULL);
109cabdff1aSopenharmony_ci    if (ret < 0) {
110cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_frame error during flushing: %s\n",
111cabdff1aSopenharmony_ci                av_err2str(ret));
112cabdff1aSopenharmony_ci        goto write_trailer_and_fail;
113cabdff1aSopenharmony_ci    }
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_ci    ret = av_write_trailer(oc);
116cabdff1aSopenharmony_ci    if (ret < 0) {
117cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_trailer error during flushing: %s\n",
118cabdff1aSopenharmony_ci                av_err2str(ret));
119cabdff1aSopenharmony_ci        goto fail;
120cabdff1aSopenharmony_ci    }
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_ci    return ret;
123cabdff1aSopenharmony_ciwrite_trailer_and_fail:
124cabdff1aSopenharmony_ci    av_write_trailer(oc);
125cabdff1aSopenharmony_cifail:
126cabdff1aSopenharmony_ci    return ret;
127cabdff1aSopenharmony_ci}
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_cistatic int fifo_overflow_drop_test(AVFormatContext *oc, AVDictionary **opts,
130cabdff1aSopenharmony_ci                                   AVPacket *pkt, const FailingMuxerPacketData *data)
131cabdff1aSopenharmony_ci{
132cabdff1aSopenharmony_ci    int ret = 0, i;
133cabdff1aSopenharmony_ci    int64_t write_pkt_start, write_pkt_end, duration;
134cabdff1aSopenharmony_ci
135cabdff1aSopenharmony_ci    ret = avformat_write_header(oc, opts);
136cabdff1aSopenharmony_ci    if (ret) {
137cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_header failure: %s\n",
138cabdff1aSopenharmony_ci                av_err2str(ret));
139cabdff1aSopenharmony_ci        return ret;
140cabdff1aSopenharmony_ci    }
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_ci    write_pkt_start = av_gettime_relative();
143cabdff1aSopenharmony_ci    for (i = 0; i < 6; i++ ) {
144cabdff1aSopenharmony_ci        ret = prepare_packet(pkt, data, i);
145cabdff1aSopenharmony_ci        if (ret < 0) {
146cabdff1aSopenharmony_ci            fprintf(stderr, "Failed to prepare test packet: %s\n",
147cabdff1aSopenharmony_ci                    av_err2str(ret));
148cabdff1aSopenharmony_ci            goto fail;
149cabdff1aSopenharmony_ci        }
150cabdff1aSopenharmony_ci        ret = av_write_frame(oc, pkt);
151cabdff1aSopenharmony_ci        av_packet_unref(pkt);
152cabdff1aSopenharmony_ci        if (ret < 0) {
153cabdff1aSopenharmony_ci            break;
154cabdff1aSopenharmony_ci        }
155cabdff1aSopenharmony_ci    }
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_ci    write_pkt_end = av_gettime_relative();
158cabdff1aSopenharmony_ci    duration = write_pkt_end - write_pkt_start;
159cabdff1aSopenharmony_ci    if (duration > (SLEEPTIME_50_MS*6)/2) {
160cabdff1aSopenharmony_ci        fprintf(stderr, "Writing packets to fifo muxer took too much time while testing"
161cabdff1aSopenharmony_ci                        "buffer overflow with drop_pkts_on_overflow was on.\n");
162cabdff1aSopenharmony_ci        ret = AVERROR_BUG;
163cabdff1aSopenharmony_ci        goto fail;
164cabdff1aSopenharmony_ci    }
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci    if (ret) {
167cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_packet error: %s\n", av_err2str(ret));
168cabdff1aSopenharmony_ci        goto fail;
169cabdff1aSopenharmony_ci    }
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_ci    ret = av_write_trailer(oc);
172cabdff1aSopenharmony_ci    if (ret < 0)
173cabdff1aSopenharmony_ci        fprintf(stderr, "Unexpected write_trailer error: %s\n", av_err2str(ret));
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    return ret;
176cabdff1aSopenharmony_cifail:
177cabdff1aSopenharmony_ci    av_write_trailer(oc);
178cabdff1aSopenharmony_ci    return ret;
179cabdff1aSopenharmony_ci}
180cabdff1aSopenharmony_ci
181cabdff1aSopenharmony_citypedef struct TestCase {
182cabdff1aSopenharmony_ci    int (*test_func)(AVFormatContext *, AVDictionary **,
183cabdff1aSopenharmony_ci                     AVPacket *, const FailingMuxerPacketData *pkt_data);
184cabdff1aSopenharmony_ci    const char *test_name;
185cabdff1aSopenharmony_ci    const char *options;
186cabdff1aSopenharmony_ci
187cabdff1aSopenharmony_ci    uint8_t print_summary_on_deinit;
188cabdff1aSopenharmony_ci    int write_header_ret;
189cabdff1aSopenharmony_ci    int write_trailer_ret;
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    FailingMuxerPacketData pkt_data;
192cabdff1aSopenharmony_ci} TestCase;
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci
195cabdff1aSopenharmony_ci#define BUFFER_SIZE 64
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_cistatic int run_test(const TestCase *test)
198cabdff1aSopenharmony_ci{
199cabdff1aSopenharmony_ci    AVDictionary *opts = NULL;
200cabdff1aSopenharmony_ci    AVFormatContext *oc = NULL;
201cabdff1aSopenharmony_ci    AVPacket *pkt = NULL;
202cabdff1aSopenharmony_ci    char buffer[BUFFER_SIZE];
203cabdff1aSopenharmony_ci    int ret, ret1;
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_ci    ret = initialize_fifo_tst_muxer_chain(&oc, &pkt);
206cabdff1aSopenharmony_ci    if (ret < 0) {
207cabdff1aSopenharmony_ci        fprintf(stderr, "Muxer initialization failed: %s\n", av_err2str(ret));
208cabdff1aSopenharmony_ci        goto end;
209cabdff1aSopenharmony_ci    }
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_ci    if (test->options) {
212cabdff1aSopenharmony_ci        ret = av_dict_parse_string(&opts, test->options, "=", ":", 0);
213cabdff1aSopenharmony_ci        if (ret < 0) {
214cabdff1aSopenharmony_ci            fprintf(stderr, "Failed to parse options: %s\n", av_err2str(ret));
215cabdff1aSopenharmony_ci            goto end;
216cabdff1aSopenharmony_ci        }
217cabdff1aSopenharmony_ci    }
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci    snprintf(buffer, BUFFER_SIZE,
220cabdff1aSopenharmony_ci             "print_deinit_summary=%d:write_header_ret=%d:write_trailer_ret=%d",
221cabdff1aSopenharmony_ci             (int)test->print_summary_on_deinit, test->write_header_ret,
222cabdff1aSopenharmony_ci             test->write_trailer_ret);
223cabdff1aSopenharmony_ci    ret = av_dict_set(&opts, "format_opts", buffer, 0);
224cabdff1aSopenharmony_ci    ret1 = av_dict_set(&opts, "fifo_format", "fifo_test", 0);
225cabdff1aSopenharmony_ci    if (ret < 0 || ret1 < 0) {
226cabdff1aSopenharmony_ci        fprintf(stderr, "Failed to set options for test muxer: %s\n",
227cabdff1aSopenharmony_ci                av_err2str(ret));
228cabdff1aSopenharmony_ci        goto end;
229cabdff1aSopenharmony_ci    }
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci    ret = test->test_func(oc, &opts, pkt, &test->pkt_data);
232cabdff1aSopenharmony_ci
233cabdff1aSopenharmony_ciend:
234cabdff1aSopenharmony_ci    printf("%s: %s\n", test->test_name, ret < 0 ? "fail" : "ok");
235cabdff1aSopenharmony_ci    avformat_free_context(oc);
236cabdff1aSopenharmony_ci    av_packet_free(&pkt);
237cabdff1aSopenharmony_ci    av_dict_free(&opts);
238cabdff1aSopenharmony_ci    return ret;
239cabdff1aSopenharmony_ci}
240cabdff1aSopenharmony_ci
241cabdff1aSopenharmony_ci
242cabdff1aSopenharmony_ciconst TestCase tests[] = {
243cabdff1aSopenharmony_ci        /* Simple test in packet-non-dropping mode, we expect to get on the output
244cabdff1aSopenharmony_ci         * exactly what was on input */
245cabdff1aSopenharmony_ci        {fifo_basic_test, "nonfail test", NULL,1, 0, 0, {0, 0, 0}},
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_ci        /* Each write_packet will fail 3 times before operation is successful. If recovery
248cabdff1aSopenharmony_ci         * Since recovery is on, fifo muxer should not return any errors. */
249cabdff1aSopenharmony_ci        {fifo_basic_test, "recovery test", "attempt_recovery=1:recovery_wait_time=0",
250cabdff1aSopenharmony_ci         0, 0, 0, {AVERROR(ETIMEDOUT), 3, 0}},
251cabdff1aSopenharmony_ci
252cabdff1aSopenharmony_ci        /* By setting low queue_size and sending packets with longer processing time,
253cabdff1aSopenharmony_ci         * this test will cause queue to overflow, since drop_pkts_on_overflow is off
254cabdff1aSopenharmony_ci         * by default, all packets should be processed and fifo should block on full
255cabdff1aSopenharmony_ci         * queue. */
256cabdff1aSopenharmony_ci        {fifo_basic_test, "overflow without packet dropping","queue_size=3",
257cabdff1aSopenharmony_ci         1, 0, 0, {0, 0, SLEEPTIME_10_MS}},
258cabdff1aSopenharmony_ci
259cabdff1aSopenharmony_ci        /* The test as the upper one, except that drop_on_overflow is turned on. In this case
260cabdff1aSopenharmony_ci         * fifo should not block when the queue is full and slow down producer, so the test
261cabdff1aSopenharmony_ci         * measures time producer spends on write_packet calls which should be significantly
262cabdff1aSopenharmony_ci         * less than number_of_pkts * 50 MS.
263cabdff1aSopenharmony_ci         */
264cabdff1aSopenharmony_ci        {fifo_overflow_drop_test, "overflow with packet dropping", "queue_size=3:drop_pkts_on_overflow=1",
265cabdff1aSopenharmony_ci         0, 0, 0, {0, 0, SLEEPTIME_50_MS}},
266cabdff1aSopenharmony_ci
267cabdff1aSopenharmony_ci        {NULL}
268cabdff1aSopenharmony_ci};
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ciint main(int argc, char *argv[])
271cabdff1aSopenharmony_ci{
272cabdff1aSopenharmony_ci    int i, ret, ret_all = 0;
273cabdff1aSopenharmony_ci
274cabdff1aSopenharmony_ci    for (i = 0; tests[i].test_func; i++) {
275cabdff1aSopenharmony_ci        ret = run_test(&tests[i]);
276cabdff1aSopenharmony_ci        if (!ret_all && ret < 0)
277cabdff1aSopenharmony_ci            ret_all = ret;
278cabdff1aSopenharmony_ci    }
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_ci    return ret;
281cabdff1aSopenharmony_ci}
282