LCOV - code coverage report
Current view: top level - libavformat - prompeg.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 188 0.0 %
Date: 2017-12-15 18:13:28 Functions: 0 7 0.0 %

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

Generated by: LCOV version 1.13