xref: /third_party/ffmpeg/libavformat/prompeg.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Pro-MPEG Code of Practice #3 Release 2 FEC
3cabdff1aSopenharmony_ci * Copyright (c) 2016 Mobibase, France (http://www.mobibase.com)
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
9cabdff1aSopenharmony_ci * License 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 GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci/**
23cabdff1aSopenharmony_ci * @file
24cabdff1aSopenharmony_ci * Pro-MPEG Code of Practice #3 Release 2 FEC protocol
25cabdff1aSopenharmony_ci * @author Vlad Tarca <vlad.tarca@gmail.com>
26cabdff1aSopenharmony_ci */
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_ci/*
29cabdff1aSopenharmony_ci * Reminder:
30cabdff1aSopenharmony_ci
31cabdff1aSopenharmony_ci [RFC 2733] FEC Packet Structure
32cabdff1aSopenharmony_ci
33cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34cabdff1aSopenharmony_ci   |                         RTP Header                            |
35cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36cabdff1aSopenharmony_ci   |                         FEC Header                            |
37cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38cabdff1aSopenharmony_ci   |                         FEC Payload                           |
39cabdff1aSopenharmony_ci   |                                                               |
40cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_ci [RFC 3550] RTP header
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_ci    0                   1                   2                   3
46cabdff1aSopenharmony_ci    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
47cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48cabdff1aSopenharmony_ci   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
49cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50cabdff1aSopenharmony_ci   |                           timestamp                           |
51cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52cabdff1aSopenharmony_ci   |           synchronization source (SSRC) identifier            |
53cabdff1aSopenharmony_ci   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
54cabdff1aSopenharmony_ci   |            contributing source (CSRC) identifiers             |
55cabdff1aSopenharmony_ci   |                             ....                              |
56cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57cabdff1aSopenharmony_ci
58cabdff1aSopenharmony_ci [RFC 3550] RTP header extension (after CSRC)
59cabdff1aSopenharmony_ci
60cabdff1aSopenharmony_ci    0                   1                   2                   3
61cabdff1aSopenharmony_ci    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
62cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63cabdff1aSopenharmony_ci   |      defined by profile       |           length              |
64cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65cabdff1aSopenharmony_ci   |                        header extension                       |
66cabdff1aSopenharmony_ci   |                             ....                              |
67cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68cabdff1aSopenharmony_ci
69cabdff1aSopenharmony_ci [Pro-MPEG COP3] FEC Header
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72cabdff1aSopenharmony_ci   |      SNBase low bits          |        length recovery        |
73cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74cabdff1aSopenharmony_ci   |E| PT recovery |                 mask                          |
75cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76cabdff1aSopenharmony_ci   |                          TS recovery                          |
77cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78cabdff1aSopenharmony_ci   |X|D|type |index|    offset     |      NA       |SNBase ext bits|
79cabdff1aSopenharmony_ci   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci */
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
84cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
85cabdff1aSopenharmony_ci#include "libavutil/opt.h"
86cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
87cabdff1aSopenharmony_ci#include "libavutil/random_seed.h"
88cabdff1aSopenharmony_ci#include "avformat.h"
89cabdff1aSopenharmony_ci#include "config.h"
90cabdff1aSopenharmony_ci#include "url.h"
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_ci#define PROMPEG_RTP_PT 0x60
93cabdff1aSopenharmony_ci#define PROMPEG_FEC_COL 0x0
94cabdff1aSopenharmony_ci#define PROMPEG_FEC_ROW 0x1
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_citypedef struct PrompegFec {
97cabdff1aSopenharmony_ci    uint16_t sn;
98cabdff1aSopenharmony_ci    uint32_t ts;
99cabdff1aSopenharmony_ci    uint8_t *bitstring;
100cabdff1aSopenharmony_ci} PrompegFec;
101cabdff1aSopenharmony_ci
102cabdff1aSopenharmony_citypedef struct PrompegContext {
103cabdff1aSopenharmony_ci    const AVClass *class;
104cabdff1aSopenharmony_ci    URLContext *fec_col_hd, *fec_row_hd;
105cabdff1aSopenharmony_ci    PrompegFec **fec_arr, **fec_col_tmp, **fec_col, *fec_row;
106cabdff1aSopenharmony_ci    int ttl;
107cabdff1aSopenharmony_ci    uint8_t l, d;
108cabdff1aSopenharmony_ci    uint8_t *rtp_buf;
109cabdff1aSopenharmony_ci    uint16_t rtp_col_sn, rtp_row_sn;
110cabdff1aSopenharmony_ci    uint16_t length_recovery;
111cabdff1aSopenharmony_ci    int packet_size;
112cabdff1aSopenharmony_ci    int packet_idx, packet_idx_max;
113cabdff1aSopenharmony_ci    int fec_arr_len;
114cabdff1aSopenharmony_ci    int bitstring_size;
115cabdff1aSopenharmony_ci    int rtp_buf_size;
116cabdff1aSopenharmony_ci    int init;
117cabdff1aSopenharmony_ci    int first;
118cabdff1aSopenharmony_ci} PrompegContext;
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(PrompegContext, x)
121cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM
122cabdff1aSopenharmony_ci
123cabdff1aSopenharmony_cistatic const AVOption options[] = {
124cabdff1aSopenharmony_ci    { "ttl",   "Time to live (in milliseconds, multicast only)", OFFSET(ttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = E },
125cabdff1aSopenharmony_ci    { "l", "FEC L", OFFSET(l), AV_OPT_TYPE_INT, { .i64 =  5 }, 4, 20, .flags = E },
126cabdff1aSopenharmony_ci    { "d", "FEC D", OFFSET(d), AV_OPT_TYPE_INT, { .i64 =  5 }, 4, 20, .flags = E },
127cabdff1aSopenharmony_ci    { NULL }
128cabdff1aSopenharmony_ci};
129cabdff1aSopenharmony_ci
130cabdff1aSopenharmony_cistatic const AVClass prompeg_class = {
131cabdff1aSopenharmony_ci    .class_name = "prompeg",
132cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
133cabdff1aSopenharmony_ci    .option     = options,
134cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
135cabdff1aSopenharmony_ci};
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_cistatic void xor_fast(const uint8_t *in1, const uint8_t *in2, uint8_t *out, int size) {
138cabdff1aSopenharmony_ci    int i, n, s;
139cabdff1aSopenharmony_ci
140cabdff1aSopenharmony_ci#if HAVE_FAST_64BIT
141cabdff1aSopenharmony_ci    uint64_t v1, v2;
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    n = size / sizeof (uint64_t);
144cabdff1aSopenharmony_ci    s = n * sizeof (uint64_t);
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci    for (i = 0; i < n; i++) {
147cabdff1aSopenharmony_ci        v1 = AV_RN64A(in1);
148cabdff1aSopenharmony_ci        v2 = AV_RN64A(in2);
149cabdff1aSopenharmony_ci        AV_WN64A(out, v1 ^ v2);
150cabdff1aSopenharmony_ci        in1 += 8;
151cabdff1aSopenharmony_ci        in2 += 8;
152cabdff1aSopenharmony_ci        out += 8;
153cabdff1aSopenharmony_ci    }
154cabdff1aSopenharmony_ci#else
155cabdff1aSopenharmony_ci    uint32_t v1, v2;
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_ci    n = size / sizeof (uint32_t);
158cabdff1aSopenharmony_ci    s = n * sizeof (uint32_t);
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_ci    for (i = 0; i < n; i++) {
161cabdff1aSopenharmony_ci        v1 = AV_RN32A(in1);
162cabdff1aSopenharmony_ci        v2 = AV_RN32A(in2);
163cabdff1aSopenharmony_ci        AV_WN32A(out, v1 ^ v2);
164cabdff1aSopenharmony_ci        in1 += 4;
165cabdff1aSopenharmony_ci        in2 += 4;
166cabdff1aSopenharmony_ci        out += 4;
167cabdff1aSopenharmony_ci    }
168cabdff1aSopenharmony_ci#endif
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_ci    n = size - s;
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_ci    for (i = 0; i < n; i++) {
173cabdff1aSopenharmony_ci        out[i] = in1[i] ^ in2[i];
174cabdff1aSopenharmony_ci    }
175cabdff1aSopenharmony_ci}
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_cistatic int prompeg_create_bitstring(URLContext *h, const uint8_t *buf, int size,
178cabdff1aSopenharmony_ci        uint8_t **bitstring) {
179cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
180cabdff1aSopenharmony_ci    uint8_t *b;
181cabdff1aSopenharmony_ci
182cabdff1aSopenharmony_ci    if (size < 12 || (buf[0] & 0xc0) != 0x80 || (buf[1] & 0x7f) != 0x21) {
183cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Unsupported stream format (expected MPEG-TS over RTP)\n");
184cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
185cabdff1aSopenharmony_ci    }
186cabdff1aSopenharmony_ci    if (size != s->packet_size) {
187cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "The RTP packet size must be constant (set pkt_size)\n");
188cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
189cabdff1aSopenharmony_ci    }
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    *bitstring = av_malloc(s->bitstring_size);
192cabdff1aSopenharmony_ci    if (!*bitstring) {
193cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Failed to allocate the bitstring buffer\n");
194cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
195cabdff1aSopenharmony_ci    }
196cabdff1aSopenharmony_ci    b = *bitstring;
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_ci    // P, X, CC
199cabdff1aSopenharmony_ci    b[0] = buf[0] & 0x3f;
200cabdff1aSopenharmony_ci    // M, PT
201cabdff1aSopenharmony_ci    b[1] = buf[1];
202cabdff1aSopenharmony_ci    // Timestamp
203cabdff1aSopenharmony_ci    b[2] = buf[4];
204cabdff1aSopenharmony_ci    b[3] = buf[5];
205cabdff1aSopenharmony_ci    b[4] = buf[6];
206cabdff1aSopenharmony_ci    b[5] = buf[7];
207cabdff1aSopenharmony_ci    /*
208cabdff1aSopenharmony_ci     * length_recovery: the unsigned network-ordered sum of lengths of CSRC,
209cabdff1aSopenharmony_ci     * padding, extension and media payload
210cabdff1aSopenharmony_ci     */
211cabdff1aSopenharmony_ci    AV_WB16(b + 6, s->length_recovery);
212cabdff1aSopenharmony_ci    // Payload
213cabdff1aSopenharmony_ci    memcpy(b + 8, buf + 12, s->length_recovery);
214cabdff1aSopenharmony_ci
215cabdff1aSopenharmony_ci    return 0;
216cabdff1aSopenharmony_ci}
217cabdff1aSopenharmony_ci
218cabdff1aSopenharmony_cistatic int prompeg_write_fec(URLContext *h, PrompegFec *fec, uint8_t type) {
219cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
220cabdff1aSopenharmony_ci    URLContext *hd;
221cabdff1aSopenharmony_ci    uint8_t *buf = s->rtp_buf; // zero-filled
222cabdff1aSopenharmony_ci    uint8_t *b = fec->bitstring;
223cabdff1aSopenharmony_ci    uint16_t sn;
224cabdff1aSopenharmony_ci    int ret;
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ci    sn = type == PROMPEG_FEC_COL ? ++s->rtp_col_sn : ++s->rtp_row_sn;
227cabdff1aSopenharmony_ci
228cabdff1aSopenharmony_ci    // V, P, X, CC
229cabdff1aSopenharmony_ci    buf[0] = 0x80 | (b[0] & 0x3f);
230cabdff1aSopenharmony_ci    // M, PT
231cabdff1aSopenharmony_ci    buf[1] = (b[1] & 0x80) | PROMPEG_RTP_PT;
232cabdff1aSopenharmony_ci    // SN
233cabdff1aSopenharmony_ci    AV_WB16(buf + 2, sn);
234cabdff1aSopenharmony_ci    // TS
235cabdff1aSopenharmony_ci    AV_WB32(buf + 4, fec->ts);
236cabdff1aSopenharmony_ci    // CSRC=0
237cabdff1aSopenharmony_ci    //AV_WB32(buf + 8, 0);
238cabdff1aSopenharmony_ci    // SNBase low bits
239cabdff1aSopenharmony_ci    AV_WB16(buf + 12, fec->sn);
240cabdff1aSopenharmony_ci    // Length recovery
241cabdff1aSopenharmony_ci    buf[14] = b[6];
242cabdff1aSopenharmony_ci    buf[15] = b[7];
243cabdff1aSopenharmony_ci    // E=1, PT recovery
244cabdff1aSopenharmony_ci    buf[16] = 0x80 | b[1];
245cabdff1aSopenharmony_ci    // Mask=0
246cabdff1aSopenharmony_ci    //buf[17] = 0x0;
247cabdff1aSopenharmony_ci    //buf[18] = 0x0;
248cabdff1aSopenharmony_ci    //buf[19] = 0x0;
249cabdff1aSopenharmony_ci    // TS recovery
250cabdff1aSopenharmony_ci    buf[20] = b[2];
251cabdff1aSopenharmony_ci    buf[21] = b[3];
252cabdff1aSopenharmony_ci    buf[22] = b[4];
253cabdff1aSopenharmony_ci    buf[23] = b[5];
254cabdff1aSopenharmony_ci    // X=0, D, type=0, index=0
255cabdff1aSopenharmony_ci    buf[24] = type == PROMPEG_FEC_COL ? 0x0 : 0x40;
256cabdff1aSopenharmony_ci    // offset
257cabdff1aSopenharmony_ci    buf[25] = type == PROMPEG_FEC_COL ? s->l : 0x1;
258cabdff1aSopenharmony_ci    // NA
259cabdff1aSopenharmony_ci    buf[26] = type == PROMPEG_FEC_COL ? s->d : s->l;
260cabdff1aSopenharmony_ci    // SNBase ext bits=0
261cabdff1aSopenharmony_ci    //buf[27] = 0x0;
262cabdff1aSopenharmony_ci    // Payload
263cabdff1aSopenharmony_ci    memcpy(buf + 28, b + 8, s->length_recovery);
264cabdff1aSopenharmony_ci
265cabdff1aSopenharmony_ci    hd = type == PROMPEG_FEC_COL ? s->fec_col_hd : s->fec_row_hd;
266cabdff1aSopenharmony_ci    ret = ffurl_write(hd, buf, s->rtp_buf_size);
267cabdff1aSopenharmony_ci    return ret;
268cabdff1aSopenharmony_ci}
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_cistatic int prompeg_open(URLContext *h, const char *uri, int flags) {
271cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
272cabdff1aSopenharmony_ci    AVDictionary *udp_opts = NULL;
273cabdff1aSopenharmony_ci    int rtp_port;
274cabdff1aSopenharmony_ci    char hostname[256];
275cabdff1aSopenharmony_ci    char buf[1024];
276cabdff1aSopenharmony_ci
277cabdff1aSopenharmony_ci    s->fec_col_hd = NULL;
278cabdff1aSopenharmony_ci    s->fec_row_hd = NULL;
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_ci    if (s->l * s->d > 100) {
281cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "L * D must be <= 100\n");
282cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
283cabdff1aSopenharmony_ci    }
284cabdff1aSopenharmony_ci
285cabdff1aSopenharmony_ci    av_url_split(NULL, 0, NULL, 0, hostname, sizeof (hostname), &rtp_port,
286cabdff1aSopenharmony_ci            NULL, 0, uri);
287cabdff1aSopenharmony_ci
288cabdff1aSopenharmony_ci    if (rtp_port < 1 || rtp_port > UINT16_MAX - 4) {
289cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Invalid RTP base port %d\n", rtp_port);
290cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
291cabdff1aSopenharmony_ci    }
292cabdff1aSopenharmony_ci
293cabdff1aSopenharmony_ci    if (s->ttl > 0) {
294cabdff1aSopenharmony_ci        av_dict_set_int(&udp_opts, "ttl", s->ttl, 0);
295cabdff1aSopenharmony_ci    }
296cabdff1aSopenharmony_ci
297cabdff1aSopenharmony_ci    ff_url_join(buf, sizeof (buf), "udp", NULL, hostname, rtp_port + 2, NULL);
298cabdff1aSopenharmony_ci    if (ffurl_open_whitelist(&s->fec_col_hd, buf, flags, &h->interrupt_callback,
299cabdff1aSopenharmony_ci            &udp_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
300cabdff1aSopenharmony_ci        goto fail;
301cabdff1aSopenharmony_ci    ff_url_join(buf, sizeof (buf), "udp", NULL, hostname, rtp_port + 4, NULL);
302cabdff1aSopenharmony_ci    if (ffurl_open_whitelist(&s->fec_row_hd, buf, flags, &h->interrupt_callback,
303cabdff1aSopenharmony_ci            &udp_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
304cabdff1aSopenharmony_ci        goto fail;
305cabdff1aSopenharmony_ci
306cabdff1aSopenharmony_ci    h->max_packet_size = s->fec_col_hd->max_packet_size;
307cabdff1aSopenharmony_ci    s->init = 1;
308cabdff1aSopenharmony_ci
309cabdff1aSopenharmony_ci    av_dict_free(&udp_opts);
310cabdff1aSopenharmony_ci    av_log(h, AV_LOG_INFO, "ProMPEG CoP#3-R2 FEC L=%d D=%d\n", s->l, s->d);
311cabdff1aSopenharmony_ci    return 0;
312cabdff1aSopenharmony_ci
313cabdff1aSopenharmony_cifail:
314cabdff1aSopenharmony_ci    ffurl_closep(&s->fec_col_hd);
315cabdff1aSopenharmony_ci    ffurl_closep(&s->fec_row_hd);
316cabdff1aSopenharmony_ci    av_dict_free(&udp_opts);
317cabdff1aSopenharmony_ci    return AVERROR(EIO);
318cabdff1aSopenharmony_ci}
319cabdff1aSopenharmony_ci
320cabdff1aSopenharmony_cistatic int prompeg_init(URLContext *h, const uint8_t *buf, int size) {
321cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
322cabdff1aSopenharmony_ci    uint32_t seed;
323cabdff1aSopenharmony_ci    int i;
324cabdff1aSopenharmony_ci
325cabdff1aSopenharmony_ci    s->fec_arr = NULL;
326cabdff1aSopenharmony_ci    s->rtp_buf = NULL;
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci    if (size < 12 || size > UINT16_MAX + 12) {
329cabdff1aSopenharmony_ci        av_log(h, AV_LOG_ERROR, "Invalid RTP packet size\n");
330cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
331cabdff1aSopenharmony_ci    }
332cabdff1aSopenharmony_ci
333cabdff1aSopenharmony_ci    s->packet_idx = 0;
334cabdff1aSopenharmony_ci    s->packet_idx_max = s->l * s->d;
335cabdff1aSopenharmony_ci    s->packet_size = size;
336cabdff1aSopenharmony_ci    s->length_recovery = size - 12;
337cabdff1aSopenharmony_ci    s->rtp_buf_size = 28 + s->length_recovery; // 12 + 16: RTP + FEC headers
338cabdff1aSopenharmony_ci    s->bitstring_size = 8 + s->length_recovery; // 8: P, X, CC, M, PT, SN, TS
339cabdff1aSopenharmony_ci    s->fec_arr_len = 1 + 2 * s->l; // row + column tmp + column out
340cabdff1aSopenharmony_ci
341cabdff1aSopenharmony_ci    if (h->flags & AVFMT_FLAG_BITEXACT) {
342cabdff1aSopenharmony_ci        s->rtp_col_sn = 0;
343cabdff1aSopenharmony_ci        s->rtp_row_sn = 0;
344cabdff1aSopenharmony_ci    } else {
345cabdff1aSopenharmony_ci        seed = av_get_random_seed();
346cabdff1aSopenharmony_ci        s->rtp_col_sn = seed & 0x0fff;
347cabdff1aSopenharmony_ci        s->rtp_row_sn = (seed >> 16) & 0x0fff;
348cabdff1aSopenharmony_ci    }
349cabdff1aSopenharmony_ci
350cabdff1aSopenharmony_ci    s->fec_arr = av_malloc_array(s->fec_arr_len, sizeof (PrompegFec*));
351cabdff1aSopenharmony_ci    if (!s->fec_arr) {
352cabdff1aSopenharmony_ci        goto fail;
353cabdff1aSopenharmony_ci    }
354cabdff1aSopenharmony_ci    for (i = 0; i < s->fec_arr_len; i++) {
355cabdff1aSopenharmony_ci        s->fec_arr[i] = av_malloc(sizeof (PrompegFec));
356cabdff1aSopenharmony_ci        if (!s->fec_arr[i]) {
357cabdff1aSopenharmony_ci            goto fail;
358cabdff1aSopenharmony_ci        }
359cabdff1aSopenharmony_ci        s->fec_arr[i]->bitstring = av_malloc_array(s->bitstring_size, sizeof (uint8_t));
360cabdff1aSopenharmony_ci        if (!s->fec_arr[i]->bitstring) {
361cabdff1aSopenharmony_ci            av_freep(&s->fec_arr[i]);
362cabdff1aSopenharmony_ci            goto fail;
363cabdff1aSopenharmony_ci        }
364cabdff1aSopenharmony_ci    }
365cabdff1aSopenharmony_ci    s->fec_row = *s->fec_arr;
366cabdff1aSopenharmony_ci    s->fec_col = s->fec_arr + 1;
367cabdff1aSopenharmony_ci    s->fec_col_tmp = s->fec_arr + 1 + s->l;
368cabdff1aSopenharmony_ci
369cabdff1aSopenharmony_ci    s->rtp_buf = av_malloc_array(s->rtp_buf_size, sizeof (uint8_t));
370cabdff1aSopenharmony_ci    if (!s->rtp_buf) {
371cabdff1aSopenharmony_ci        goto fail;
372cabdff1aSopenharmony_ci    }
373cabdff1aSopenharmony_ci    memset(s->rtp_buf, 0, s->rtp_buf_size);
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci    s->init = 0;
376cabdff1aSopenharmony_ci    s->first = 1;
377cabdff1aSopenharmony_ci
378cabdff1aSopenharmony_ci    return 0;
379cabdff1aSopenharmony_ci
380cabdff1aSopenharmony_cifail:
381cabdff1aSopenharmony_ci    av_log(h, AV_LOG_ERROR, "Failed to allocate the FEC buffer\n");
382cabdff1aSopenharmony_ci    return AVERROR(ENOMEM);
383cabdff1aSopenharmony_ci}
384cabdff1aSopenharmony_ci
385cabdff1aSopenharmony_cistatic int prompeg_write(URLContext *h, const uint8_t *buf, int size) {
386cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
387cabdff1aSopenharmony_ci    PrompegFec *fec_tmp;
388cabdff1aSopenharmony_ci    uint8_t *bitstring = NULL;
389cabdff1aSopenharmony_ci    int col_idx, col_out_idx, row_idx;
390cabdff1aSopenharmony_ci    int ret = 0;
391cabdff1aSopenharmony_ci
392cabdff1aSopenharmony_ci    if (s->init && ((ret = prompeg_init(h, buf, size)) < 0))
393cabdff1aSopenharmony_ci        goto end;
394cabdff1aSopenharmony_ci
395cabdff1aSopenharmony_ci    if ((ret = prompeg_create_bitstring(h, buf, size, &bitstring)) < 0)
396cabdff1aSopenharmony_ci        goto end;
397cabdff1aSopenharmony_ci
398cabdff1aSopenharmony_ci    col_idx = s->packet_idx % s->l;
399cabdff1aSopenharmony_ci    row_idx = s->packet_idx / s->l % s->d;
400cabdff1aSopenharmony_ci
401cabdff1aSopenharmony_ci    // FEC' (row) send block-aligned, xor
402cabdff1aSopenharmony_ci    if (col_idx == 0) {
403cabdff1aSopenharmony_ci        if (!s->first || s->packet_idx > 0) {
404cabdff1aSopenharmony_ci            if ((ret = prompeg_write_fec(h, s->fec_row, PROMPEG_FEC_ROW)) < 0)
405cabdff1aSopenharmony_ci                goto end;
406cabdff1aSopenharmony_ci        }
407cabdff1aSopenharmony_ci        memcpy(s->fec_row->bitstring, bitstring, s->bitstring_size);
408cabdff1aSopenharmony_ci        s->fec_row->sn = AV_RB16(buf + 2);
409cabdff1aSopenharmony_ci        s->fec_row->ts = AV_RB32(buf + 4);
410cabdff1aSopenharmony_ci    } else {
411cabdff1aSopenharmony_ci        xor_fast(s->fec_row->bitstring, bitstring, s->fec_row->bitstring,
412cabdff1aSopenharmony_ci                s->bitstring_size);
413cabdff1aSopenharmony_ci    }
414cabdff1aSopenharmony_ci
415cabdff1aSopenharmony_ci    // FEC (column) xor
416cabdff1aSopenharmony_ci    if (row_idx == 0) {
417cabdff1aSopenharmony_ci        if (!s->first) {
418cabdff1aSopenharmony_ci            // swap fec_col and fec_col_tmp
419cabdff1aSopenharmony_ci            fec_tmp = s->fec_col[col_idx];
420cabdff1aSopenharmony_ci            s->fec_col[col_idx] = s->fec_col_tmp[col_idx];
421cabdff1aSopenharmony_ci            s->fec_col_tmp[col_idx] = fec_tmp;
422cabdff1aSopenharmony_ci        }
423cabdff1aSopenharmony_ci        memcpy(s->fec_col_tmp[col_idx]->bitstring, bitstring, s->bitstring_size);
424cabdff1aSopenharmony_ci        s->fec_col_tmp[col_idx]->sn = AV_RB16(buf + 2);
425cabdff1aSopenharmony_ci        s->fec_col_tmp[col_idx]->ts = AV_RB32(buf + 4);
426cabdff1aSopenharmony_ci    } else {
427cabdff1aSopenharmony_ci        xor_fast(s->fec_col_tmp[col_idx]->bitstring, bitstring,
428cabdff1aSopenharmony_ci                s->fec_col_tmp[col_idx]->bitstring, s->bitstring_size);
429cabdff1aSopenharmony_ci    }
430cabdff1aSopenharmony_ci
431cabdff1aSopenharmony_ci    // FEC (column) send block-aligned
432cabdff1aSopenharmony_ci    if (!s->first && s->packet_idx % s->d == 0) {
433cabdff1aSopenharmony_ci        col_out_idx = s->packet_idx / s->d;
434cabdff1aSopenharmony_ci        if ((ret = prompeg_write_fec(h, s->fec_col[col_out_idx], PROMPEG_FEC_COL)) < 0)
435cabdff1aSopenharmony_ci            goto end;
436cabdff1aSopenharmony_ci    }
437cabdff1aSopenharmony_ci
438cabdff1aSopenharmony_ci    if (++s->packet_idx >= s->packet_idx_max) {
439cabdff1aSopenharmony_ci        s->packet_idx = 0;
440cabdff1aSopenharmony_ci        if (s->first)
441cabdff1aSopenharmony_ci            s->first = 0;
442cabdff1aSopenharmony_ci    }
443cabdff1aSopenharmony_ci
444cabdff1aSopenharmony_ci    ret = size;
445cabdff1aSopenharmony_ci
446cabdff1aSopenharmony_ciend:
447cabdff1aSopenharmony_ci    av_free(bitstring);
448cabdff1aSopenharmony_ci    return ret;
449cabdff1aSopenharmony_ci}
450cabdff1aSopenharmony_ci
451cabdff1aSopenharmony_cistatic int prompeg_close(URLContext *h) {
452cabdff1aSopenharmony_ci    PrompegContext *s = h->priv_data;
453cabdff1aSopenharmony_ci    int i;
454cabdff1aSopenharmony_ci
455cabdff1aSopenharmony_ci    ffurl_closep(&s->fec_col_hd);
456cabdff1aSopenharmony_ci    ffurl_closep(&s->fec_row_hd);
457cabdff1aSopenharmony_ci
458cabdff1aSopenharmony_ci    if (s->fec_arr) {
459cabdff1aSopenharmony_ci        for (i = 0; i < s->fec_arr_len; i++) {
460cabdff1aSopenharmony_ci            av_free(s->fec_arr[i]->bitstring);
461cabdff1aSopenharmony_ci            av_freep(&s->fec_arr[i]);
462cabdff1aSopenharmony_ci        }
463cabdff1aSopenharmony_ci        av_freep(&s->fec_arr);
464cabdff1aSopenharmony_ci    }
465cabdff1aSopenharmony_ci    av_freep(&s->rtp_buf);
466cabdff1aSopenharmony_ci
467cabdff1aSopenharmony_ci    return 0;
468cabdff1aSopenharmony_ci}
469cabdff1aSopenharmony_ci
470cabdff1aSopenharmony_ciconst URLProtocol ff_prompeg_protocol = {
471cabdff1aSopenharmony_ci    .name                      = "prompeg",
472cabdff1aSopenharmony_ci    .url_open                  = prompeg_open,
473cabdff1aSopenharmony_ci    .url_write                 = prompeg_write,
474cabdff1aSopenharmony_ci    .url_close                 = prompeg_close,
475cabdff1aSopenharmony_ci    .priv_data_size            = sizeof(PrompegContext),
476cabdff1aSopenharmony_ci    .flags                     = URL_PROTOCOL_FLAG_NETWORK,
477cabdff1aSopenharmony_ci    .priv_data_class           = &prompeg_class,
478cabdff1aSopenharmony_ci};
479