1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * RTMP network protocol
3cabdff1aSopenharmony_ci * Copyright (c) 2009 Konstantin Shishkov
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 * RTMP protocol
25cabdff1aSopenharmony_ci */
26cabdff1aSopenharmony_ci
27cabdff1aSopenharmony_ci#include "config_components.h"
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_ci#include "libavcodec/bytestream.h"
30cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
31cabdff1aSopenharmony_ci#include "libavutil/base64.h"
32cabdff1aSopenharmony_ci#include "libavutil/intfloat.h"
33cabdff1aSopenharmony_ci#include "libavutil/lfg.h"
34cabdff1aSopenharmony_ci#include "libavutil/md5.h"
35cabdff1aSopenharmony_ci#include "libavutil/opt.h"
36cabdff1aSopenharmony_ci#include "libavutil/random_seed.h"
37cabdff1aSopenharmony_ci#include "avformat.h"
38cabdff1aSopenharmony_ci#include "internal.h"
39cabdff1aSopenharmony_ci
40cabdff1aSopenharmony_ci#include "network.h"
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_ci#include "flv.h"
43cabdff1aSopenharmony_ci#include "rtmp.h"
44cabdff1aSopenharmony_ci#include "rtmpcrypt.h"
45cabdff1aSopenharmony_ci#include "rtmppkt.h"
46cabdff1aSopenharmony_ci#include "url.h"
47cabdff1aSopenharmony_ci#include "version.h"
48cabdff1aSopenharmony_ci
49cabdff1aSopenharmony_ci#if CONFIG_ZLIB
50cabdff1aSopenharmony_ci#include <zlib.h>
51cabdff1aSopenharmony_ci#endif
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci#define APP_MAX_LENGTH 1024
54cabdff1aSopenharmony_ci#define TCURL_MAX_LENGTH 1024
55cabdff1aSopenharmony_ci#define FLASHVER_MAX_LENGTH 64
56cabdff1aSopenharmony_ci#define RTMP_PKTDATA_DEFAULT_SIZE 4096
57cabdff1aSopenharmony_ci#define RTMP_HEADER 11
58cabdff1aSopenharmony_ci
59cabdff1aSopenharmony_ci/** RTMP protocol handler state */
60cabdff1aSopenharmony_citypedef enum {
61cabdff1aSopenharmony_ci    STATE_START,      ///< client has not done anything yet
62cabdff1aSopenharmony_ci    STATE_HANDSHAKED, ///< client has performed handshake
63cabdff1aSopenharmony_ci    STATE_FCPUBLISH,  ///< client FCPublishing stream (for output)
64cabdff1aSopenharmony_ci    STATE_PLAYING,    ///< client has started receiving multimedia data from server
65cabdff1aSopenharmony_ci    STATE_SEEKING,    ///< client has started the seek operation. Back on STATE_PLAYING when the time comes
66cabdff1aSopenharmony_ci    STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
67cabdff1aSopenharmony_ci    STATE_RECEIVING,  ///< received a publish command (for input)
68cabdff1aSopenharmony_ci    STATE_SENDING,    ///< received a play command (for output)
69cabdff1aSopenharmony_ci    STATE_STOPPED,    ///< the broadcast has been stopped
70cabdff1aSopenharmony_ci} ClientState;
71cabdff1aSopenharmony_ci
72cabdff1aSopenharmony_citypedef struct TrackedMethod {
73cabdff1aSopenharmony_ci    char *name;
74cabdff1aSopenharmony_ci    int id;
75cabdff1aSopenharmony_ci} TrackedMethod;
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci/** protocol handler context */
78cabdff1aSopenharmony_citypedef struct RTMPContext {
79cabdff1aSopenharmony_ci    const AVClass *class;
80cabdff1aSopenharmony_ci    URLContext*   stream;                     ///< TCP stream used in interactions with RTMP server
81cabdff1aSopenharmony_ci    RTMPPacket    *prev_pkt[2];               ///< packet history used when reading and sending packets ([0] for reading, [1] for writing)
82cabdff1aSopenharmony_ci    int           nb_prev_pkt[2];             ///< number of elements in prev_pkt
83cabdff1aSopenharmony_ci    int           in_chunk_size;              ///< size of the chunks incoming RTMP packets are divided into
84cabdff1aSopenharmony_ci    int           out_chunk_size;             ///< size of the chunks outgoing RTMP packets are divided into
85cabdff1aSopenharmony_ci    int           is_input;                   ///< input/output flag
86cabdff1aSopenharmony_ci    char          *playpath;                  ///< stream identifier to play (with possible "mp4:" prefix)
87cabdff1aSopenharmony_ci    int           live;                       ///< 0: recorded, -1: live, -2: both
88cabdff1aSopenharmony_ci    char          *app;                       ///< name of application
89cabdff1aSopenharmony_ci    char          *conn;                      ///< append arbitrary AMF data to the Connect message
90cabdff1aSopenharmony_ci    ClientState   state;                      ///< current state
91cabdff1aSopenharmony_ci    int           stream_id;                  ///< ID assigned by the server for the stream
92cabdff1aSopenharmony_ci    uint8_t*      flv_data;                   ///< buffer with data for demuxer
93cabdff1aSopenharmony_ci    int           flv_size;                   ///< current buffer size
94cabdff1aSopenharmony_ci    int           flv_off;                    ///< number of bytes read from current buffer
95cabdff1aSopenharmony_ci    int           flv_nb_packets;             ///< number of flv packets published
96cabdff1aSopenharmony_ci    RTMPPacket    out_pkt;                    ///< rtmp packet, created from flv a/v or metadata (for output)
97cabdff1aSopenharmony_ci    uint32_t      receive_report_size;        ///< number of bytes after which we should report the number of received bytes to the peer
98cabdff1aSopenharmony_ci    uint64_t      bytes_read;                 ///< number of bytes read from server
99cabdff1aSopenharmony_ci    uint64_t      last_bytes_read;            ///< number of bytes read last reported to server
100cabdff1aSopenharmony_ci    uint32_t      last_timestamp;             ///< last timestamp received in a packet
101cabdff1aSopenharmony_ci    int           skip_bytes;                 ///< number of bytes to skip from the input FLV stream in the next write call
102cabdff1aSopenharmony_ci    int           has_audio;                  ///< presence of audio data
103cabdff1aSopenharmony_ci    int           has_video;                  ///< presence of video data
104cabdff1aSopenharmony_ci    int           received_metadata;          ///< Indicates if we have received metadata about the streams
105cabdff1aSopenharmony_ci    uint8_t       flv_header[RTMP_HEADER];    ///< partial incoming flv packet header
106cabdff1aSopenharmony_ci    int           flv_header_bytes;           ///< number of initialized bytes in flv_header
107cabdff1aSopenharmony_ci    int           nb_invokes;                 ///< keeps track of invoke messages
108cabdff1aSopenharmony_ci    char*         tcurl;                      ///< url of the target stream
109cabdff1aSopenharmony_ci    char*         flashver;                   ///< version of the flash plugin
110cabdff1aSopenharmony_ci    char*         swfhash;                    ///< SHA256 hash of the decompressed SWF file (32 bytes)
111cabdff1aSopenharmony_ci    int           swfhash_len;                ///< length of the SHA256 hash
112cabdff1aSopenharmony_ci    int           swfsize;                    ///< size of the decompressed SWF file
113cabdff1aSopenharmony_ci    char*         swfurl;                     ///< url of the swf player
114cabdff1aSopenharmony_ci    char*         swfverify;                  ///< URL to player swf file, compute hash/size automatically
115cabdff1aSopenharmony_ci    char          swfverification[42];        ///< hash of the SWF verification
116cabdff1aSopenharmony_ci    char*         pageurl;                    ///< url of the web page
117cabdff1aSopenharmony_ci    char*         subscribe;                  ///< name of live stream to subscribe
118cabdff1aSopenharmony_ci    int           max_sent_unacked;           ///< max unacked sent bytes
119cabdff1aSopenharmony_ci    int           client_buffer_time;         ///< client buffer time in ms
120cabdff1aSopenharmony_ci    int           flush_interval;             ///< number of packets flushed in the same request (RTMPT only)
121cabdff1aSopenharmony_ci    int           encrypted;                  ///< use an encrypted connection (RTMPE only)
122cabdff1aSopenharmony_ci    TrackedMethod*tracked_methods;            ///< tracked methods buffer
123cabdff1aSopenharmony_ci    int           nb_tracked_methods;         ///< number of tracked methods
124cabdff1aSopenharmony_ci    int           tracked_methods_size;       ///< size of the tracked methods buffer
125cabdff1aSopenharmony_ci    int           listen;                     ///< listen mode flag
126cabdff1aSopenharmony_ci    int           listen_timeout;             ///< listen timeout to wait for new connections
127cabdff1aSopenharmony_ci    int           nb_streamid;                ///< The next stream id to return on createStream calls
128cabdff1aSopenharmony_ci    double        duration;                   ///< Duration of the stream in seconds as returned by the server (only valid if non-zero)
129cabdff1aSopenharmony_ci    int           tcp_nodelay;                ///< Use TCP_NODELAY to disable Nagle's algorithm if set to 1
130cabdff1aSopenharmony_ci    char          username[50];
131cabdff1aSopenharmony_ci    char          password[50];
132cabdff1aSopenharmony_ci    char          auth_params[500];
133cabdff1aSopenharmony_ci    int           do_reconnect;
134cabdff1aSopenharmony_ci    int           auth_tried;
135cabdff1aSopenharmony_ci} RTMPContext;
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci#define PLAYER_KEY_OPEN_PART_LEN 30   ///< length of partial key used for first client digest signing
138cabdff1aSopenharmony_ci/** Client key used for digest signing */
139cabdff1aSopenharmony_cistatic const uint8_t rtmp_player_key[] = {
140cabdff1aSopenharmony_ci    'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
141cabdff1aSopenharmony_ci    'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci    0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
144cabdff1aSopenharmony_ci    0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
145cabdff1aSopenharmony_ci    0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
146cabdff1aSopenharmony_ci};
147cabdff1aSopenharmony_ci
148cabdff1aSopenharmony_ci#define SERVER_KEY_OPEN_PART_LEN 36   ///< length of partial key used for first server digest signing
149cabdff1aSopenharmony_ci/** Key used for RTMP server digest signing */
150cabdff1aSopenharmony_cistatic const uint8_t rtmp_server_key[] = {
151cabdff1aSopenharmony_ci    'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
152cabdff1aSopenharmony_ci    'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
153cabdff1aSopenharmony_ci    'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
154cabdff1aSopenharmony_ci
155cabdff1aSopenharmony_ci    0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
156cabdff1aSopenharmony_ci    0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
157cabdff1aSopenharmony_ci    0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
158cabdff1aSopenharmony_ci};
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_cistatic int handle_chunk_size(URLContext *s, RTMPPacket *pkt);
161cabdff1aSopenharmony_cistatic int handle_window_ack_size(URLContext *s, RTMPPacket *pkt);
162cabdff1aSopenharmony_cistatic int handle_set_peer_bw(URLContext *s, RTMPPacket *pkt);
163cabdff1aSopenharmony_ci
164cabdff1aSopenharmony_cistatic int add_tracked_method(RTMPContext *rt, const char *name, int id)
165cabdff1aSopenharmony_ci{
166cabdff1aSopenharmony_ci    int err;
167cabdff1aSopenharmony_ci
168cabdff1aSopenharmony_ci    if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) {
169cabdff1aSopenharmony_ci        rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2;
170cabdff1aSopenharmony_ci        if ((err = av_reallocp_array(&rt->tracked_methods, rt->tracked_methods_size,
171cabdff1aSopenharmony_ci                               sizeof(*rt->tracked_methods))) < 0) {
172cabdff1aSopenharmony_ci            rt->nb_tracked_methods = 0;
173cabdff1aSopenharmony_ci            rt->tracked_methods_size = 0;
174cabdff1aSopenharmony_ci            return err;
175cabdff1aSopenharmony_ci        }
176cabdff1aSopenharmony_ci    }
177cabdff1aSopenharmony_ci
178cabdff1aSopenharmony_ci    rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
179cabdff1aSopenharmony_ci    if (!rt->tracked_methods[rt->nb_tracked_methods].name)
180cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
181cabdff1aSopenharmony_ci    rt->tracked_methods[rt->nb_tracked_methods].id = id;
182cabdff1aSopenharmony_ci    rt->nb_tracked_methods++;
183cabdff1aSopenharmony_ci
184cabdff1aSopenharmony_ci    return 0;
185cabdff1aSopenharmony_ci}
186cabdff1aSopenharmony_ci
187cabdff1aSopenharmony_cistatic void del_tracked_method(RTMPContext *rt, int index)
188cabdff1aSopenharmony_ci{
189cabdff1aSopenharmony_ci    memmove(&rt->tracked_methods[index], &rt->tracked_methods[index + 1],
190cabdff1aSopenharmony_ci            sizeof(*rt->tracked_methods) * (rt->nb_tracked_methods - index - 1));
191cabdff1aSopenharmony_ci    rt->nb_tracked_methods--;
192cabdff1aSopenharmony_ci}
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_cistatic int find_tracked_method(URLContext *s, RTMPPacket *pkt, int offset,
195cabdff1aSopenharmony_ci                               char **tracked_method)
196cabdff1aSopenharmony_ci{
197cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
198cabdff1aSopenharmony_ci    GetByteContext gbc;
199cabdff1aSopenharmony_ci    double pkt_id;
200cabdff1aSopenharmony_ci    int ret;
201cabdff1aSopenharmony_ci    int i;
202cabdff1aSopenharmony_ci
203cabdff1aSopenharmony_ci    bytestream2_init(&gbc, pkt->data + offset, pkt->size - offset);
204cabdff1aSopenharmony_ci    if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
205cabdff1aSopenharmony_ci        return ret;
206cabdff1aSopenharmony_ci
207cabdff1aSopenharmony_ci    for (i = 0; i < rt->nb_tracked_methods; i++) {
208cabdff1aSopenharmony_ci        if (rt->tracked_methods[i].id != pkt_id)
209cabdff1aSopenharmony_ci            continue;
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_ci        *tracked_method = rt->tracked_methods[i].name;
212cabdff1aSopenharmony_ci        del_tracked_method(rt, i);
213cabdff1aSopenharmony_ci        break;
214cabdff1aSopenharmony_ci    }
215cabdff1aSopenharmony_ci
216cabdff1aSopenharmony_ci    return 0;
217cabdff1aSopenharmony_ci}
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_cistatic void free_tracked_methods(RTMPContext *rt)
220cabdff1aSopenharmony_ci{
221cabdff1aSopenharmony_ci    int i;
222cabdff1aSopenharmony_ci
223cabdff1aSopenharmony_ci    for (i = 0; i < rt->nb_tracked_methods; i ++)
224cabdff1aSopenharmony_ci        av_freep(&rt->tracked_methods[i].name);
225cabdff1aSopenharmony_ci    av_freep(&rt->tracked_methods);
226cabdff1aSopenharmony_ci    rt->tracked_methods_size = 0;
227cabdff1aSopenharmony_ci    rt->nb_tracked_methods   = 0;
228cabdff1aSopenharmony_ci}
229cabdff1aSopenharmony_ci
230cabdff1aSopenharmony_cistatic int rtmp_send_packet(RTMPContext *rt, RTMPPacket *pkt, int track)
231cabdff1aSopenharmony_ci{
232cabdff1aSopenharmony_ci    int ret;
233cabdff1aSopenharmony_ci
234cabdff1aSopenharmony_ci    if (pkt->type == RTMP_PT_INVOKE && track) {
235cabdff1aSopenharmony_ci        GetByteContext gbc;
236cabdff1aSopenharmony_ci        char name[128];
237cabdff1aSopenharmony_ci        double pkt_id;
238cabdff1aSopenharmony_ci        int len;
239cabdff1aSopenharmony_ci
240cabdff1aSopenharmony_ci        bytestream2_init(&gbc, pkt->data, pkt->size);
241cabdff1aSopenharmony_ci        if ((ret = ff_amf_read_string(&gbc, name, sizeof(name), &len)) < 0)
242cabdff1aSopenharmony_ci            goto fail;
243cabdff1aSopenharmony_ci
244cabdff1aSopenharmony_ci        if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
245cabdff1aSopenharmony_ci            goto fail;
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_ci        if ((ret = add_tracked_method(rt, name, pkt_id)) < 0)
248cabdff1aSopenharmony_ci            goto fail;
249cabdff1aSopenharmony_ci    }
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
252cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
253cabdff1aSopenharmony_cifail:
254cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(pkt);
255cabdff1aSopenharmony_ci    return ret;
256cabdff1aSopenharmony_ci}
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_cistatic int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
259cabdff1aSopenharmony_ci{
260cabdff1aSopenharmony_ci    char *field, *value;
261cabdff1aSopenharmony_ci    char type;
262cabdff1aSopenharmony_ci
263cabdff1aSopenharmony_ci    /* The type must be B for Boolean, N for number, S for string, O for
264cabdff1aSopenharmony_ci     * object, or Z for null. For Booleans the data must be either 0 or 1 for
265cabdff1aSopenharmony_ci     * FALSE or TRUE, respectively. Likewise for Objects the data must be
266cabdff1aSopenharmony_ci     * 0 or 1 to end or begin an object, respectively. Data items in subobjects
267cabdff1aSopenharmony_ci     * may be named, by prefixing the type with 'N' and specifying the name
268cabdff1aSopenharmony_ci     * before the value (ie. NB:myFlag:1). This option may be used multiple times
269cabdff1aSopenharmony_ci     * to construct arbitrary AMF sequences. */
270cabdff1aSopenharmony_ci    if (param[0] && param[1] == ':') {
271cabdff1aSopenharmony_ci        type = param[0];
272cabdff1aSopenharmony_ci        value = param + 2;
273cabdff1aSopenharmony_ci    } else if (param[0] == 'N' && param[1] && param[2] == ':') {
274cabdff1aSopenharmony_ci        type = param[1];
275cabdff1aSopenharmony_ci        field = param + 3;
276cabdff1aSopenharmony_ci        value = strchr(field, ':');
277cabdff1aSopenharmony_ci        if (!value)
278cabdff1aSopenharmony_ci            goto fail;
279cabdff1aSopenharmony_ci        *value = '\0';
280cabdff1aSopenharmony_ci        value++;
281cabdff1aSopenharmony_ci
282cabdff1aSopenharmony_ci        ff_amf_write_field_name(p, field);
283cabdff1aSopenharmony_ci    } else {
284cabdff1aSopenharmony_ci        goto fail;
285cabdff1aSopenharmony_ci    }
286cabdff1aSopenharmony_ci
287cabdff1aSopenharmony_ci    switch (type) {
288cabdff1aSopenharmony_ci    case 'B':
289cabdff1aSopenharmony_ci        ff_amf_write_bool(p, value[0] != '0');
290cabdff1aSopenharmony_ci        break;
291cabdff1aSopenharmony_ci    case 'S':
292cabdff1aSopenharmony_ci        ff_amf_write_string(p, value);
293cabdff1aSopenharmony_ci        break;
294cabdff1aSopenharmony_ci    case 'N':
295cabdff1aSopenharmony_ci        ff_amf_write_number(p, strtod(value, NULL));
296cabdff1aSopenharmony_ci        break;
297cabdff1aSopenharmony_ci    case 'Z':
298cabdff1aSopenharmony_ci        ff_amf_write_null(p);
299cabdff1aSopenharmony_ci        break;
300cabdff1aSopenharmony_ci    case 'O':
301cabdff1aSopenharmony_ci        if (value[0] != '0')
302cabdff1aSopenharmony_ci            ff_amf_write_object_start(p);
303cabdff1aSopenharmony_ci        else
304cabdff1aSopenharmony_ci            ff_amf_write_object_end(p);
305cabdff1aSopenharmony_ci        break;
306cabdff1aSopenharmony_ci    default:
307cabdff1aSopenharmony_ci        goto fail;
308cabdff1aSopenharmony_ci        break;
309cabdff1aSopenharmony_ci    }
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci    return 0;
312cabdff1aSopenharmony_ci
313cabdff1aSopenharmony_cifail:
314cabdff1aSopenharmony_ci    av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
315cabdff1aSopenharmony_ci    return AVERROR(EINVAL);
316cabdff1aSopenharmony_ci}
317cabdff1aSopenharmony_ci
318cabdff1aSopenharmony_ci/**
319cabdff1aSopenharmony_ci * Generate 'connect' call and send it to the server.
320cabdff1aSopenharmony_ci */
321cabdff1aSopenharmony_cistatic int gen_connect(URLContext *s, RTMPContext *rt)
322cabdff1aSopenharmony_ci{
323cabdff1aSopenharmony_ci    RTMPPacket pkt;
324cabdff1aSopenharmony_ci    uint8_t *p;
325cabdff1aSopenharmony_ci    int ret;
326cabdff1aSopenharmony_ci
327cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
328cabdff1aSopenharmony_ci                                     0, 4096 + APP_MAX_LENGTH)) < 0)
329cabdff1aSopenharmony_ci        return ret;
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci    p = pkt.data;
332cabdff1aSopenharmony_ci
333cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "connect");
334cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
335cabdff1aSopenharmony_ci    ff_amf_write_object_start(&p);
336cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "app");
337cabdff1aSopenharmony_ci    ff_amf_write_string2(&p, rt->app, rt->auth_params);
338cabdff1aSopenharmony_ci
339cabdff1aSopenharmony_ci    if (!rt->is_input) {
340cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "type");
341cabdff1aSopenharmony_ci        ff_amf_write_string(&p, "nonprivate");
342cabdff1aSopenharmony_ci    }
343cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "flashVer");
344cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->flashver);
345cabdff1aSopenharmony_ci
346cabdff1aSopenharmony_ci    if (rt->swfurl || rt->swfverify) {
347cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "swfUrl");
348cabdff1aSopenharmony_ci        if (rt->swfurl)
349cabdff1aSopenharmony_ci            ff_amf_write_string(&p, rt->swfurl);
350cabdff1aSopenharmony_ci        else
351cabdff1aSopenharmony_ci            ff_amf_write_string(&p, rt->swfverify);
352cabdff1aSopenharmony_ci    }
353cabdff1aSopenharmony_ci
354cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "tcUrl");
355cabdff1aSopenharmony_ci    ff_amf_write_string2(&p, rt->tcurl, rt->auth_params);
356cabdff1aSopenharmony_ci    if (rt->is_input) {
357cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "fpad");
358cabdff1aSopenharmony_ci        ff_amf_write_bool(&p, 0);
359cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "capabilities");
360cabdff1aSopenharmony_ci        ff_amf_write_number(&p, 15.0);
361cabdff1aSopenharmony_ci
362cabdff1aSopenharmony_ci        /* Tell the server we support all the audio codecs except
363cabdff1aSopenharmony_ci         * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
364cabdff1aSopenharmony_ci         * which are unused in the RTMP protocol implementation. */
365cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "audioCodecs");
366cabdff1aSopenharmony_ci        ff_amf_write_number(&p, 4071.0);
367cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "videoCodecs");
368cabdff1aSopenharmony_ci        ff_amf_write_number(&p, 252.0);
369cabdff1aSopenharmony_ci        ff_amf_write_field_name(&p, "videoFunction");
370cabdff1aSopenharmony_ci        ff_amf_write_number(&p, 1.0);
371cabdff1aSopenharmony_ci
372cabdff1aSopenharmony_ci        if (rt->pageurl) {
373cabdff1aSopenharmony_ci            ff_amf_write_field_name(&p, "pageUrl");
374cabdff1aSopenharmony_ci            ff_amf_write_string(&p, rt->pageurl);
375cabdff1aSopenharmony_ci        }
376cabdff1aSopenharmony_ci    }
377cabdff1aSopenharmony_ci    ff_amf_write_object_end(&p);
378cabdff1aSopenharmony_ci
379cabdff1aSopenharmony_ci    if (rt->conn) {
380cabdff1aSopenharmony_ci        char *param = rt->conn;
381cabdff1aSopenharmony_ci
382cabdff1aSopenharmony_ci        // Write arbitrary AMF data to the Connect message.
383cabdff1aSopenharmony_ci        while (param) {
384cabdff1aSopenharmony_ci            char *sep;
385cabdff1aSopenharmony_ci            param += strspn(param, " ");
386cabdff1aSopenharmony_ci            if (!*param)
387cabdff1aSopenharmony_ci                break;
388cabdff1aSopenharmony_ci            sep = strchr(param, ' ');
389cabdff1aSopenharmony_ci            if (sep)
390cabdff1aSopenharmony_ci                *sep = '\0';
391cabdff1aSopenharmony_ci            if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
392cabdff1aSopenharmony_ci                // Invalid AMF parameter.
393cabdff1aSopenharmony_ci                ff_rtmp_packet_destroy(&pkt);
394cabdff1aSopenharmony_ci                return ret;
395cabdff1aSopenharmony_ci            }
396cabdff1aSopenharmony_ci
397cabdff1aSopenharmony_ci            if (sep)
398cabdff1aSopenharmony_ci                param = sep + 1;
399cabdff1aSopenharmony_ci            else
400cabdff1aSopenharmony_ci                break;
401cabdff1aSopenharmony_ci        }
402cabdff1aSopenharmony_ci    }
403cabdff1aSopenharmony_ci
404cabdff1aSopenharmony_ci    pkt.size = p - pkt.data;
405cabdff1aSopenharmony_ci
406cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
407cabdff1aSopenharmony_ci}
408cabdff1aSopenharmony_ci
409cabdff1aSopenharmony_ci
410cabdff1aSopenharmony_ci#define RTMP_CTRL_ABORT_MESSAGE  (2)
411cabdff1aSopenharmony_ci
412cabdff1aSopenharmony_cistatic int read_connect(URLContext *s, RTMPContext *rt)
413cabdff1aSopenharmony_ci{
414cabdff1aSopenharmony_ci    RTMPPacket pkt = { 0 };
415cabdff1aSopenharmony_ci    uint8_t *p;
416cabdff1aSopenharmony_ci    const uint8_t *cp;
417cabdff1aSopenharmony_ci    int ret;
418cabdff1aSopenharmony_ci    char command[64];
419cabdff1aSopenharmony_ci    int stringlen;
420cabdff1aSopenharmony_ci    double seqnum;
421cabdff1aSopenharmony_ci    uint8_t tmpstr[256];
422cabdff1aSopenharmony_ci    GetByteContext gbc;
423cabdff1aSopenharmony_ci
424cabdff1aSopenharmony_ci    // handle RTMP Protocol Control Messages
425cabdff1aSopenharmony_ci    for (;;) {
426cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
427cabdff1aSopenharmony_ci                                       &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
428cabdff1aSopenharmony_ci            return ret;
429cabdff1aSopenharmony_ci#ifdef DEBUG
430cabdff1aSopenharmony_ci        ff_rtmp_packet_dump(s, &pkt);
431cabdff1aSopenharmony_ci#endif
432cabdff1aSopenharmony_ci        if (pkt.type == RTMP_PT_CHUNK_SIZE) {
433cabdff1aSopenharmony_ci            if ((ret = handle_chunk_size(s, &pkt)) < 0) {
434cabdff1aSopenharmony_ci                ff_rtmp_packet_destroy(&pkt);
435cabdff1aSopenharmony_ci                return ret;
436cabdff1aSopenharmony_ci            }
437cabdff1aSopenharmony_ci        } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) {
438cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "received abort message\n");
439cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&pkt);
440cabdff1aSopenharmony_ci            return AVERROR_UNKNOWN;
441cabdff1aSopenharmony_ci        } else if (pkt.type == RTMP_PT_BYTES_READ) {
442cabdff1aSopenharmony_ci            av_log(s, AV_LOG_TRACE, "received acknowledgement\n");
443cabdff1aSopenharmony_ci        } else if (pkt.type == RTMP_PT_WINDOW_ACK_SIZE) {
444cabdff1aSopenharmony_ci            if ((ret = handle_window_ack_size(s, &pkt)) < 0) {
445cabdff1aSopenharmony_ci                ff_rtmp_packet_destroy(&pkt);
446cabdff1aSopenharmony_ci                return ret;
447cabdff1aSopenharmony_ci            }
448cabdff1aSopenharmony_ci        } else if (pkt.type == RTMP_PT_SET_PEER_BW) {
449cabdff1aSopenharmony_ci            if ((ret = handle_set_peer_bw(s, &pkt)) < 0) {
450cabdff1aSopenharmony_ci                ff_rtmp_packet_destroy(&pkt);
451cabdff1aSopenharmony_ci                return ret;
452cabdff1aSopenharmony_ci            }
453cabdff1aSopenharmony_ci        } else if (pkt.type == RTMP_PT_INVOKE) {
454cabdff1aSopenharmony_ci            // received RTMP Command Message
455cabdff1aSopenharmony_ci            break;
456cabdff1aSopenharmony_ci        } else {
457cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Unknown control message type (%d)\n", pkt.type);
458cabdff1aSopenharmony_ci        }
459cabdff1aSopenharmony_ci        ff_rtmp_packet_destroy(&pkt);
460cabdff1aSopenharmony_ci    }
461cabdff1aSopenharmony_ci
462cabdff1aSopenharmony_ci    cp = pkt.data;
463cabdff1aSopenharmony_ci    bytestream2_init(&gbc, cp, pkt.size);
464cabdff1aSopenharmony_ci    if (ff_amf_read_string(&gbc, command, sizeof(command), &stringlen)) {
465cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Unable to read command string\n");
466cabdff1aSopenharmony_ci        ff_rtmp_packet_destroy(&pkt);
467cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
468cabdff1aSopenharmony_ci    }
469cabdff1aSopenharmony_ci    if (strcmp(command, "connect")) {
470cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Expecting connect, got %s\n", command);
471cabdff1aSopenharmony_ci        ff_rtmp_packet_destroy(&pkt);
472cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
473cabdff1aSopenharmony_ci    }
474cabdff1aSopenharmony_ci    ret = ff_amf_read_number(&gbc, &seqnum);
475cabdff1aSopenharmony_ci    if (ret)
476cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "SeqNum not found\n");
477cabdff1aSopenharmony_ci    /* Here one could parse an AMF Object with data as flashVers and others. */
478cabdff1aSopenharmony_ci    ret = ff_amf_get_field_value(gbc.buffer,
479cabdff1aSopenharmony_ci                                 gbc.buffer + bytestream2_get_bytes_left(&gbc),
480cabdff1aSopenharmony_ci                                 "app", tmpstr, sizeof(tmpstr));
481cabdff1aSopenharmony_ci    if (ret)
482cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "App field not found in connect\n");
483cabdff1aSopenharmony_ci    if (!ret && strcmp(tmpstr, rt->app))
484cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "App field don't match up: %s <-> %s\n",
485cabdff1aSopenharmony_ci               tmpstr, rt->app);
486cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
487cabdff1aSopenharmony_ci
488cabdff1aSopenharmony_ci    // Send Window Acknowledgement Size (as defined in specification)
489cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
490cabdff1aSopenharmony_ci                                     RTMP_PT_WINDOW_ACK_SIZE, 0, 4)) < 0)
491cabdff1aSopenharmony_ci        return ret;
492cabdff1aSopenharmony_ci    p = pkt.data;
493cabdff1aSopenharmony_ci    // Inform the peer about how often we want acknowledgements about what
494cabdff1aSopenharmony_ci    // we send. (We don't check for the acknowledgements currently.)
495cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->max_sent_unacked);
496cabdff1aSopenharmony_ci    pkt.size = p - pkt.data;
497cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
498cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
499cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
500cabdff1aSopenharmony_ci    if (ret < 0)
501cabdff1aSopenharmony_ci        return ret;
502cabdff1aSopenharmony_ci    // Set Peer Bandwidth
503cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
504cabdff1aSopenharmony_ci                                     RTMP_PT_SET_PEER_BW, 0, 5)) < 0)
505cabdff1aSopenharmony_ci        return ret;
506cabdff1aSopenharmony_ci    p = pkt.data;
507cabdff1aSopenharmony_ci    // Tell the peer to only send this many bytes unless it gets acknowledgements.
508cabdff1aSopenharmony_ci    // This could be any arbitrary value we want here.
509cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->max_sent_unacked);
510cabdff1aSopenharmony_ci    bytestream_put_byte(&p, 2); // dynamic
511cabdff1aSopenharmony_ci    pkt.size = p - pkt.data;
512cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
513cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
514cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
515cabdff1aSopenharmony_ci    if (ret < 0)
516cabdff1aSopenharmony_ci        return ret;
517cabdff1aSopenharmony_ci
518cabdff1aSopenharmony_ci    // User control
519cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
520cabdff1aSopenharmony_ci                                     RTMP_PT_USER_CONTROL, 0, 6)) < 0)
521cabdff1aSopenharmony_ci        return ret;
522cabdff1aSopenharmony_ci
523cabdff1aSopenharmony_ci    p = pkt.data;
524cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 0); // 0 -> Stream Begin
525cabdff1aSopenharmony_ci    bytestream_put_be32(&p, 0); // Stream 0
526cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
527cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
528cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
529cabdff1aSopenharmony_ci    if (ret < 0)
530cabdff1aSopenharmony_ci        return ret;
531cabdff1aSopenharmony_ci
532cabdff1aSopenharmony_ci    // Chunk size
533cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
534cabdff1aSopenharmony_ci                                     RTMP_PT_CHUNK_SIZE, 0, 4)) < 0)
535cabdff1aSopenharmony_ci        return ret;
536cabdff1aSopenharmony_ci
537cabdff1aSopenharmony_ci    p = pkt.data;
538cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->out_chunk_size);
539cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
540cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
541cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
542cabdff1aSopenharmony_ci    if (ret < 0)
543cabdff1aSopenharmony_ci        return ret;
544cabdff1aSopenharmony_ci
545cabdff1aSopenharmony_ci    // Send _result NetConnection.Connect.Success to connect
546cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
547cabdff1aSopenharmony_ci                                     RTMP_PT_INVOKE, 0,
548cabdff1aSopenharmony_ci                                     RTMP_PKTDATA_DEFAULT_SIZE)) < 0)
549cabdff1aSopenharmony_ci        return ret;
550cabdff1aSopenharmony_ci
551cabdff1aSopenharmony_ci    p = pkt.data;
552cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "_result");
553cabdff1aSopenharmony_ci    ff_amf_write_number(&p, seqnum);
554cabdff1aSopenharmony_ci
555cabdff1aSopenharmony_ci    ff_amf_write_object_start(&p);
556cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "fmsVer");
557cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "FMS/3,0,1,123");
558cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "capabilities");
559cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 31);
560cabdff1aSopenharmony_ci    ff_amf_write_object_end(&p);
561cabdff1aSopenharmony_ci
562cabdff1aSopenharmony_ci    ff_amf_write_object_start(&p);
563cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "level");
564cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "status");
565cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "code");
566cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "NetConnection.Connect.Success");
567cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "description");
568cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "Connection succeeded.");
569cabdff1aSopenharmony_ci    ff_amf_write_field_name(&p, "objectEncoding");
570cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 0);
571cabdff1aSopenharmony_ci    ff_amf_write_object_end(&p);
572cabdff1aSopenharmony_ci
573cabdff1aSopenharmony_ci    pkt.size = p - pkt.data;
574cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
575cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
576cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
577cabdff1aSopenharmony_ci    if (ret < 0)
578cabdff1aSopenharmony_ci        return ret;
579cabdff1aSopenharmony_ci
580cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
581cabdff1aSopenharmony_ci                                     RTMP_PT_INVOKE, 0, 30)) < 0)
582cabdff1aSopenharmony_ci        return ret;
583cabdff1aSopenharmony_ci    p = pkt.data;
584cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "onBWDone");
585cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 0);
586cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
587cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 8192);
588cabdff1aSopenharmony_ci    pkt.size = p - pkt.data;
589cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
590cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
591cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&pkt);
592cabdff1aSopenharmony_ci
593cabdff1aSopenharmony_ci    return ret;
594cabdff1aSopenharmony_ci}
595cabdff1aSopenharmony_ci
596cabdff1aSopenharmony_ci/**
597cabdff1aSopenharmony_ci * Generate 'releaseStream' call and send it to the server. It should make
598cabdff1aSopenharmony_ci * the server release some channel for media streams.
599cabdff1aSopenharmony_ci */
600cabdff1aSopenharmony_cistatic int gen_release_stream(URLContext *s, RTMPContext *rt)
601cabdff1aSopenharmony_ci{
602cabdff1aSopenharmony_ci    RTMPPacket pkt;
603cabdff1aSopenharmony_ci    uint8_t *p;
604cabdff1aSopenharmony_ci    int ret;
605cabdff1aSopenharmony_ci
606cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
607cabdff1aSopenharmony_ci                                     0, 29 + strlen(rt->playpath))) < 0)
608cabdff1aSopenharmony_ci        return ret;
609cabdff1aSopenharmony_ci
610cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
611cabdff1aSopenharmony_ci    p = pkt.data;
612cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "releaseStream");
613cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
614cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
615cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
616cabdff1aSopenharmony_ci
617cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
618cabdff1aSopenharmony_ci}
619cabdff1aSopenharmony_ci
620cabdff1aSopenharmony_ci/**
621cabdff1aSopenharmony_ci * Generate 'FCPublish' call and send it to the server. It should make
622cabdff1aSopenharmony_ci * the server prepare for receiving media streams.
623cabdff1aSopenharmony_ci */
624cabdff1aSopenharmony_cistatic int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
625cabdff1aSopenharmony_ci{
626cabdff1aSopenharmony_ci    RTMPPacket pkt;
627cabdff1aSopenharmony_ci    uint8_t *p;
628cabdff1aSopenharmony_ci    int ret;
629cabdff1aSopenharmony_ci
630cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
631cabdff1aSopenharmony_ci                                     0, 25 + strlen(rt->playpath))) < 0)
632cabdff1aSopenharmony_ci        return ret;
633cabdff1aSopenharmony_ci
634cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
635cabdff1aSopenharmony_ci    p = pkt.data;
636cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "FCPublish");
637cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
638cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
639cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
640cabdff1aSopenharmony_ci
641cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
642cabdff1aSopenharmony_ci}
643cabdff1aSopenharmony_ci
644cabdff1aSopenharmony_ci/**
645cabdff1aSopenharmony_ci * Generate 'FCUnpublish' call and send it to the server. It should make
646cabdff1aSopenharmony_ci * the server destroy stream.
647cabdff1aSopenharmony_ci */
648cabdff1aSopenharmony_cistatic int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
649cabdff1aSopenharmony_ci{
650cabdff1aSopenharmony_ci    RTMPPacket pkt;
651cabdff1aSopenharmony_ci    uint8_t *p;
652cabdff1aSopenharmony_ci    int ret;
653cabdff1aSopenharmony_ci
654cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
655cabdff1aSopenharmony_ci                                     0, 27 + strlen(rt->playpath))) < 0)
656cabdff1aSopenharmony_ci        return ret;
657cabdff1aSopenharmony_ci
658cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
659cabdff1aSopenharmony_ci    p = pkt.data;
660cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "FCUnpublish");
661cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
662cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
663cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
664cabdff1aSopenharmony_ci
665cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
666cabdff1aSopenharmony_ci}
667cabdff1aSopenharmony_ci
668cabdff1aSopenharmony_ci/**
669cabdff1aSopenharmony_ci * Generate 'createStream' call and send it to the server. It should make
670cabdff1aSopenharmony_ci * the server allocate some channel for media streams.
671cabdff1aSopenharmony_ci */
672cabdff1aSopenharmony_cistatic int gen_create_stream(URLContext *s, RTMPContext *rt)
673cabdff1aSopenharmony_ci{
674cabdff1aSopenharmony_ci    RTMPPacket pkt;
675cabdff1aSopenharmony_ci    uint8_t *p;
676cabdff1aSopenharmony_ci    int ret;
677cabdff1aSopenharmony_ci
678cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
679cabdff1aSopenharmony_ci
680cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
681cabdff1aSopenharmony_ci                                     0, 25)) < 0)
682cabdff1aSopenharmony_ci        return ret;
683cabdff1aSopenharmony_ci
684cabdff1aSopenharmony_ci    p = pkt.data;
685cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "createStream");
686cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
687cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
688cabdff1aSopenharmony_ci
689cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
690cabdff1aSopenharmony_ci}
691cabdff1aSopenharmony_ci
692cabdff1aSopenharmony_ci
693cabdff1aSopenharmony_ci/**
694cabdff1aSopenharmony_ci * Generate 'deleteStream' call and send it to the server. It should make
695cabdff1aSopenharmony_ci * the server remove some channel for media streams.
696cabdff1aSopenharmony_ci */
697cabdff1aSopenharmony_cistatic int gen_delete_stream(URLContext *s, RTMPContext *rt)
698cabdff1aSopenharmony_ci{
699cabdff1aSopenharmony_ci    RTMPPacket pkt;
700cabdff1aSopenharmony_ci    uint8_t *p;
701cabdff1aSopenharmony_ci    int ret;
702cabdff1aSopenharmony_ci
703cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
704cabdff1aSopenharmony_ci
705cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
706cabdff1aSopenharmony_ci                                     0, 34)) < 0)
707cabdff1aSopenharmony_ci        return ret;
708cabdff1aSopenharmony_ci
709cabdff1aSopenharmony_ci    p = pkt.data;
710cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "deleteStream");
711cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
712cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
713cabdff1aSopenharmony_ci    ff_amf_write_number(&p, rt->stream_id);
714cabdff1aSopenharmony_ci
715cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
716cabdff1aSopenharmony_ci}
717cabdff1aSopenharmony_ci
718cabdff1aSopenharmony_ci/**
719cabdff1aSopenharmony_ci * Generate 'getStreamLength' call and send it to the server. If the server
720cabdff1aSopenharmony_ci * knows the duration of the selected stream, it will reply with the duration
721cabdff1aSopenharmony_ci * in seconds.
722cabdff1aSopenharmony_ci */
723cabdff1aSopenharmony_cistatic int gen_get_stream_length(URLContext *s, RTMPContext *rt)
724cabdff1aSopenharmony_ci{
725cabdff1aSopenharmony_ci    RTMPPacket pkt;
726cabdff1aSopenharmony_ci    uint8_t *p;
727cabdff1aSopenharmony_ci    int ret;
728cabdff1aSopenharmony_ci
729cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
730cabdff1aSopenharmony_ci                                     0, 31 + strlen(rt->playpath))) < 0)
731cabdff1aSopenharmony_ci        return ret;
732cabdff1aSopenharmony_ci
733cabdff1aSopenharmony_ci    p = pkt.data;
734cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "getStreamLength");
735cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
736cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
737cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
738cabdff1aSopenharmony_ci
739cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
740cabdff1aSopenharmony_ci}
741cabdff1aSopenharmony_ci
742cabdff1aSopenharmony_ci/**
743cabdff1aSopenharmony_ci * Generate client buffer time and send it to the server.
744cabdff1aSopenharmony_ci */
745cabdff1aSopenharmony_cistatic int gen_buffer_time(URLContext *s, RTMPContext *rt)
746cabdff1aSopenharmony_ci{
747cabdff1aSopenharmony_ci    RTMPPacket pkt;
748cabdff1aSopenharmony_ci    uint8_t *p;
749cabdff1aSopenharmony_ci    int ret;
750cabdff1aSopenharmony_ci
751cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_USER_CONTROL,
752cabdff1aSopenharmony_ci                                     1, 10)) < 0)
753cabdff1aSopenharmony_ci        return ret;
754cabdff1aSopenharmony_ci
755cabdff1aSopenharmony_ci    p = pkt.data;
756cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 3); // SetBuffer Length
757cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->stream_id);
758cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->client_buffer_time);
759cabdff1aSopenharmony_ci
760cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
761cabdff1aSopenharmony_ci}
762cabdff1aSopenharmony_ci
763cabdff1aSopenharmony_ci/**
764cabdff1aSopenharmony_ci * Generate 'play' call and send it to the server, then ping the server
765cabdff1aSopenharmony_ci * to start actual playing.
766cabdff1aSopenharmony_ci */
767cabdff1aSopenharmony_cistatic int gen_play(URLContext *s, RTMPContext *rt)
768cabdff1aSopenharmony_ci{
769cabdff1aSopenharmony_ci    RTMPPacket pkt;
770cabdff1aSopenharmony_ci    uint8_t *p;
771cabdff1aSopenharmony_ci    int ret;
772cabdff1aSopenharmony_ci
773cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
774cabdff1aSopenharmony_ci
775cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
776cabdff1aSopenharmony_ci                                     0, 29 + strlen(rt->playpath))) < 0)
777cabdff1aSopenharmony_ci        return ret;
778cabdff1aSopenharmony_ci
779cabdff1aSopenharmony_ci    pkt.extra = rt->stream_id;
780cabdff1aSopenharmony_ci
781cabdff1aSopenharmony_ci    p = pkt.data;
782cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "play");
783cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
784cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
785cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
786cabdff1aSopenharmony_ci    ff_amf_write_number(&p, rt->live * 1000);
787cabdff1aSopenharmony_ci
788cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
789cabdff1aSopenharmony_ci}
790cabdff1aSopenharmony_ci
791cabdff1aSopenharmony_cistatic int gen_seek(URLContext *s, RTMPContext *rt, int64_t timestamp)
792cabdff1aSopenharmony_ci{
793cabdff1aSopenharmony_ci    RTMPPacket pkt;
794cabdff1aSopenharmony_ci    uint8_t *p;
795cabdff1aSopenharmony_ci    int ret;
796cabdff1aSopenharmony_ci
797cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Sending seek command for timestamp %"PRId64"\n",
798cabdff1aSopenharmony_ci           timestamp);
799cabdff1aSopenharmony_ci
800cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, 3, RTMP_PT_INVOKE, 0, 26)) < 0)
801cabdff1aSopenharmony_ci        return ret;
802cabdff1aSopenharmony_ci
803cabdff1aSopenharmony_ci    pkt.extra = rt->stream_id;
804cabdff1aSopenharmony_ci
805cabdff1aSopenharmony_ci    p = pkt.data;
806cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "seek");
807cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 0); //no tracking back responses
808cabdff1aSopenharmony_ci    ff_amf_write_null(&p); //as usual, the first null param
809cabdff1aSopenharmony_ci    ff_amf_write_number(&p, timestamp); //where we want to jump
810cabdff1aSopenharmony_ci
811cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
812cabdff1aSopenharmony_ci}
813cabdff1aSopenharmony_ci
814cabdff1aSopenharmony_ci/**
815cabdff1aSopenharmony_ci * Generate a pause packet that either pauses or unpauses the current stream.
816cabdff1aSopenharmony_ci */
817cabdff1aSopenharmony_cistatic int gen_pause(URLContext *s, RTMPContext *rt, int pause, uint32_t timestamp)
818cabdff1aSopenharmony_ci{
819cabdff1aSopenharmony_ci    RTMPPacket pkt;
820cabdff1aSopenharmony_ci    uint8_t *p;
821cabdff1aSopenharmony_ci    int ret;
822cabdff1aSopenharmony_ci
823cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Sending pause command for timestamp %d\n",
824cabdff1aSopenharmony_ci           timestamp);
825cabdff1aSopenharmony_ci
826cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, 3, RTMP_PT_INVOKE, 0, 29)) < 0)
827cabdff1aSopenharmony_ci        return ret;
828cabdff1aSopenharmony_ci
829cabdff1aSopenharmony_ci    pkt.extra = rt->stream_id;
830cabdff1aSopenharmony_ci
831cabdff1aSopenharmony_ci    p = pkt.data;
832cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "pause");
833cabdff1aSopenharmony_ci    ff_amf_write_number(&p, 0); //no tracking back responses
834cabdff1aSopenharmony_ci    ff_amf_write_null(&p); //as usual, the first null param
835cabdff1aSopenharmony_ci    ff_amf_write_bool(&p, pause); // pause or unpause
836cabdff1aSopenharmony_ci    ff_amf_write_number(&p, timestamp); //where we pause the stream
837cabdff1aSopenharmony_ci
838cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
839cabdff1aSopenharmony_ci}
840cabdff1aSopenharmony_ci
841cabdff1aSopenharmony_ci/**
842cabdff1aSopenharmony_ci * Generate 'publish' call and send it to the server.
843cabdff1aSopenharmony_ci */
844cabdff1aSopenharmony_cistatic int gen_publish(URLContext *s, RTMPContext *rt)
845cabdff1aSopenharmony_ci{
846cabdff1aSopenharmony_ci    RTMPPacket pkt;
847cabdff1aSopenharmony_ci    uint8_t *p;
848cabdff1aSopenharmony_ci    int ret;
849cabdff1aSopenharmony_ci
850cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
851cabdff1aSopenharmony_ci
852cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
853cabdff1aSopenharmony_ci                                     0, 30 + strlen(rt->playpath))) < 0)
854cabdff1aSopenharmony_ci        return ret;
855cabdff1aSopenharmony_ci
856cabdff1aSopenharmony_ci    pkt.extra = rt->stream_id;
857cabdff1aSopenharmony_ci
858cabdff1aSopenharmony_ci    p = pkt.data;
859cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "publish");
860cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
861cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
862cabdff1aSopenharmony_ci    ff_amf_write_string(&p, rt->playpath);
863cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "live");
864cabdff1aSopenharmony_ci
865cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
866cabdff1aSopenharmony_ci}
867cabdff1aSopenharmony_ci
868cabdff1aSopenharmony_ci/**
869cabdff1aSopenharmony_ci * Generate ping reply and send it to the server.
870cabdff1aSopenharmony_ci */
871cabdff1aSopenharmony_cistatic int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
872cabdff1aSopenharmony_ci{
873cabdff1aSopenharmony_ci    RTMPPacket pkt;
874cabdff1aSopenharmony_ci    uint8_t *p;
875cabdff1aSopenharmony_ci    int ret;
876cabdff1aSopenharmony_ci
877cabdff1aSopenharmony_ci    if (ppkt->size < 6) {
878cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
879cabdff1aSopenharmony_ci               ppkt->size);
880cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
881cabdff1aSopenharmony_ci    }
882cabdff1aSopenharmony_ci
883cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,RTMP_PT_USER_CONTROL,
884cabdff1aSopenharmony_ci                                     ppkt->timestamp + 1, 6)) < 0)
885cabdff1aSopenharmony_ci        return ret;
886cabdff1aSopenharmony_ci
887cabdff1aSopenharmony_ci    p = pkt.data;
888cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 7); // PingResponse
889cabdff1aSopenharmony_ci    bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
890cabdff1aSopenharmony_ci
891cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
892cabdff1aSopenharmony_ci}
893cabdff1aSopenharmony_ci
894cabdff1aSopenharmony_ci/**
895cabdff1aSopenharmony_ci * Generate SWF verification message and send it to the server.
896cabdff1aSopenharmony_ci */
897cabdff1aSopenharmony_cistatic int gen_swf_verification(URLContext *s, RTMPContext *rt)
898cabdff1aSopenharmony_ci{
899cabdff1aSopenharmony_ci    RTMPPacket pkt;
900cabdff1aSopenharmony_ci    uint8_t *p;
901cabdff1aSopenharmony_ci    int ret;
902cabdff1aSopenharmony_ci
903cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n");
904cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_USER_CONTROL,
905cabdff1aSopenharmony_ci                                     0, 44)) < 0)
906cabdff1aSopenharmony_ci        return ret;
907cabdff1aSopenharmony_ci
908cabdff1aSopenharmony_ci    p = pkt.data;
909cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 27);
910cabdff1aSopenharmony_ci    memcpy(p, rt->swfverification, 42);
911cabdff1aSopenharmony_ci
912cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
913cabdff1aSopenharmony_ci}
914cabdff1aSopenharmony_ci
915cabdff1aSopenharmony_ci/**
916cabdff1aSopenharmony_ci * Generate window acknowledgement size message and send it to the server.
917cabdff1aSopenharmony_ci */
918cabdff1aSopenharmony_cistatic int gen_window_ack_size(URLContext *s, RTMPContext *rt)
919cabdff1aSopenharmony_ci{
920cabdff1aSopenharmony_ci    RTMPPacket pkt;
921cabdff1aSopenharmony_ci    uint8_t *p;
922cabdff1aSopenharmony_ci    int ret;
923cabdff1aSopenharmony_ci
924cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_WINDOW_ACK_SIZE,
925cabdff1aSopenharmony_ci                                     0, 4)) < 0)
926cabdff1aSopenharmony_ci        return ret;
927cabdff1aSopenharmony_ci
928cabdff1aSopenharmony_ci    p = pkt.data;
929cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->max_sent_unacked);
930cabdff1aSopenharmony_ci
931cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
932cabdff1aSopenharmony_ci}
933cabdff1aSopenharmony_ci
934cabdff1aSopenharmony_ci/**
935cabdff1aSopenharmony_ci * Generate check bandwidth message and send it to the server.
936cabdff1aSopenharmony_ci */
937cabdff1aSopenharmony_cistatic int gen_check_bw(URLContext *s, RTMPContext *rt)
938cabdff1aSopenharmony_ci{
939cabdff1aSopenharmony_ci    RTMPPacket pkt;
940cabdff1aSopenharmony_ci    uint8_t *p;
941cabdff1aSopenharmony_ci    int ret;
942cabdff1aSopenharmony_ci
943cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
944cabdff1aSopenharmony_ci                                     0, 21)) < 0)
945cabdff1aSopenharmony_ci        return ret;
946cabdff1aSopenharmony_ci
947cabdff1aSopenharmony_ci    p = pkt.data;
948cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "_checkbw");
949cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
950cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
951cabdff1aSopenharmony_ci
952cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
953cabdff1aSopenharmony_ci}
954cabdff1aSopenharmony_ci
955cabdff1aSopenharmony_ci/**
956cabdff1aSopenharmony_ci * Generate report on bytes read so far and send it to the server.
957cabdff1aSopenharmony_ci */
958cabdff1aSopenharmony_cistatic int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
959cabdff1aSopenharmony_ci{
960cabdff1aSopenharmony_ci    RTMPPacket pkt;
961cabdff1aSopenharmony_ci    uint8_t *p;
962cabdff1aSopenharmony_ci    int ret;
963cabdff1aSopenharmony_ci
964cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
965cabdff1aSopenharmony_ci                                     ts, 4)) < 0)
966cabdff1aSopenharmony_ci        return ret;
967cabdff1aSopenharmony_ci
968cabdff1aSopenharmony_ci    p = pkt.data;
969cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->bytes_read);
970cabdff1aSopenharmony_ci
971cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 0);
972cabdff1aSopenharmony_ci}
973cabdff1aSopenharmony_ci
974cabdff1aSopenharmony_cistatic int gen_fcsubscribe_stream(URLContext *s, RTMPContext *rt,
975cabdff1aSopenharmony_ci                                  const char *subscribe)
976cabdff1aSopenharmony_ci{
977cabdff1aSopenharmony_ci    RTMPPacket pkt;
978cabdff1aSopenharmony_ci    uint8_t *p;
979cabdff1aSopenharmony_ci    int ret;
980cabdff1aSopenharmony_ci
981cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
982cabdff1aSopenharmony_ci                                     0, 27 + strlen(subscribe))) < 0)
983cabdff1aSopenharmony_ci        return ret;
984cabdff1aSopenharmony_ci
985cabdff1aSopenharmony_ci    p = pkt.data;
986cabdff1aSopenharmony_ci    ff_amf_write_string(&p, "FCSubscribe");
987cabdff1aSopenharmony_ci    ff_amf_write_number(&p, ++rt->nb_invokes);
988cabdff1aSopenharmony_ci    ff_amf_write_null(&p);
989cabdff1aSopenharmony_ci    ff_amf_write_string(&p, subscribe);
990cabdff1aSopenharmony_ci
991cabdff1aSopenharmony_ci    return rtmp_send_packet(rt, &pkt, 1);
992cabdff1aSopenharmony_ci}
993cabdff1aSopenharmony_ci
994cabdff1aSopenharmony_ci/**
995cabdff1aSopenharmony_ci * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
996cabdff1aSopenharmony_ci * will be stored) into that packet.
997cabdff1aSopenharmony_ci *
998cabdff1aSopenharmony_ci * @param buf handshake data (1536 bytes)
999cabdff1aSopenharmony_ci * @param encrypted use an encrypted connection (RTMPE)
1000cabdff1aSopenharmony_ci * @return offset to the digest inside input data
1001cabdff1aSopenharmony_ci */
1002cabdff1aSopenharmony_cistatic int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
1003cabdff1aSopenharmony_ci{
1004cabdff1aSopenharmony_ci    int ret, digest_pos;
1005cabdff1aSopenharmony_ci
1006cabdff1aSopenharmony_ci    if (encrypted)
1007cabdff1aSopenharmony_ci        digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
1008cabdff1aSopenharmony_ci    else
1009cabdff1aSopenharmony_ci        digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
1010cabdff1aSopenharmony_ci
1011cabdff1aSopenharmony_ci    ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
1012cabdff1aSopenharmony_ci                              rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
1013cabdff1aSopenharmony_ci                              buf + digest_pos);
1014cabdff1aSopenharmony_ci    if (ret < 0)
1015cabdff1aSopenharmony_ci        return ret;
1016cabdff1aSopenharmony_ci
1017cabdff1aSopenharmony_ci    return digest_pos;
1018cabdff1aSopenharmony_ci}
1019cabdff1aSopenharmony_ci
1020cabdff1aSopenharmony_ci/**
1021cabdff1aSopenharmony_ci * Verify that the received server response has the expected digest value.
1022cabdff1aSopenharmony_ci *
1023cabdff1aSopenharmony_ci * @param buf handshake data received from the server (1536 bytes)
1024cabdff1aSopenharmony_ci * @param off position to search digest offset from
1025cabdff1aSopenharmony_ci * @return 0 if digest is valid, digest position otherwise
1026cabdff1aSopenharmony_ci */
1027cabdff1aSopenharmony_cistatic int rtmp_validate_digest(uint8_t *buf, int off)
1028cabdff1aSopenharmony_ci{
1029cabdff1aSopenharmony_ci    uint8_t digest[32];
1030cabdff1aSopenharmony_ci    int ret, digest_pos;
1031cabdff1aSopenharmony_ci
1032cabdff1aSopenharmony_ci    digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
1033cabdff1aSopenharmony_ci
1034cabdff1aSopenharmony_ci    ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
1035cabdff1aSopenharmony_ci                              rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
1036cabdff1aSopenharmony_ci                              digest);
1037cabdff1aSopenharmony_ci    if (ret < 0)
1038cabdff1aSopenharmony_ci        return ret;
1039cabdff1aSopenharmony_ci
1040cabdff1aSopenharmony_ci    if (!memcmp(digest, buf + digest_pos, 32))
1041cabdff1aSopenharmony_ci        return digest_pos;
1042cabdff1aSopenharmony_ci    return 0;
1043cabdff1aSopenharmony_ci}
1044cabdff1aSopenharmony_ci
1045cabdff1aSopenharmony_cistatic int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
1046cabdff1aSopenharmony_ci                                      uint8_t *buf)
1047cabdff1aSopenharmony_ci{
1048cabdff1aSopenharmony_ci    uint8_t *p;
1049cabdff1aSopenharmony_ci    int ret;
1050cabdff1aSopenharmony_ci
1051cabdff1aSopenharmony_ci    if (rt->swfhash_len != 32) {
1052cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1053cabdff1aSopenharmony_ci               "Hash of the decompressed SWF file is not 32 bytes long.\n");
1054cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
1055cabdff1aSopenharmony_ci    }
1056cabdff1aSopenharmony_ci
1057cabdff1aSopenharmony_ci    p = &rt->swfverification[0];
1058cabdff1aSopenharmony_ci    bytestream_put_byte(&p, 1);
1059cabdff1aSopenharmony_ci    bytestream_put_byte(&p, 1);
1060cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->swfsize);
1061cabdff1aSopenharmony_ci    bytestream_put_be32(&p, rt->swfsize);
1062cabdff1aSopenharmony_ci
1063cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0)
1064cabdff1aSopenharmony_ci        return ret;
1065cabdff1aSopenharmony_ci
1066cabdff1aSopenharmony_ci    return 0;
1067cabdff1aSopenharmony_ci}
1068cabdff1aSopenharmony_ci
1069cabdff1aSopenharmony_ci#if CONFIG_ZLIB
1070cabdff1aSopenharmony_cistatic int rtmp_uncompress_swfplayer(uint8_t *in_data, int64_t in_size,
1071cabdff1aSopenharmony_ci                                     uint8_t **out_data, int64_t *out_size)
1072cabdff1aSopenharmony_ci{
1073cabdff1aSopenharmony_ci    z_stream zs = { 0 };
1074cabdff1aSopenharmony_ci    void *ptr;
1075cabdff1aSopenharmony_ci    int size;
1076cabdff1aSopenharmony_ci    int ret = 0;
1077cabdff1aSopenharmony_ci
1078cabdff1aSopenharmony_ci    zs.avail_in = in_size;
1079cabdff1aSopenharmony_ci    zs.next_in  = in_data;
1080cabdff1aSopenharmony_ci    ret = inflateInit(&zs);
1081cabdff1aSopenharmony_ci    if (ret != Z_OK)
1082cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1083cabdff1aSopenharmony_ci
1084cabdff1aSopenharmony_ci    do {
1085cabdff1aSopenharmony_ci        uint8_t tmp_buf[16384];
1086cabdff1aSopenharmony_ci
1087cabdff1aSopenharmony_ci        zs.avail_out = sizeof(tmp_buf);
1088cabdff1aSopenharmony_ci        zs.next_out  = tmp_buf;
1089cabdff1aSopenharmony_ci
1090cabdff1aSopenharmony_ci        ret = inflate(&zs, Z_NO_FLUSH);
1091cabdff1aSopenharmony_ci        if (ret != Z_OK && ret != Z_STREAM_END) {
1092cabdff1aSopenharmony_ci            ret = AVERROR_UNKNOWN;
1093cabdff1aSopenharmony_ci            goto fail;
1094cabdff1aSopenharmony_ci        }
1095cabdff1aSopenharmony_ci
1096cabdff1aSopenharmony_ci        size = sizeof(tmp_buf) - zs.avail_out;
1097cabdff1aSopenharmony_ci        if (!(ptr = av_realloc(*out_data, *out_size + size))) {
1098cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
1099cabdff1aSopenharmony_ci            goto fail;
1100cabdff1aSopenharmony_ci        }
1101cabdff1aSopenharmony_ci        *out_data = ptr;
1102cabdff1aSopenharmony_ci
1103cabdff1aSopenharmony_ci        memcpy(*out_data + *out_size, tmp_buf, size);
1104cabdff1aSopenharmony_ci        *out_size += size;
1105cabdff1aSopenharmony_ci    } while (zs.avail_out == 0);
1106cabdff1aSopenharmony_ci
1107cabdff1aSopenharmony_cifail:
1108cabdff1aSopenharmony_ci    inflateEnd(&zs);
1109cabdff1aSopenharmony_ci    return ret;
1110cabdff1aSopenharmony_ci}
1111cabdff1aSopenharmony_ci#endif
1112cabdff1aSopenharmony_ci
1113cabdff1aSopenharmony_cistatic int rtmp_calc_swfhash(URLContext *s)
1114cabdff1aSopenharmony_ci{
1115cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1116cabdff1aSopenharmony_ci    uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
1117cabdff1aSopenharmony_ci    int64_t in_size;
1118cabdff1aSopenharmony_ci    URLContext *stream = NULL;
1119cabdff1aSopenharmony_ci    char swfhash[32];
1120cabdff1aSopenharmony_ci    int swfsize;
1121cabdff1aSopenharmony_ci    int ret = 0;
1122cabdff1aSopenharmony_ci
1123cabdff1aSopenharmony_ci    /* Get the SWF player file. */
1124cabdff1aSopenharmony_ci    if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
1125cabdff1aSopenharmony_ci                                    &s->interrupt_callback, NULL,
1126cabdff1aSopenharmony_ci                                    s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
1127cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
1128cabdff1aSopenharmony_ci        goto fail;
1129cabdff1aSopenharmony_ci    }
1130cabdff1aSopenharmony_ci
1131cabdff1aSopenharmony_ci    if ((in_size = ffurl_seek(stream, 0, AVSEEK_SIZE)) < 0) {
1132cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
1133cabdff1aSopenharmony_ci        goto fail;
1134cabdff1aSopenharmony_ci    }
1135cabdff1aSopenharmony_ci
1136cabdff1aSopenharmony_ci    if (!(in_data = av_malloc(in_size))) {
1137cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
1138cabdff1aSopenharmony_ci        goto fail;
1139cabdff1aSopenharmony_ci    }
1140cabdff1aSopenharmony_ci
1141cabdff1aSopenharmony_ci    if ((ret = ffurl_read_complete(stream, in_data, in_size)) < 0)
1142cabdff1aSopenharmony_ci        goto fail;
1143cabdff1aSopenharmony_ci
1144cabdff1aSopenharmony_ci    if (in_size < 3) {
1145cabdff1aSopenharmony_ci        ret = AVERROR_INVALIDDATA;
1146cabdff1aSopenharmony_ci        goto fail;
1147cabdff1aSopenharmony_ci    }
1148cabdff1aSopenharmony_ci
1149cabdff1aSopenharmony_ci    if (!memcmp(in_data, "CWS", 3)) {
1150cabdff1aSopenharmony_ci#if CONFIG_ZLIB
1151cabdff1aSopenharmony_ci        int64_t out_size;
1152cabdff1aSopenharmony_ci        /* Decompress the SWF player file using Zlib. */
1153cabdff1aSopenharmony_ci        if (!(out_data = av_malloc(8))) {
1154cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
1155cabdff1aSopenharmony_ci            goto fail;
1156cabdff1aSopenharmony_ci        }
1157cabdff1aSopenharmony_ci        *in_data = 'F'; // magic stuff
1158cabdff1aSopenharmony_ci        memcpy(out_data, in_data, 8);
1159cabdff1aSopenharmony_ci        out_size = 8;
1160cabdff1aSopenharmony_ci
1161cabdff1aSopenharmony_ci        if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
1162cabdff1aSopenharmony_ci                                             &out_data, &out_size)) < 0)
1163cabdff1aSopenharmony_ci            goto fail;
1164cabdff1aSopenharmony_ci        swfsize = out_size;
1165cabdff1aSopenharmony_ci        swfdata = out_data;
1166cabdff1aSopenharmony_ci#else
1167cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1168cabdff1aSopenharmony_ci               "Zlib is required for decompressing the SWF player file.\n");
1169cabdff1aSopenharmony_ci        ret = AVERROR(EINVAL);
1170cabdff1aSopenharmony_ci        goto fail;
1171cabdff1aSopenharmony_ci#endif
1172cabdff1aSopenharmony_ci    } else {
1173cabdff1aSopenharmony_ci        swfsize = in_size;
1174cabdff1aSopenharmony_ci        swfdata = in_data;
1175cabdff1aSopenharmony_ci    }
1176cabdff1aSopenharmony_ci
1177cabdff1aSopenharmony_ci    /* Compute the SHA256 hash of the SWF player file. */
1178cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_calc_digest(swfdata, swfsize, 0,
1179cabdff1aSopenharmony_ci                                   "Genuine Adobe Flash Player 001", 30,
1180cabdff1aSopenharmony_ci                                   swfhash)) < 0)
1181cabdff1aSopenharmony_ci        goto fail;
1182cabdff1aSopenharmony_ci
1183cabdff1aSopenharmony_ci    /* Set SWFVerification parameters. */
1184cabdff1aSopenharmony_ci    av_opt_set_bin(rt, "rtmp_swfhash", swfhash, 32, 0);
1185cabdff1aSopenharmony_ci    rt->swfsize = swfsize;
1186cabdff1aSopenharmony_ci
1187cabdff1aSopenharmony_cifail:
1188cabdff1aSopenharmony_ci    av_freep(&in_data);
1189cabdff1aSopenharmony_ci    av_freep(&out_data);
1190cabdff1aSopenharmony_ci    ffurl_close(stream);
1191cabdff1aSopenharmony_ci    return ret;
1192cabdff1aSopenharmony_ci}
1193cabdff1aSopenharmony_ci
1194cabdff1aSopenharmony_ci/**
1195cabdff1aSopenharmony_ci * Perform handshake with the server by means of exchanging pseudorandom data
1196cabdff1aSopenharmony_ci * signed with HMAC-SHA2 digest.
1197cabdff1aSopenharmony_ci *
1198cabdff1aSopenharmony_ci * @return 0 if handshake succeeds, negative value otherwise
1199cabdff1aSopenharmony_ci */
1200cabdff1aSopenharmony_cistatic int rtmp_handshake(URLContext *s, RTMPContext *rt)
1201cabdff1aSopenharmony_ci{
1202cabdff1aSopenharmony_ci    AVLFG rnd;
1203cabdff1aSopenharmony_ci    uint8_t tosend    [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
1204cabdff1aSopenharmony_ci        3,                // unencrypted data
1205cabdff1aSopenharmony_ci        0, 0, 0, 0,       // client uptime
1206cabdff1aSopenharmony_ci        RTMP_CLIENT_VER1,
1207cabdff1aSopenharmony_ci        RTMP_CLIENT_VER2,
1208cabdff1aSopenharmony_ci        RTMP_CLIENT_VER3,
1209cabdff1aSopenharmony_ci        RTMP_CLIENT_VER4,
1210cabdff1aSopenharmony_ci    };
1211cabdff1aSopenharmony_ci    uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
1212cabdff1aSopenharmony_ci    uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
1213cabdff1aSopenharmony_ci    int i;
1214cabdff1aSopenharmony_ci    int server_pos, client_pos;
1215cabdff1aSopenharmony_ci    uint8_t digest[32], signature[32];
1216cabdff1aSopenharmony_ci    int ret, type = 0;
1217cabdff1aSopenharmony_ci
1218cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
1219cabdff1aSopenharmony_ci
1220cabdff1aSopenharmony_ci    av_lfg_init(&rnd, 0xDEADC0DE);
1221cabdff1aSopenharmony_ci    // generate handshake packet - 1536 bytes of pseudorandom data
1222cabdff1aSopenharmony_ci    for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
1223cabdff1aSopenharmony_ci        tosend[i] = av_lfg_get(&rnd) >> 24;
1224cabdff1aSopenharmony_ci
1225cabdff1aSopenharmony_ci    if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1226cabdff1aSopenharmony_ci        /* When the client wants to use RTMPE, we have to change the command
1227cabdff1aSopenharmony_ci         * byte to 0x06 which means to use encrypted data and we have to set
1228cabdff1aSopenharmony_ci         * the flash version to at least 9.0.115.0. */
1229cabdff1aSopenharmony_ci        tosend[0] = 6;
1230cabdff1aSopenharmony_ci        tosend[5] = 128;
1231cabdff1aSopenharmony_ci        tosend[6] = 0;
1232cabdff1aSopenharmony_ci        tosend[7] = 3;
1233cabdff1aSopenharmony_ci        tosend[8] = 2;
1234cabdff1aSopenharmony_ci
1235cabdff1aSopenharmony_ci        /* Initialize the Diffie-Hellmann context and generate the public key
1236cabdff1aSopenharmony_ci         * to send to the server. */
1237cabdff1aSopenharmony_ci        if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
1238cabdff1aSopenharmony_ci            return ret;
1239cabdff1aSopenharmony_ci    }
1240cabdff1aSopenharmony_ci
1241cabdff1aSopenharmony_ci    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
1242cabdff1aSopenharmony_ci    if (client_pos < 0)
1243cabdff1aSopenharmony_ci        return client_pos;
1244cabdff1aSopenharmony_ci
1245cabdff1aSopenharmony_ci    if ((ret = ffurl_write(rt->stream, tosend,
1246cabdff1aSopenharmony_ci                           RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
1247cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
1248cabdff1aSopenharmony_ci        return ret;
1249cabdff1aSopenharmony_ci    }
1250cabdff1aSopenharmony_ci
1251cabdff1aSopenharmony_ci    if ((ret = ffurl_read_complete(rt->stream, serverdata,
1252cabdff1aSopenharmony_ci                                   RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
1253cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
1254cabdff1aSopenharmony_ci        return ret;
1255cabdff1aSopenharmony_ci    }
1256cabdff1aSopenharmony_ci
1257cabdff1aSopenharmony_ci    if ((ret = ffurl_read_complete(rt->stream, clientdata,
1258cabdff1aSopenharmony_ci                                   RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
1259cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
1260cabdff1aSopenharmony_ci        return ret;
1261cabdff1aSopenharmony_ci    }
1262cabdff1aSopenharmony_ci
1263cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
1264cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
1265cabdff1aSopenharmony_ci           serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
1266cabdff1aSopenharmony_ci
1267cabdff1aSopenharmony_ci    if (rt->is_input && serverdata[5] >= 3) {
1268cabdff1aSopenharmony_ci        server_pos = rtmp_validate_digest(serverdata + 1, 772);
1269cabdff1aSopenharmony_ci        if (server_pos < 0)
1270cabdff1aSopenharmony_ci            return server_pos;
1271cabdff1aSopenharmony_ci
1272cabdff1aSopenharmony_ci        if (!server_pos) {
1273cabdff1aSopenharmony_ci            type = 1;
1274cabdff1aSopenharmony_ci            server_pos = rtmp_validate_digest(serverdata + 1, 8);
1275cabdff1aSopenharmony_ci            if (server_pos < 0)
1276cabdff1aSopenharmony_ci                return server_pos;
1277cabdff1aSopenharmony_ci
1278cabdff1aSopenharmony_ci            if (!server_pos) {
1279cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
1280cabdff1aSopenharmony_ci                return AVERROR(EIO);
1281cabdff1aSopenharmony_ci            }
1282cabdff1aSopenharmony_ci        }
1283cabdff1aSopenharmony_ci
1284cabdff1aSopenharmony_ci        /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
1285cabdff1aSopenharmony_ci         * key are the last 32 bytes of the server handshake. */
1286cabdff1aSopenharmony_ci        if (rt->swfsize) {
1287cabdff1aSopenharmony_ci            if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
1288cabdff1aSopenharmony_ci                                                  RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
1289cabdff1aSopenharmony_ci                return ret;
1290cabdff1aSopenharmony_ci        }
1291cabdff1aSopenharmony_ci
1292cabdff1aSopenharmony_ci        ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
1293cabdff1aSopenharmony_ci                                  rtmp_server_key, sizeof(rtmp_server_key),
1294cabdff1aSopenharmony_ci                                  digest);
1295cabdff1aSopenharmony_ci        if (ret < 0)
1296cabdff1aSopenharmony_ci            return ret;
1297cabdff1aSopenharmony_ci
1298cabdff1aSopenharmony_ci        ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
1299cabdff1aSopenharmony_ci                                  0, digest, 32, signature);
1300cabdff1aSopenharmony_ci        if (ret < 0)
1301cabdff1aSopenharmony_ci            return ret;
1302cabdff1aSopenharmony_ci
1303cabdff1aSopenharmony_ci        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1304cabdff1aSopenharmony_ci            /* Compute the shared secret key sent by the server and initialize
1305cabdff1aSopenharmony_ci             * the RC4 encryption. */
1306cabdff1aSopenharmony_ci            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1307cabdff1aSopenharmony_ci                                                   tosend + 1, type)) < 0)
1308cabdff1aSopenharmony_ci                return ret;
1309cabdff1aSopenharmony_ci
1310cabdff1aSopenharmony_ci            /* Encrypt the signature received by the server. */
1311cabdff1aSopenharmony_ci            ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
1312cabdff1aSopenharmony_ci        }
1313cabdff1aSopenharmony_ci
1314cabdff1aSopenharmony_ci        if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
1315cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
1316cabdff1aSopenharmony_ci            return AVERROR(EIO);
1317cabdff1aSopenharmony_ci        }
1318cabdff1aSopenharmony_ci
1319cabdff1aSopenharmony_ci        for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
1320cabdff1aSopenharmony_ci            tosend[i] = av_lfg_get(&rnd) >> 24;
1321cabdff1aSopenharmony_ci        ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
1322cabdff1aSopenharmony_ci                                  rtmp_player_key, sizeof(rtmp_player_key),
1323cabdff1aSopenharmony_ci                                  digest);
1324cabdff1aSopenharmony_ci        if (ret < 0)
1325cabdff1aSopenharmony_ci            return ret;
1326cabdff1aSopenharmony_ci
1327cabdff1aSopenharmony_ci        ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
1328cabdff1aSopenharmony_ci                                  digest, 32,
1329cabdff1aSopenharmony_ci                                  tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
1330cabdff1aSopenharmony_ci        if (ret < 0)
1331cabdff1aSopenharmony_ci            return ret;
1332cabdff1aSopenharmony_ci
1333cabdff1aSopenharmony_ci        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1334cabdff1aSopenharmony_ci            /* Encrypt the signature to be send to the server. */
1335cabdff1aSopenharmony_ci            ff_rtmpe_encrypt_sig(rt->stream, tosend +
1336cabdff1aSopenharmony_ci                                 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
1337cabdff1aSopenharmony_ci                                 serverdata[0]);
1338cabdff1aSopenharmony_ci        }
1339cabdff1aSopenharmony_ci
1340cabdff1aSopenharmony_ci        // write reply back to the server
1341cabdff1aSopenharmony_ci        if ((ret = ffurl_write(rt->stream, tosend,
1342cabdff1aSopenharmony_ci                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1343cabdff1aSopenharmony_ci            return ret;
1344cabdff1aSopenharmony_ci
1345cabdff1aSopenharmony_ci        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1346cabdff1aSopenharmony_ci            /* Set RC4 keys for encryption and update the keystreams. */
1347cabdff1aSopenharmony_ci            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1348cabdff1aSopenharmony_ci                return ret;
1349cabdff1aSopenharmony_ci        }
1350cabdff1aSopenharmony_ci    } else {
1351cabdff1aSopenharmony_ci        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1352cabdff1aSopenharmony_ci            /* Compute the shared secret key sent by the server and initialize
1353cabdff1aSopenharmony_ci             * the RC4 encryption. */
1354cabdff1aSopenharmony_ci            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1355cabdff1aSopenharmony_ci                            tosend + 1, 1)) < 0)
1356cabdff1aSopenharmony_ci                return ret;
1357cabdff1aSopenharmony_ci
1358cabdff1aSopenharmony_ci            if (serverdata[0] == 9) {
1359cabdff1aSopenharmony_ci                /* Encrypt the signature received by the server. */
1360cabdff1aSopenharmony_ci                ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
1361cabdff1aSopenharmony_ci                                     serverdata[0]);
1362cabdff1aSopenharmony_ci            }
1363cabdff1aSopenharmony_ci        }
1364cabdff1aSopenharmony_ci
1365cabdff1aSopenharmony_ci        if ((ret = ffurl_write(rt->stream, serverdata + 1,
1366cabdff1aSopenharmony_ci                               RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1367cabdff1aSopenharmony_ci            return ret;
1368cabdff1aSopenharmony_ci
1369cabdff1aSopenharmony_ci        if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
1370cabdff1aSopenharmony_ci            /* Set RC4 keys for encryption and update the keystreams. */
1371cabdff1aSopenharmony_ci            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1372cabdff1aSopenharmony_ci                return ret;
1373cabdff1aSopenharmony_ci        }
1374cabdff1aSopenharmony_ci    }
1375cabdff1aSopenharmony_ci
1376cabdff1aSopenharmony_ci    return 0;
1377cabdff1aSopenharmony_ci}
1378cabdff1aSopenharmony_ci
1379cabdff1aSopenharmony_cistatic int rtmp_receive_hs_packet(RTMPContext* rt, uint32_t *first_int,
1380cabdff1aSopenharmony_ci                                  uint32_t *second_int, char *arraydata,
1381cabdff1aSopenharmony_ci                                  int size)
1382cabdff1aSopenharmony_ci{
1383cabdff1aSopenharmony_ci    int inoutsize;
1384cabdff1aSopenharmony_ci
1385cabdff1aSopenharmony_ci    inoutsize = ffurl_read_complete(rt->stream, arraydata,
1386cabdff1aSopenharmony_ci                                    RTMP_HANDSHAKE_PACKET_SIZE);
1387cabdff1aSopenharmony_ci    if (inoutsize <= 0)
1388cabdff1aSopenharmony_ci        return AVERROR(EIO);
1389cabdff1aSopenharmony_ci    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1390cabdff1aSopenharmony_ci        av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
1391cabdff1aSopenharmony_ci               " not following standard\n", (int)inoutsize);
1392cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
1393cabdff1aSopenharmony_ci    }
1394cabdff1aSopenharmony_ci
1395cabdff1aSopenharmony_ci    *first_int  = AV_RB32(arraydata);
1396cabdff1aSopenharmony_ci    *second_int = AV_RB32(arraydata + 4);
1397cabdff1aSopenharmony_ci    return 0;
1398cabdff1aSopenharmony_ci}
1399cabdff1aSopenharmony_ci
1400cabdff1aSopenharmony_cistatic int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
1401cabdff1aSopenharmony_ci                               uint32_t second_int, char *arraydata, int size)
1402cabdff1aSopenharmony_ci{
1403cabdff1aSopenharmony_ci    int inoutsize;
1404cabdff1aSopenharmony_ci
1405cabdff1aSopenharmony_ci    AV_WB32(arraydata, first_int);
1406cabdff1aSopenharmony_ci    AV_WB32(arraydata + 4, second_int);
1407cabdff1aSopenharmony_ci    inoutsize = ffurl_write(rt->stream, arraydata,
1408cabdff1aSopenharmony_ci                            RTMP_HANDSHAKE_PACKET_SIZE);
1409cabdff1aSopenharmony_ci    if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1410cabdff1aSopenharmony_ci        av_log(rt, AV_LOG_ERROR, "Unable to write answer\n");
1411cabdff1aSopenharmony_ci        return AVERROR(EIO);
1412cabdff1aSopenharmony_ci    }
1413cabdff1aSopenharmony_ci
1414cabdff1aSopenharmony_ci    return 0;
1415cabdff1aSopenharmony_ci}
1416cabdff1aSopenharmony_ci
1417cabdff1aSopenharmony_ci/**
1418cabdff1aSopenharmony_ci * rtmp handshake server side
1419cabdff1aSopenharmony_ci */
1420cabdff1aSopenharmony_cistatic int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
1421cabdff1aSopenharmony_ci{
1422cabdff1aSopenharmony_ci    uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
1423cabdff1aSopenharmony_ci    uint32_t hs_epoch;
1424cabdff1aSopenharmony_ci    uint32_t hs_my_epoch;
1425cabdff1aSopenharmony_ci    uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
1426cabdff1aSopenharmony_ci    uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
1427cabdff1aSopenharmony_ci    uint32_t zeroes;
1428cabdff1aSopenharmony_ci    uint32_t temp       = 0;
1429cabdff1aSopenharmony_ci    int randomidx       = 0;
1430cabdff1aSopenharmony_ci    int inoutsize       = 0;
1431cabdff1aSopenharmony_ci    int ret;
1432cabdff1aSopenharmony_ci
1433cabdff1aSopenharmony_ci    inoutsize = ffurl_read_complete(rt->stream, buffer, 1);       // Receive C0
1434cabdff1aSopenharmony_ci    if (inoutsize <= 0) {
1435cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
1436cabdff1aSopenharmony_ci        return AVERROR(EIO);
1437cabdff1aSopenharmony_ci    }
1438cabdff1aSopenharmony_ci    // Check Version
1439cabdff1aSopenharmony_ci    if (buffer[0] != 3) {
1440cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
1441cabdff1aSopenharmony_ci        return AVERROR(EIO);
1442cabdff1aSopenharmony_ci    }
1443cabdff1aSopenharmony_ci    if (ffurl_write(rt->stream, buffer, 1) <= 0) {                 // Send S0
1444cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1445cabdff1aSopenharmony_ci               "Unable to write answer - RTMP S0\n");
1446cabdff1aSopenharmony_ci        return AVERROR(EIO);
1447cabdff1aSopenharmony_ci    }
1448cabdff1aSopenharmony_ci    /* Receive C1 */
1449cabdff1aSopenharmony_ci    ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
1450cabdff1aSopenharmony_ci                                 RTMP_HANDSHAKE_PACKET_SIZE);
1451cabdff1aSopenharmony_ci    if (ret) {
1452cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
1453cabdff1aSopenharmony_ci        return ret;
1454cabdff1aSopenharmony_ci    }
1455cabdff1aSopenharmony_ci    /* Send S1 */
1456cabdff1aSopenharmony_ci    /* By now same epoch will be sent */
1457cabdff1aSopenharmony_ci    hs_my_epoch = hs_epoch;
1458cabdff1aSopenharmony_ci    /* Generate random */
1459cabdff1aSopenharmony_ci    for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
1460cabdff1aSopenharmony_ci         randomidx += 4)
1461cabdff1aSopenharmony_ci        AV_WB32(hs_s1 + randomidx, av_get_random_seed());
1462cabdff1aSopenharmony_ci
1463cabdff1aSopenharmony_ci    ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
1464cabdff1aSopenharmony_ci                              RTMP_HANDSHAKE_PACKET_SIZE);
1465cabdff1aSopenharmony_ci    if (ret) {
1466cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
1467cabdff1aSopenharmony_ci        return ret;
1468cabdff1aSopenharmony_ci    }
1469cabdff1aSopenharmony_ci    /* Send S2 */
1470cabdff1aSopenharmony_ci    ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
1471cabdff1aSopenharmony_ci                              RTMP_HANDSHAKE_PACKET_SIZE);
1472cabdff1aSopenharmony_ci    if (ret) {
1473cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
1474cabdff1aSopenharmony_ci        return ret;
1475cabdff1aSopenharmony_ci    }
1476cabdff1aSopenharmony_ci    /* Receive C2 */
1477cabdff1aSopenharmony_ci    ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
1478cabdff1aSopenharmony_ci                                 RTMP_HANDSHAKE_PACKET_SIZE);
1479cabdff1aSopenharmony_ci    if (ret) {
1480cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
1481cabdff1aSopenharmony_ci        return ret;
1482cabdff1aSopenharmony_ci    }
1483cabdff1aSopenharmony_ci    if (temp != hs_my_epoch)
1484cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING,
1485cabdff1aSopenharmony_ci               "Erroneous C2 Message epoch does not match up with C1 epoch\n");
1486cabdff1aSopenharmony_ci    if (memcmp(buffer + 8, hs_s1 + 8,
1487cabdff1aSopenharmony_ci               RTMP_HANDSHAKE_PACKET_SIZE - 8))
1488cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING,
1489cabdff1aSopenharmony_ci               "Erroneous C2 Message random does not match up\n");
1490cabdff1aSopenharmony_ci
1491cabdff1aSopenharmony_ci    return 0;
1492cabdff1aSopenharmony_ci}
1493cabdff1aSopenharmony_ci
1494cabdff1aSopenharmony_cistatic int handle_chunk_size(URLContext *s, RTMPPacket *pkt)
1495cabdff1aSopenharmony_ci{
1496cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1497cabdff1aSopenharmony_ci    int ret;
1498cabdff1aSopenharmony_ci
1499cabdff1aSopenharmony_ci    if (pkt->size < 4) {
1500cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1501cabdff1aSopenharmony_ci               "Too short chunk size change packet (%d)\n",
1502cabdff1aSopenharmony_ci               pkt->size);
1503cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1504cabdff1aSopenharmony_ci    }
1505cabdff1aSopenharmony_ci
1506cabdff1aSopenharmony_ci    if (!rt->is_input) {
1507cabdff1aSopenharmony_ci        /* Send the same chunk size change packet back to the server,
1508cabdff1aSopenharmony_ci         * setting the outgoing chunk size to the same as the incoming one. */
1509cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
1510cabdff1aSopenharmony_ci                                        &rt->prev_pkt[1], &rt->nb_prev_pkt[1])) < 0)
1511cabdff1aSopenharmony_ci            return ret;
1512cabdff1aSopenharmony_ci        rt->out_chunk_size = AV_RB32(pkt->data);
1513cabdff1aSopenharmony_ci    }
1514cabdff1aSopenharmony_ci
1515cabdff1aSopenharmony_ci    rt->in_chunk_size = AV_RB32(pkt->data);
1516cabdff1aSopenharmony_ci    if (rt->in_chunk_size <= 0) {
1517cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n",
1518cabdff1aSopenharmony_ci               rt->in_chunk_size);
1519cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1520cabdff1aSopenharmony_ci    }
1521cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "New incoming chunk size = %d\n",
1522cabdff1aSopenharmony_ci           rt->in_chunk_size);
1523cabdff1aSopenharmony_ci
1524cabdff1aSopenharmony_ci    return 0;
1525cabdff1aSopenharmony_ci}
1526cabdff1aSopenharmony_ci
1527cabdff1aSopenharmony_cistatic int handle_user_control(URLContext *s, RTMPPacket *pkt)
1528cabdff1aSopenharmony_ci{
1529cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1530cabdff1aSopenharmony_ci    int t, ret;
1531cabdff1aSopenharmony_ci
1532cabdff1aSopenharmony_ci    if (pkt->size < 2) {
1533cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Too short user control packet (%d)\n",
1534cabdff1aSopenharmony_ci               pkt->size);
1535cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1536cabdff1aSopenharmony_ci    }
1537cabdff1aSopenharmony_ci
1538cabdff1aSopenharmony_ci    t = AV_RB16(pkt->data);
1539cabdff1aSopenharmony_ci    if (t == 6) { // PingRequest
1540cabdff1aSopenharmony_ci        if ((ret = gen_pong(s, rt, pkt)) < 0)
1541cabdff1aSopenharmony_ci            return ret;
1542cabdff1aSopenharmony_ci    } else if (t == 26) {
1543cabdff1aSopenharmony_ci        if (rt->swfsize) {
1544cabdff1aSopenharmony_ci            if ((ret = gen_swf_verification(s, rt)) < 0)
1545cabdff1aSopenharmony_ci                return ret;
1546cabdff1aSopenharmony_ci        } else {
1547cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n");
1548cabdff1aSopenharmony_ci        }
1549cabdff1aSopenharmony_ci    }
1550cabdff1aSopenharmony_ci
1551cabdff1aSopenharmony_ci    return 0;
1552cabdff1aSopenharmony_ci}
1553cabdff1aSopenharmony_ci
1554cabdff1aSopenharmony_cistatic int handle_set_peer_bw(URLContext *s, RTMPPacket *pkt)
1555cabdff1aSopenharmony_ci{
1556cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1557cabdff1aSopenharmony_ci
1558cabdff1aSopenharmony_ci    if (pkt->size < 4) {
1559cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1560cabdff1aSopenharmony_ci               "Peer bandwidth packet is less than 4 bytes long (%d)\n",
1561cabdff1aSopenharmony_ci               pkt->size);
1562cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1563cabdff1aSopenharmony_ci    }
1564cabdff1aSopenharmony_ci
1565cabdff1aSopenharmony_ci    // We currently don't check how much the peer has acknowledged of
1566cabdff1aSopenharmony_ci    // what we have sent. To do that properly, we should call
1567cabdff1aSopenharmony_ci    // gen_window_ack_size here, to tell the peer that we want an
1568cabdff1aSopenharmony_ci    // acknowledgement with (at least) that interval.
1569cabdff1aSopenharmony_ci    rt->max_sent_unacked = AV_RB32(pkt->data);
1570cabdff1aSopenharmony_ci    if (rt->max_sent_unacked <= 0) {
1571cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Incorrect set peer bandwidth %d\n",
1572cabdff1aSopenharmony_ci               rt->max_sent_unacked);
1573cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1574cabdff1aSopenharmony_ci
1575cabdff1aSopenharmony_ci    }
1576cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Max sent, unacked = %d\n", rt->max_sent_unacked);
1577cabdff1aSopenharmony_ci
1578cabdff1aSopenharmony_ci    return 0;
1579cabdff1aSopenharmony_ci}
1580cabdff1aSopenharmony_ci
1581cabdff1aSopenharmony_cistatic int handle_window_ack_size(URLContext *s, RTMPPacket *pkt)
1582cabdff1aSopenharmony_ci{
1583cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1584cabdff1aSopenharmony_ci
1585cabdff1aSopenharmony_ci    if (pkt->size < 4) {
1586cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1587cabdff1aSopenharmony_ci               "Too short window acknowledgement size packet (%d)\n",
1588cabdff1aSopenharmony_ci               pkt->size);
1589cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1590cabdff1aSopenharmony_ci    }
1591cabdff1aSopenharmony_ci
1592cabdff1aSopenharmony_ci    rt->receive_report_size = AV_RB32(pkt->data);
1593cabdff1aSopenharmony_ci    if (rt->receive_report_size <= 0) {
1594cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Incorrect window acknowledgement size %d\n",
1595cabdff1aSopenharmony_ci               rt->receive_report_size);
1596cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1597cabdff1aSopenharmony_ci    }
1598cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Window acknowledgement size = %d\n", rt->receive_report_size);
1599cabdff1aSopenharmony_ci    // Send an Acknowledgement packet after receiving half the maximum
1600cabdff1aSopenharmony_ci    // size, to make sure the peer can keep on sending without waiting
1601cabdff1aSopenharmony_ci    // for acknowledgements.
1602cabdff1aSopenharmony_ci    rt->receive_report_size >>= 1;
1603cabdff1aSopenharmony_ci
1604cabdff1aSopenharmony_ci    return 0;
1605cabdff1aSopenharmony_ci}
1606cabdff1aSopenharmony_ci
1607cabdff1aSopenharmony_cistatic int do_adobe_auth(RTMPContext *rt, const char *user, const char *salt,
1608cabdff1aSopenharmony_ci                         const char *opaque, const char *challenge)
1609cabdff1aSopenharmony_ci{
1610cabdff1aSopenharmony_ci    uint8_t hash[16];
1611cabdff1aSopenharmony_ci    char hashstr[AV_BASE64_SIZE(sizeof(hash))], challenge2[10];
1612cabdff1aSopenharmony_ci    struct AVMD5 *md5 = av_md5_alloc();
1613cabdff1aSopenharmony_ci    if (!md5)
1614cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
1615cabdff1aSopenharmony_ci
1616cabdff1aSopenharmony_ci    snprintf(challenge2, sizeof(challenge2), "%08x", av_get_random_seed());
1617cabdff1aSopenharmony_ci
1618cabdff1aSopenharmony_ci    av_md5_init(md5);
1619cabdff1aSopenharmony_ci    av_md5_update(md5, user, strlen(user));
1620cabdff1aSopenharmony_ci    av_md5_update(md5, salt, strlen(salt));
1621cabdff1aSopenharmony_ci    av_md5_update(md5, rt->password, strlen(rt->password));
1622cabdff1aSopenharmony_ci    av_md5_final(md5, hash);
1623cabdff1aSopenharmony_ci    av_base64_encode(hashstr, sizeof(hashstr), hash,
1624cabdff1aSopenharmony_ci                     sizeof(hash));
1625cabdff1aSopenharmony_ci    av_md5_init(md5);
1626cabdff1aSopenharmony_ci    av_md5_update(md5, hashstr, strlen(hashstr));
1627cabdff1aSopenharmony_ci    if (opaque)
1628cabdff1aSopenharmony_ci        av_md5_update(md5, opaque, strlen(opaque));
1629cabdff1aSopenharmony_ci    else if (challenge)
1630cabdff1aSopenharmony_ci        av_md5_update(md5, challenge, strlen(challenge));
1631cabdff1aSopenharmony_ci    av_md5_update(md5, challenge2, strlen(challenge2));
1632cabdff1aSopenharmony_ci    av_md5_final(md5, hash);
1633cabdff1aSopenharmony_ci    av_base64_encode(hashstr, sizeof(hashstr), hash,
1634cabdff1aSopenharmony_ci                     sizeof(hash));
1635cabdff1aSopenharmony_ci    snprintf(rt->auth_params, sizeof(rt->auth_params),
1636cabdff1aSopenharmony_ci             "?authmod=%s&user=%s&challenge=%s&response=%s",
1637cabdff1aSopenharmony_ci             "adobe", user, challenge2, hashstr);
1638cabdff1aSopenharmony_ci    if (opaque)
1639cabdff1aSopenharmony_ci        av_strlcatf(rt->auth_params, sizeof(rt->auth_params),
1640cabdff1aSopenharmony_ci                    "&opaque=%s", opaque);
1641cabdff1aSopenharmony_ci
1642cabdff1aSopenharmony_ci    av_free(md5);
1643cabdff1aSopenharmony_ci    return 0;
1644cabdff1aSopenharmony_ci}
1645cabdff1aSopenharmony_ci
1646cabdff1aSopenharmony_cistatic int do_llnw_auth(RTMPContext *rt, const char *user, const char *nonce)
1647cabdff1aSopenharmony_ci{
1648cabdff1aSopenharmony_ci    uint8_t hash[16];
1649cabdff1aSopenharmony_ci    char hashstr1[33], hashstr2[33];
1650cabdff1aSopenharmony_ci    const char *realm = "live";
1651cabdff1aSopenharmony_ci    const char *method = "publish";
1652cabdff1aSopenharmony_ci    const char *qop = "auth";
1653cabdff1aSopenharmony_ci    const char *nc = "00000001";
1654cabdff1aSopenharmony_ci    char cnonce[10];
1655cabdff1aSopenharmony_ci    struct AVMD5 *md5 = av_md5_alloc();
1656cabdff1aSopenharmony_ci    if (!md5)
1657cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
1658cabdff1aSopenharmony_ci
1659cabdff1aSopenharmony_ci    snprintf(cnonce, sizeof(cnonce), "%08x", av_get_random_seed());
1660cabdff1aSopenharmony_ci
1661cabdff1aSopenharmony_ci    av_md5_init(md5);
1662cabdff1aSopenharmony_ci    av_md5_update(md5, user, strlen(user));
1663cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1664cabdff1aSopenharmony_ci    av_md5_update(md5, realm, strlen(realm));
1665cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1666cabdff1aSopenharmony_ci    av_md5_update(md5, rt->password, strlen(rt->password));
1667cabdff1aSopenharmony_ci    av_md5_final(md5, hash);
1668cabdff1aSopenharmony_ci    ff_data_to_hex(hashstr1, hash, 16, 1);
1669cabdff1aSopenharmony_ci
1670cabdff1aSopenharmony_ci    av_md5_init(md5);
1671cabdff1aSopenharmony_ci    av_md5_update(md5, method, strlen(method));
1672cabdff1aSopenharmony_ci    av_md5_update(md5, ":/", 2);
1673cabdff1aSopenharmony_ci    av_md5_update(md5, rt->app, strlen(rt->app));
1674cabdff1aSopenharmony_ci    if (!strchr(rt->app, '/'))
1675cabdff1aSopenharmony_ci        av_md5_update(md5, "/_definst_", strlen("/_definst_"));
1676cabdff1aSopenharmony_ci    av_md5_final(md5, hash);
1677cabdff1aSopenharmony_ci    ff_data_to_hex(hashstr2, hash, 16, 1);
1678cabdff1aSopenharmony_ci
1679cabdff1aSopenharmony_ci    av_md5_init(md5);
1680cabdff1aSopenharmony_ci    av_md5_update(md5, hashstr1, strlen(hashstr1));
1681cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1682cabdff1aSopenharmony_ci    if (nonce)
1683cabdff1aSopenharmony_ci        av_md5_update(md5, nonce, strlen(nonce));
1684cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1685cabdff1aSopenharmony_ci    av_md5_update(md5, nc, strlen(nc));
1686cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1687cabdff1aSopenharmony_ci    av_md5_update(md5, cnonce, strlen(cnonce));
1688cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1689cabdff1aSopenharmony_ci    av_md5_update(md5, qop, strlen(qop));
1690cabdff1aSopenharmony_ci    av_md5_update(md5, ":", 1);
1691cabdff1aSopenharmony_ci    av_md5_update(md5, hashstr2, strlen(hashstr2));
1692cabdff1aSopenharmony_ci    av_md5_final(md5, hash);
1693cabdff1aSopenharmony_ci    ff_data_to_hex(hashstr1, hash, 16, 1);
1694cabdff1aSopenharmony_ci
1695cabdff1aSopenharmony_ci    snprintf(rt->auth_params, sizeof(rt->auth_params),
1696cabdff1aSopenharmony_ci             "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
1697cabdff1aSopenharmony_ci             "llnw", user, nonce, cnonce, nc, hashstr1);
1698cabdff1aSopenharmony_ci
1699cabdff1aSopenharmony_ci    av_free(md5);
1700cabdff1aSopenharmony_ci    return 0;
1701cabdff1aSopenharmony_ci}
1702cabdff1aSopenharmony_ci
1703cabdff1aSopenharmony_cistatic int handle_connect_error(URLContext *s, const char *desc)
1704cabdff1aSopenharmony_ci{
1705cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1706cabdff1aSopenharmony_ci    char buf[300], *ptr, authmod[15];
1707cabdff1aSopenharmony_ci    int i = 0, ret = 0;
1708cabdff1aSopenharmony_ci    const char *user = "", *salt = "", *opaque = NULL,
1709cabdff1aSopenharmony_ci               *challenge = NULL, *cptr = NULL, *nonce = NULL;
1710cabdff1aSopenharmony_ci
1711cabdff1aSopenharmony_ci    if (!(cptr = strstr(desc, "authmod=adobe")) &&
1712cabdff1aSopenharmony_ci        !(cptr = strstr(desc, "authmod=llnw"))) {
1713cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
1714cabdff1aSopenharmony_ci               "Unknown connect error (unsupported authentication method?)\n");
1715cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1716cabdff1aSopenharmony_ci    }
1717cabdff1aSopenharmony_ci    cptr += strlen("authmod=");
1718cabdff1aSopenharmony_ci    while (*cptr && *cptr != ' ' && i < sizeof(authmod) - 1)
1719cabdff1aSopenharmony_ci        authmod[i++] = *cptr++;
1720cabdff1aSopenharmony_ci    authmod[i] = '\0';
1721cabdff1aSopenharmony_ci
1722cabdff1aSopenharmony_ci    if (!rt->username[0] || !rt->password[0]) {
1723cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "No credentials set\n");
1724cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1725cabdff1aSopenharmony_ci    }
1726cabdff1aSopenharmony_ci
1727cabdff1aSopenharmony_ci    if (strstr(desc, "?reason=authfailed")) {
1728cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Incorrect username/password\n");
1729cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1730cabdff1aSopenharmony_ci    } else if (strstr(desc, "?reason=nosuchuser")) {
1731cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Incorrect username\n");
1732cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1733cabdff1aSopenharmony_ci    }
1734cabdff1aSopenharmony_ci
1735cabdff1aSopenharmony_ci    if (rt->auth_tried) {
1736cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Authentication failed\n");
1737cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1738cabdff1aSopenharmony_ci    }
1739cabdff1aSopenharmony_ci
1740cabdff1aSopenharmony_ci    rt->auth_params[0] = '\0';
1741cabdff1aSopenharmony_ci
1742cabdff1aSopenharmony_ci    if (strstr(desc, "code=403 need auth")) {
1743cabdff1aSopenharmony_ci        snprintf(rt->auth_params, sizeof(rt->auth_params),
1744cabdff1aSopenharmony_ci                 "?authmod=%s&user=%s", authmod, rt->username);
1745cabdff1aSopenharmony_ci        return 0;
1746cabdff1aSopenharmony_ci    }
1747cabdff1aSopenharmony_ci
1748cabdff1aSopenharmony_ci    if (!(cptr = strstr(desc, "?reason=needauth"))) {
1749cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "No auth parameters found\n");
1750cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
1751cabdff1aSopenharmony_ci    }
1752cabdff1aSopenharmony_ci
1753cabdff1aSopenharmony_ci    av_strlcpy(buf, cptr + 1, sizeof(buf));
1754cabdff1aSopenharmony_ci    ptr = buf;
1755cabdff1aSopenharmony_ci
1756cabdff1aSopenharmony_ci    while (ptr) {
1757cabdff1aSopenharmony_ci        char *next  = strchr(ptr, '&');
1758cabdff1aSopenharmony_ci        char *value = strchr(ptr, '=');
1759cabdff1aSopenharmony_ci        if (next)
1760cabdff1aSopenharmony_ci            *next++ = '\0';
1761cabdff1aSopenharmony_ci        if (value) {
1762cabdff1aSopenharmony_ci            *value++ = '\0';
1763cabdff1aSopenharmony_ci            if (!strcmp(ptr, "user")) {
1764cabdff1aSopenharmony_ci                user = value;
1765cabdff1aSopenharmony_ci            } else if (!strcmp(ptr, "salt")) {
1766cabdff1aSopenharmony_ci                salt = value;
1767cabdff1aSopenharmony_ci            } else if (!strcmp(ptr, "opaque")) {
1768cabdff1aSopenharmony_ci                opaque = value;
1769cabdff1aSopenharmony_ci            } else if (!strcmp(ptr, "challenge")) {
1770cabdff1aSopenharmony_ci                challenge = value;
1771cabdff1aSopenharmony_ci            } else if (!strcmp(ptr, "nonce")) {
1772cabdff1aSopenharmony_ci                nonce = value;
1773cabdff1aSopenharmony_ci            } else {
1774cabdff1aSopenharmony_ci                av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr);
1775cabdff1aSopenharmony_ci            }
1776cabdff1aSopenharmony_ci        } else {
1777cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr);
1778cabdff1aSopenharmony_ci        }
1779cabdff1aSopenharmony_ci        ptr = next;
1780cabdff1aSopenharmony_ci    }
1781cabdff1aSopenharmony_ci
1782cabdff1aSopenharmony_ci    if (!strcmp(authmod, "adobe")) {
1783cabdff1aSopenharmony_ci        if ((ret = do_adobe_auth(rt, user, salt, opaque, challenge)) < 0)
1784cabdff1aSopenharmony_ci            return ret;
1785cabdff1aSopenharmony_ci    } else {
1786cabdff1aSopenharmony_ci        if ((ret = do_llnw_auth(rt, user, nonce)) < 0)
1787cabdff1aSopenharmony_ci            return ret;
1788cabdff1aSopenharmony_ci    }
1789cabdff1aSopenharmony_ci
1790cabdff1aSopenharmony_ci    rt->auth_tried = 1;
1791cabdff1aSopenharmony_ci    return 0;
1792cabdff1aSopenharmony_ci}
1793cabdff1aSopenharmony_ci
1794cabdff1aSopenharmony_cistatic int handle_invoke_error(URLContext *s, RTMPPacket *pkt)
1795cabdff1aSopenharmony_ci{
1796cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1797cabdff1aSopenharmony_ci    const uint8_t *data_end = pkt->data + pkt->size;
1798cabdff1aSopenharmony_ci    char *tracked_method = NULL;
1799cabdff1aSopenharmony_ci    int level = AV_LOG_ERROR;
1800cabdff1aSopenharmony_ci    uint8_t tmpstr[256];
1801cabdff1aSopenharmony_ci    int ret;
1802cabdff1aSopenharmony_ci
1803cabdff1aSopenharmony_ci    if ((ret = find_tracked_method(s, pkt, 9, &tracked_method)) < 0)
1804cabdff1aSopenharmony_ci        return ret;
1805cabdff1aSopenharmony_ci
1806cabdff1aSopenharmony_ci    if (!ff_amf_get_field_value(pkt->data + 9, data_end,
1807cabdff1aSopenharmony_ci                                "description", tmpstr, sizeof(tmpstr))) {
1808cabdff1aSopenharmony_ci        if (tracked_method && (!strcmp(tracked_method, "_checkbw")      ||
1809cabdff1aSopenharmony_ci                               !strcmp(tracked_method, "releaseStream") ||
1810cabdff1aSopenharmony_ci                               !strcmp(tracked_method, "FCSubscribe")   ||
1811cabdff1aSopenharmony_ci                               !strcmp(tracked_method, "FCPublish"))) {
1812cabdff1aSopenharmony_ci            /* Gracefully ignore Adobe-specific historical artifact errors. */
1813cabdff1aSopenharmony_ci            level = AV_LOG_WARNING;
1814cabdff1aSopenharmony_ci            ret = 0;
1815cabdff1aSopenharmony_ci        } else if (tracked_method && !strcmp(tracked_method, "getStreamLength")) {
1816cabdff1aSopenharmony_ci            level = rt->live ? AV_LOG_DEBUG : AV_LOG_WARNING;
1817cabdff1aSopenharmony_ci            ret = 0;
1818cabdff1aSopenharmony_ci        } else if (tracked_method && !strcmp(tracked_method, "connect")) {
1819cabdff1aSopenharmony_ci            ret = handle_connect_error(s, tmpstr);
1820cabdff1aSopenharmony_ci            if (!ret) {
1821cabdff1aSopenharmony_ci                rt->do_reconnect = 1;
1822cabdff1aSopenharmony_ci                level = AV_LOG_VERBOSE;
1823cabdff1aSopenharmony_ci            }
1824cabdff1aSopenharmony_ci        } else
1825cabdff1aSopenharmony_ci            ret = AVERROR_UNKNOWN;
1826cabdff1aSopenharmony_ci        av_log(s, level, "Server error: %s\n", tmpstr);
1827cabdff1aSopenharmony_ci    }
1828cabdff1aSopenharmony_ci
1829cabdff1aSopenharmony_ci    av_free(tracked_method);
1830cabdff1aSopenharmony_ci    return ret;
1831cabdff1aSopenharmony_ci}
1832cabdff1aSopenharmony_ci
1833cabdff1aSopenharmony_cistatic int write_begin(URLContext *s)
1834cabdff1aSopenharmony_ci{
1835cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1836cabdff1aSopenharmony_ci    PutByteContext pbc;
1837cabdff1aSopenharmony_ci    RTMPPacket spkt = { 0 };
1838cabdff1aSopenharmony_ci    int ret;
1839cabdff1aSopenharmony_ci
1840cabdff1aSopenharmony_ci    // Send Stream Begin 1
1841cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
1842cabdff1aSopenharmony_ci                                     RTMP_PT_USER_CONTROL, 0, 6)) < 0) {
1843cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1844cabdff1aSopenharmony_ci        return ret;
1845cabdff1aSopenharmony_ci    }
1846cabdff1aSopenharmony_ci
1847cabdff1aSopenharmony_ci    bytestream2_init_writer(&pbc, spkt.data, spkt.size);
1848cabdff1aSopenharmony_ci    bytestream2_put_be16(&pbc, 0);          // 0 -> Stream Begin
1849cabdff1aSopenharmony_ci    bytestream2_put_be32(&pbc, rt->nb_streamid);
1850cabdff1aSopenharmony_ci
1851cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1852cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
1853cabdff1aSopenharmony_ci
1854cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&spkt);
1855cabdff1aSopenharmony_ci
1856cabdff1aSopenharmony_ci    return ret;
1857cabdff1aSopenharmony_ci}
1858cabdff1aSopenharmony_ci
1859cabdff1aSopenharmony_cistatic int write_status(URLContext *s, RTMPPacket *pkt,
1860cabdff1aSopenharmony_ci                        const char *status, const char *description, const char *details)
1861cabdff1aSopenharmony_ci{
1862cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1863cabdff1aSopenharmony_ci    RTMPPacket spkt = { 0 };
1864cabdff1aSopenharmony_ci    uint8_t *pp;
1865cabdff1aSopenharmony_ci    int ret;
1866cabdff1aSopenharmony_ci
1867cabdff1aSopenharmony_ci    if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1868cabdff1aSopenharmony_ci                                     RTMP_PT_INVOKE, 0,
1869cabdff1aSopenharmony_ci                                     RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1870cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1871cabdff1aSopenharmony_ci        return ret;
1872cabdff1aSopenharmony_ci    }
1873cabdff1aSopenharmony_ci
1874cabdff1aSopenharmony_ci    pp = spkt.data;
1875cabdff1aSopenharmony_ci    spkt.extra = pkt->extra;
1876cabdff1aSopenharmony_ci    ff_amf_write_string(&pp, "onStatus");
1877cabdff1aSopenharmony_ci    ff_amf_write_number(&pp, 0);
1878cabdff1aSopenharmony_ci    ff_amf_write_null(&pp);
1879cabdff1aSopenharmony_ci
1880cabdff1aSopenharmony_ci    ff_amf_write_object_start(&pp);
1881cabdff1aSopenharmony_ci    ff_amf_write_field_name(&pp, "level");
1882cabdff1aSopenharmony_ci    ff_amf_write_string(&pp, "status");
1883cabdff1aSopenharmony_ci    ff_amf_write_field_name(&pp, "code");
1884cabdff1aSopenharmony_ci    ff_amf_write_string(&pp, status);
1885cabdff1aSopenharmony_ci    ff_amf_write_field_name(&pp, "description");
1886cabdff1aSopenharmony_ci    ff_amf_write_string(&pp, description);
1887cabdff1aSopenharmony_ci    if (details) {
1888cabdff1aSopenharmony_ci        ff_amf_write_field_name(&pp, "details");
1889cabdff1aSopenharmony_ci        ff_amf_write_string(&pp, details);
1890cabdff1aSopenharmony_ci    }
1891cabdff1aSopenharmony_ci    ff_amf_write_object_end(&pp);
1892cabdff1aSopenharmony_ci
1893cabdff1aSopenharmony_ci    spkt.size = pp - spkt.data;
1894cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1895cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
1896cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&spkt);
1897cabdff1aSopenharmony_ci
1898cabdff1aSopenharmony_ci    return ret;
1899cabdff1aSopenharmony_ci}
1900cabdff1aSopenharmony_ci
1901cabdff1aSopenharmony_cistatic int send_invoke_response(URLContext *s, RTMPPacket *pkt)
1902cabdff1aSopenharmony_ci{
1903cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
1904cabdff1aSopenharmony_ci    double seqnum;
1905cabdff1aSopenharmony_ci    char filename[128];
1906cabdff1aSopenharmony_ci    char command[64];
1907cabdff1aSopenharmony_ci    int stringlen;
1908cabdff1aSopenharmony_ci    char *pchar;
1909cabdff1aSopenharmony_ci    const uint8_t *p = pkt->data;
1910cabdff1aSopenharmony_ci    uint8_t *pp      = NULL;
1911cabdff1aSopenharmony_ci    RTMPPacket spkt  = { 0 };
1912cabdff1aSopenharmony_ci    GetByteContext gbc;
1913cabdff1aSopenharmony_ci    int ret;
1914cabdff1aSopenharmony_ci
1915cabdff1aSopenharmony_ci    bytestream2_init(&gbc, p, pkt->size);
1916cabdff1aSopenharmony_ci    if (ff_amf_read_string(&gbc, command, sizeof(command),
1917cabdff1aSopenharmony_ci                           &stringlen)) {
1918cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Error in PT_INVOKE\n");
1919cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
1920cabdff1aSopenharmony_ci    }
1921cabdff1aSopenharmony_ci
1922cabdff1aSopenharmony_ci    ret = ff_amf_read_number(&gbc, &seqnum);
1923cabdff1aSopenharmony_ci    if (ret)
1924cabdff1aSopenharmony_ci        return ret;
1925cabdff1aSopenharmony_ci    ret = ff_amf_read_null(&gbc);
1926cabdff1aSopenharmony_ci    if (ret)
1927cabdff1aSopenharmony_ci        return ret;
1928cabdff1aSopenharmony_ci    if (!strcmp(command, "FCPublish") ||
1929cabdff1aSopenharmony_ci        !strcmp(command, "publish")) {
1930cabdff1aSopenharmony_ci        ret = ff_amf_read_string(&gbc, filename,
1931cabdff1aSopenharmony_ci                                 sizeof(filename), &stringlen);
1932cabdff1aSopenharmony_ci        if (ret) {
1933cabdff1aSopenharmony_ci            if (ret == AVERROR(EINVAL))
1934cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Unable to parse stream name - name too long?\n");
1935cabdff1aSopenharmony_ci            else
1936cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Unable to parse stream name\n");
1937cabdff1aSopenharmony_ci            return ret;
1938cabdff1aSopenharmony_ci        }
1939cabdff1aSopenharmony_ci        // check with url
1940cabdff1aSopenharmony_ci        if (s->filename) {
1941cabdff1aSopenharmony_ci            pchar = strrchr(s->filename, '/');
1942cabdff1aSopenharmony_ci            if (!pchar) {
1943cabdff1aSopenharmony_ci                av_log(s, AV_LOG_WARNING,
1944cabdff1aSopenharmony_ci                       "Unable to find / in url %s, bad format\n",
1945cabdff1aSopenharmony_ci                       s->filename);
1946cabdff1aSopenharmony_ci                pchar = s->filename;
1947cabdff1aSopenharmony_ci            }
1948cabdff1aSopenharmony_ci            pchar++;
1949cabdff1aSopenharmony_ci            if (strcmp(pchar, filename))
1950cabdff1aSopenharmony_ci                av_log(s, AV_LOG_WARNING, "Unexpected stream %s, expecting"
1951cabdff1aSopenharmony_ci                       " %s\n", filename, pchar);
1952cabdff1aSopenharmony_ci        }
1953cabdff1aSopenharmony_ci        rt->state = STATE_RECEIVING;
1954cabdff1aSopenharmony_ci    }
1955cabdff1aSopenharmony_ci
1956cabdff1aSopenharmony_ci    if (!strcmp(command, "FCPublish")) {
1957cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1958cabdff1aSopenharmony_ci                                         RTMP_PT_INVOKE, 0,
1959cabdff1aSopenharmony_ci                                         RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1960cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1961cabdff1aSopenharmony_ci            return ret;
1962cabdff1aSopenharmony_ci        }
1963cabdff1aSopenharmony_ci        pp = spkt.data;
1964cabdff1aSopenharmony_ci        ff_amf_write_string(&pp, "onFCPublish");
1965cabdff1aSopenharmony_ci    } else if (!strcmp(command, "publish")) {
1966cabdff1aSopenharmony_ci        char statusmsg[128];
1967cabdff1aSopenharmony_ci        snprintf(statusmsg, sizeof(statusmsg), "%s is now published", filename);
1968cabdff1aSopenharmony_ci        ret = write_begin(s);
1969cabdff1aSopenharmony_ci        if (ret < 0)
1970cabdff1aSopenharmony_ci            return ret;
1971cabdff1aSopenharmony_ci
1972cabdff1aSopenharmony_ci        // Send onStatus(NetStream.Publish.Start)
1973cabdff1aSopenharmony_ci        return write_status(s, pkt, "NetStream.Publish.Start",
1974cabdff1aSopenharmony_ci                            statusmsg, filename);
1975cabdff1aSopenharmony_ci    } else if (!strcmp(command, "play")) {
1976cabdff1aSopenharmony_ci        ret = write_begin(s);
1977cabdff1aSopenharmony_ci        if (ret < 0)
1978cabdff1aSopenharmony_ci            return ret;
1979cabdff1aSopenharmony_ci        rt->state = STATE_SENDING;
1980cabdff1aSopenharmony_ci        return write_status(s, pkt, "NetStream.Play.Start",
1981cabdff1aSopenharmony_ci                            "playing stream", NULL);
1982cabdff1aSopenharmony_ci    } else {
1983cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1984cabdff1aSopenharmony_ci                                         RTMP_PT_INVOKE, 0,
1985cabdff1aSopenharmony_ci                                         RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1986cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1987cabdff1aSopenharmony_ci            return ret;
1988cabdff1aSopenharmony_ci        }
1989cabdff1aSopenharmony_ci        pp = spkt.data;
1990cabdff1aSopenharmony_ci        ff_amf_write_string(&pp, "_result");
1991cabdff1aSopenharmony_ci        ff_amf_write_number(&pp, seqnum);
1992cabdff1aSopenharmony_ci        ff_amf_write_null(&pp);
1993cabdff1aSopenharmony_ci        if (!strcmp(command, "createStream")) {
1994cabdff1aSopenharmony_ci            rt->nb_streamid++;
1995cabdff1aSopenharmony_ci            if (rt->nb_streamid == 0 || rt->nb_streamid == 2)
1996cabdff1aSopenharmony_ci                rt->nb_streamid++; /* Values 0 and 2 are reserved */
1997cabdff1aSopenharmony_ci            ff_amf_write_number(&pp, rt->nb_streamid);
1998cabdff1aSopenharmony_ci            /* By now we don't control which streams are removed in
1999cabdff1aSopenharmony_ci             * deleteStream. There is no stream creation control
2000cabdff1aSopenharmony_ci             * if a client creates more than 2^32 - 2 streams. */
2001cabdff1aSopenharmony_ci        }
2002cabdff1aSopenharmony_ci    }
2003cabdff1aSopenharmony_ci    spkt.size = pp - spkt.data;
2004cabdff1aSopenharmony_ci    ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
2005cabdff1aSopenharmony_ci                               &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
2006cabdff1aSopenharmony_ci    ff_rtmp_packet_destroy(&spkt);
2007cabdff1aSopenharmony_ci    return ret;
2008cabdff1aSopenharmony_ci}
2009cabdff1aSopenharmony_ci
2010cabdff1aSopenharmony_ci/**
2011cabdff1aSopenharmony_ci * Read the AMF_NUMBER response ("_result") to a function call
2012cabdff1aSopenharmony_ci * (e.g. createStream()). This response should be made up of the AMF_STRING
2013cabdff1aSopenharmony_ci * "result", a NULL object and then the response encoded as AMF_NUMBER. On a
2014cabdff1aSopenharmony_ci * successful response, we will return set the value to number (otherwise number
2015cabdff1aSopenharmony_ci * will not be changed).
2016cabdff1aSopenharmony_ci *
2017cabdff1aSopenharmony_ci * @return 0 if reading the value succeeds, negative value otherwise
2018cabdff1aSopenharmony_ci */
2019cabdff1aSopenharmony_cistatic int read_number_result(RTMPPacket *pkt, double *number)
2020cabdff1aSopenharmony_ci{
2021cabdff1aSopenharmony_ci    // We only need to fit "_result" in this.
2022cabdff1aSopenharmony_ci    uint8_t strbuffer[8];
2023cabdff1aSopenharmony_ci    int stringlen;
2024cabdff1aSopenharmony_ci    double numbuffer;
2025cabdff1aSopenharmony_ci    GetByteContext gbc;
2026cabdff1aSopenharmony_ci
2027cabdff1aSopenharmony_ci    bytestream2_init(&gbc, pkt->data, pkt->size);
2028cabdff1aSopenharmony_ci
2029cabdff1aSopenharmony_ci    // Value 1/4: "_result" as AMF_STRING
2030cabdff1aSopenharmony_ci    if (ff_amf_read_string(&gbc, strbuffer, sizeof(strbuffer), &stringlen))
2031cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2032cabdff1aSopenharmony_ci    if (strcmp(strbuffer, "_result"))
2033cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2034cabdff1aSopenharmony_ci    // Value 2/4: The callee reference number
2035cabdff1aSopenharmony_ci    if (ff_amf_read_number(&gbc, &numbuffer))
2036cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2037cabdff1aSopenharmony_ci    // Value 3/4: Null
2038cabdff1aSopenharmony_ci    if (ff_amf_read_null(&gbc))
2039cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2040cabdff1aSopenharmony_ci    // Value 4/4: The response as AMF_NUMBER
2041cabdff1aSopenharmony_ci    if (ff_amf_read_number(&gbc, &numbuffer))
2042cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2043cabdff1aSopenharmony_ci    else
2044cabdff1aSopenharmony_ci        *number = numbuffer;
2045cabdff1aSopenharmony_ci
2046cabdff1aSopenharmony_ci    return 0;
2047cabdff1aSopenharmony_ci}
2048cabdff1aSopenharmony_ci
2049cabdff1aSopenharmony_cistatic int handle_invoke_result(URLContext *s, RTMPPacket *pkt)
2050cabdff1aSopenharmony_ci{
2051cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2052cabdff1aSopenharmony_ci    char *tracked_method = NULL;
2053cabdff1aSopenharmony_ci    int ret = 0;
2054cabdff1aSopenharmony_ci
2055cabdff1aSopenharmony_ci    if ((ret = find_tracked_method(s, pkt, 10, &tracked_method)) < 0)
2056cabdff1aSopenharmony_ci        return ret;
2057cabdff1aSopenharmony_ci
2058cabdff1aSopenharmony_ci    if (!tracked_method) {
2059cabdff1aSopenharmony_ci        /* Ignore this reply when the current method is not tracked. */
2060cabdff1aSopenharmony_ci        return ret;
2061cabdff1aSopenharmony_ci    }
2062cabdff1aSopenharmony_ci
2063cabdff1aSopenharmony_ci    if (!strcmp(tracked_method, "connect")) {
2064cabdff1aSopenharmony_ci        if (!rt->is_input) {
2065cabdff1aSopenharmony_ci            if ((ret = gen_release_stream(s, rt)) < 0)
2066cabdff1aSopenharmony_ci                goto fail;
2067cabdff1aSopenharmony_ci
2068cabdff1aSopenharmony_ci            if ((ret = gen_fcpublish_stream(s, rt)) < 0)
2069cabdff1aSopenharmony_ci                goto fail;
2070cabdff1aSopenharmony_ci        } else {
2071cabdff1aSopenharmony_ci            if ((ret = gen_window_ack_size(s, rt)) < 0)
2072cabdff1aSopenharmony_ci                goto fail;
2073cabdff1aSopenharmony_ci        }
2074cabdff1aSopenharmony_ci
2075cabdff1aSopenharmony_ci        if ((ret = gen_create_stream(s, rt)) < 0)
2076cabdff1aSopenharmony_ci            goto fail;
2077cabdff1aSopenharmony_ci
2078cabdff1aSopenharmony_ci        if (rt->is_input) {
2079cabdff1aSopenharmony_ci            /* Send the FCSubscribe command when the name of live
2080cabdff1aSopenharmony_ci             * stream is defined by the user or if it's a live stream. */
2081cabdff1aSopenharmony_ci            if (rt->subscribe) {
2082cabdff1aSopenharmony_ci                if ((ret = gen_fcsubscribe_stream(s, rt, rt->subscribe)) < 0)
2083cabdff1aSopenharmony_ci                    goto fail;
2084cabdff1aSopenharmony_ci            } else if (rt->live == -1) {
2085cabdff1aSopenharmony_ci                if ((ret = gen_fcsubscribe_stream(s, rt, rt->playpath)) < 0)
2086cabdff1aSopenharmony_ci                    goto fail;
2087cabdff1aSopenharmony_ci            }
2088cabdff1aSopenharmony_ci        }
2089cabdff1aSopenharmony_ci    } else if (!strcmp(tracked_method, "createStream")) {
2090cabdff1aSopenharmony_ci        double stream_id;
2091cabdff1aSopenharmony_ci        if (read_number_result(pkt, &stream_id)) {
2092cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
2093cabdff1aSopenharmony_ci        } else {
2094cabdff1aSopenharmony_ci            rt->stream_id = stream_id;
2095cabdff1aSopenharmony_ci        }
2096cabdff1aSopenharmony_ci
2097cabdff1aSopenharmony_ci        if (!rt->is_input) {
2098cabdff1aSopenharmony_ci            if ((ret = gen_publish(s, rt)) < 0)
2099cabdff1aSopenharmony_ci                goto fail;
2100cabdff1aSopenharmony_ci        } else {
2101cabdff1aSopenharmony_ci            if (rt->live != -1) {
2102cabdff1aSopenharmony_ci                if ((ret = gen_get_stream_length(s, rt)) < 0)
2103cabdff1aSopenharmony_ci                    goto fail;
2104cabdff1aSopenharmony_ci            }
2105cabdff1aSopenharmony_ci            if ((ret = gen_play(s, rt)) < 0)
2106cabdff1aSopenharmony_ci                goto fail;
2107cabdff1aSopenharmony_ci            if ((ret = gen_buffer_time(s, rt)) < 0)
2108cabdff1aSopenharmony_ci                goto fail;
2109cabdff1aSopenharmony_ci        }
2110cabdff1aSopenharmony_ci    } else if (!strcmp(tracked_method, "getStreamLength")) {
2111cabdff1aSopenharmony_ci        if (read_number_result(pkt, &rt->duration)) {
2112cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "Unexpected reply on getStreamLength()\n");
2113cabdff1aSopenharmony_ci        }
2114cabdff1aSopenharmony_ci    }
2115cabdff1aSopenharmony_ci
2116cabdff1aSopenharmony_cifail:
2117cabdff1aSopenharmony_ci    av_free(tracked_method);
2118cabdff1aSopenharmony_ci    return ret;
2119cabdff1aSopenharmony_ci}
2120cabdff1aSopenharmony_ci
2121cabdff1aSopenharmony_cistatic int handle_invoke_status(URLContext *s, RTMPPacket *pkt)
2122cabdff1aSopenharmony_ci{
2123cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2124cabdff1aSopenharmony_ci    const uint8_t *data_end = pkt->data + pkt->size;
2125cabdff1aSopenharmony_ci    const uint8_t *ptr = pkt->data + RTMP_HEADER;
2126cabdff1aSopenharmony_ci    uint8_t tmpstr[256];
2127cabdff1aSopenharmony_ci    int i, t;
2128cabdff1aSopenharmony_ci
2129cabdff1aSopenharmony_ci    for (i = 0; i < 2; i++) {
2130cabdff1aSopenharmony_ci        t = ff_amf_tag_size(ptr, data_end);
2131cabdff1aSopenharmony_ci        if (t < 0)
2132cabdff1aSopenharmony_ci            return 1;
2133cabdff1aSopenharmony_ci        ptr += t;
2134cabdff1aSopenharmony_ci    }
2135cabdff1aSopenharmony_ci
2136cabdff1aSopenharmony_ci    t = ff_amf_get_field_value(ptr, data_end, "level", tmpstr, sizeof(tmpstr));
2137cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "error")) {
2138cabdff1aSopenharmony_ci        t = ff_amf_get_field_value(ptr, data_end,
2139cabdff1aSopenharmony_ci                                   "description", tmpstr, sizeof(tmpstr));
2140cabdff1aSopenharmony_ci        if (t || !tmpstr[0])
2141cabdff1aSopenharmony_ci            t = ff_amf_get_field_value(ptr, data_end, "code",
2142cabdff1aSopenharmony_ci                                       tmpstr, sizeof(tmpstr));
2143cabdff1aSopenharmony_ci        if (!t)
2144cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr);
2145cabdff1aSopenharmony_ci        return -1;
2146cabdff1aSopenharmony_ci    }
2147cabdff1aSopenharmony_ci
2148cabdff1aSopenharmony_ci    t = ff_amf_get_field_value(ptr, data_end, "code", tmpstr, sizeof(tmpstr));
2149cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING;
2150cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED;
2151cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED;
2152cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING;
2153cabdff1aSopenharmony_ci    if (!t && !strcmp(tmpstr, "NetStream.Seek.Notify")) rt->state = STATE_PLAYING;
2154cabdff1aSopenharmony_ci
2155cabdff1aSopenharmony_ci    return 0;
2156cabdff1aSopenharmony_ci}
2157cabdff1aSopenharmony_ci
2158cabdff1aSopenharmony_cistatic int handle_invoke(URLContext *s, RTMPPacket *pkt)
2159cabdff1aSopenharmony_ci{
2160cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2161cabdff1aSopenharmony_ci    int ret = 0;
2162cabdff1aSopenharmony_ci
2163cabdff1aSopenharmony_ci    //TODO: check for the messages sent for wrong state?
2164cabdff1aSopenharmony_ci    if (ff_amf_match_string(pkt->data, pkt->size, "_error")) {
2165cabdff1aSopenharmony_ci        if ((ret = handle_invoke_error(s, pkt)) < 0)
2166cabdff1aSopenharmony_ci            return ret;
2167cabdff1aSopenharmony_ci    } else if (ff_amf_match_string(pkt->data, pkt->size, "_result")) {
2168cabdff1aSopenharmony_ci        if ((ret = handle_invoke_result(s, pkt)) < 0)
2169cabdff1aSopenharmony_ci            return ret;
2170cabdff1aSopenharmony_ci    } else if (ff_amf_match_string(pkt->data, pkt->size, "onStatus")) {
2171cabdff1aSopenharmony_ci        if ((ret = handle_invoke_status(s, pkt)) < 0)
2172cabdff1aSopenharmony_ci            return ret;
2173cabdff1aSopenharmony_ci    } else if (ff_amf_match_string(pkt->data, pkt->size, "onBWDone")) {
2174cabdff1aSopenharmony_ci        if ((ret = gen_check_bw(s, rt)) < 0)
2175cabdff1aSopenharmony_ci            return ret;
2176cabdff1aSopenharmony_ci    } else if (ff_amf_match_string(pkt->data, pkt->size, "releaseStream") ||
2177cabdff1aSopenharmony_ci               ff_amf_match_string(pkt->data, pkt->size, "FCPublish")     ||
2178cabdff1aSopenharmony_ci               ff_amf_match_string(pkt->data, pkt->size, "publish")       ||
2179cabdff1aSopenharmony_ci               ff_amf_match_string(pkt->data, pkt->size, "play")          ||
2180cabdff1aSopenharmony_ci               ff_amf_match_string(pkt->data, pkt->size, "_checkbw")      ||
2181cabdff1aSopenharmony_ci               ff_amf_match_string(pkt->data, pkt->size, "createStream")) {
2182cabdff1aSopenharmony_ci        if ((ret = send_invoke_response(s, pkt)) < 0)
2183cabdff1aSopenharmony_ci            return ret;
2184cabdff1aSopenharmony_ci    }
2185cabdff1aSopenharmony_ci
2186cabdff1aSopenharmony_ci    return ret;
2187cabdff1aSopenharmony_ci}
2188cabdff1aSopenharmony_ci
2189cabdff1aSopenharmony_cistatic int update_offset(RTMPContext *rt, int size)
2190cabdff1aSopenharmony_ci{
2191cabdff1aSopenharmony_ci    int old_flv_size;
2192cabdff1aSopenharmony_ci
2193cabdff1aSopenharmony_ci    // generate packet header and put data into buffer for FLV demuxer
2194cabdff1aSopenharmony_ci    if (rt->flv_off < rt->flv_size) {
2195cabdff1aSopenharmony_ci        // There is old unread data in the buffer, thus append at the end
2196cabdff1aSopenharmony_ci        old_flv_size  = rt->flv_size;
2197cabdff1aSopenharmony_ci        rt->flv_size += size;
2198cabdff1aSopenharmony_ci    } else {
2199cabdff1aSopenharmony_ci        // All data has been read, write the new data at the start of the buffer
2200cabdff1aSopenharmony_ci        old_flv_size = 0;
2201cabdff1aSopenharmony_ci        rt->flv_size = size;
2202cabdff1aSopenharmony_ci        rt->flv_off  = 0;
2203cabdff1aSopenharmony_ci    }
2204cabdff1aSopenharmony_ci
2205cabdff1aSopenharmony_ci    return old_flv_size;
2206cabdff1aSopenharmony_ci}
2207cabdff1aSopenharmony_ci
2208cabdff1aSopenharmony_cistatic int append_flv_data(RTMPContext *rt, RTMPPacket *pkt, int skip)
2209cabdff1aSopenharmony_ci{
2210cabdff1aSopenharmony_ci    int old_flv_size, ret;
2211cabdff1aSopenharmony_ci    PutByteContext pbc;
2212cabdff1aSopenharmony_ci    const uint8_t *data = pkt->data + skip;
2213cabdff1aSopenharmony_ci    const int size      = pkt->size - skip;
2214cabdff1aSopenharmony_ci    uint32_t ts         = pkt->timestamp;
2215cabdff1aSopenharmony_ci
2216cabdff1aSopenharmony_ci    if (pkt->type == RTMP_PT_AUDIO) {
2217cabdff1aSopenharmony_ci        rt->has_audio = 1;
2218cabdff1aSopenharmony_ci    } else if (pkt->type == RTMP_PT_VIDEO) {
2219cabdff1aSopenharmony_ci        rt->has_video = 1;
2220cabdff1aSopenharmony_ci    }
2221cabdff1aSopenharmony_ci
2222cabdff1aSopenharmony_ci    old_flv_size = update_offset(rt, size + 15);
2223cabdff1aSopenharmony_ci
2224cabdff1aSopenharmony_ci    if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
2225cabdff1aSopenharmony_ci        rt->flv_size = rt->flv_off = 0;
2226cabdff1aSopenharmony_ci        return ret;
2227cabdff1aSopenharmony_ci    }
2228cabdff1aSopenharmony_ci    bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
2229cabdff1aSopenharmony_ci    bytestream2_skip_p(&pbc, old_flv_size);
2230cabdff1aSopenharmony_ci    bytestream2_put_byte(&pbc, pkt->type);
2231cabdff1aSopenharmony_ci    bytestream2_put_be24(&pbc, size);
2232cabdff1aSopenharmony_ci    bytestream2_put_be24(&pbc, ts);
2233cabdff1aSopenharmony_ci    bytestream2_put_byte(&pbc, ts >> 24);
2234cabdff1aSopenharmony_ci    bytestream2_put_be24(&pbc, 0);
2235cabdff1aSopenharmony_ci    bytestream2_put_buffer(&pbc, data, size);
2236cabdff1aSopenharmony_ci    bytestream2_put_be32(&pbc, size + RTMP_HEADER);
2237cabdff1aSopenharmony_ci
2238cabdff1aSopenharmony_ci    return 0;
2239cabdff1aSopenharmony_ci}
2240cabdff1aSopenharmony_ci
2241cabdff1aSopenharmony_cistatic int handle_notify(URLContext *s, RTMPPacket *pkt)
2242cabdff1aSopenharmony_ci{
2243cabdff1aSopenharmony_ci    RTMPContext *rt  = s->priv_data;
2244cabdff1aSopenharmony_ci    uint8_t commandbuffer[64];
2245cabdff1aSopenharmony_ci    char statusmsg[128];
2246cabdff1aSopenharmony_ci    int stringlen, ret, skip = 0;
2247cabdff1aSopenharmony_ci    GetByteContext gbc;
2248cabdff1aSopenharmony_ci
2249cabdff1aSopenharmony_ci    bytestream2_init(&gbc, pkt->data, pkt->size);
2250cabdff1aSopenharmony_ci    if (ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer),
2251cabdff1aSopenharmony_ci                           &stringlen))
2252cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
2253cabdff1aSopenharmony_ci
2254cabdff1aSopenharmony_ci    if (!strcmp(commandbuffer, "onMetaData")) {
2255cabdff1aSopenharmony_ci        // metadata properties should be stored in a mixed array
2256cabdff1aSopenharmony_ci        if (bytestream2_get_byte(&gbc) == AMF_DATA_TYPE_MIXEDARRAY) {
2257cabdff1aSopenharmony_ci            // We have found a metaData Array so flv can determine the streams
2258cabdff1aSopenharmony_ci            // from this.
2259cabdff1aSopenharmony_ci            rt->received_metadata = 1;
2260cabdff1aSopenharmony_ci            // skip 32-bit max array index
2261cabdff1aSopenharmony_ci            bytestream2_skip(&gbc, 4);
2262cabdff1aSopenharmony_ci            while (bytestream2_get_bytes_left(&gbc) > 3) {
2263cabdff1aSopenharmony_ci                if (ff_amf_get_string(&gbc, statusmsg, sizeof(statusmsg),
2264cabdff1aSopenharmony_ci                                      &stringlen))
2265cabdff1aSopenharmony_ci                    return AVERROR_INVALIDDATA;
2266cabdff1aSopenharmony_ci                // We do not care about the content of the property (yet).
2267cabdff1aSopenharmony_ci                stringlen = ff_amf_tag_size(gbc.buffer, gbc.buffer_end);
2268cabdff1aSopenharmony_ci                if (stringlen < 0)
2269cabdff1aSopenharmony_ci                    return AVERROR_INVALIDDATA;
2270cabdff1aSopenharmony_ci                bytestream2_skip(&gbc, stringlen);
2271cabdff1aSopenharmony_ci
2272cabdff1aSopenharmony_ci                // The presence of the following properties indicates that the
2273cabdff1aSopenharmony_ci                // respective streams are present.
2274cabdff1aSopenharmony_ci                if (!strcmp(statusmsg, "videocodecid")) {
2275cabdff1aSopenharmony_ci                    rt->has_video = 1;
2276cabdff1aSopenharmony_ci                }
2277cabdff1aSopenharmony_ci                if (!strcmp(statusmsg, "audiocodecid")) {
2278cabdff1aSopenharmony_ci                    rt->has_audio = 1;
2279cabdff1aSopenharmony_ci                }
2280cabdff1aSopenharmony_ci            }
2281cabdff1aSopenharmony_ci            if (bytestream2_get_be24(&gbc) != AMF_END_OF_OBJECT)
2282cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
2283cabdff1aSopenharmony_ci        }
2284cabdff1aSopenharmony_ci    }
2285cabdff1aSopenharmony_ci
2286cabdff1aSopenharmony_ci    // Skip the @setDataFrame string and validate it is a notification
2287cabdff1aSopenharmony_ci    if (!strcmp(commandbuffer, "@setDataFrame")) {
2288cabdff1aSopenharmony_ci        skip = gbc.buffer - pkt->data;
2289cabdff1aSopenharmony_ci        ret = ff_amf_read_string(&gbc, statusmsg,
2290cabdff1aSopenharmony_ci                                 sizeof(statusmsg), &stringlen);
2291cabdff1aSopenharmony_ci        if (ret < 0)
2292cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
2293cabdff1aSopenharmony_ci    }
2294cabdff1aSopenharmony_ci
2295cabdff1aSopenharmony_ci    return append_flv_data(rt, pkt, skip);
2296cabdff1aSopenharmony_ci}
2297cabdff1aSopenharmony_ci
2298cabdff1aSopenharmony_ci/**
2299cabdff1aSopenharmony_ci * Parse received packet and possibly perform some action depending on
2300cabdff1aSopenharmony_ci * the packet contents.
2301cabdff1aSopenharmony_ci * @return 0 for no errors, negative values for serious errors which prevent
2302cabdff1aSopenharmony_ci *         further communications, positive values for uncritical errors
2303cabdff1aSopenharmony_ci */
2304cabdff1aSopenharmony_cistatic int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
2305cabdff1aSopenharmony_ci{
2306cabdff1aSopenharmony_ci    int ret;
2307cabdff1aSopenharmony_ci
2308cabdff1aSopenharmony_ci#ifdef DEBUG
2309cabdff1aSopenharmony_ci    ff_rtmp_packet_dump(s, pkt);
2310cabdff1aSopenharmony_ci#endif
2311cabdff1aSopenharmony_ci
2312cabdff1aSopenharmony_ci    switch (pkt->type) {
2313cabdff1aSopenharmony_ci    case RTMP_PT_BYTES_READ:
2314cabdff1aSopenharmony_ci        av_log(s, AV_LOG_TRACE, "received bytes read report\n");
2315cabdff1aSopenharmony_ci        break;
2316cabdff1aSopenharmony_ci    case RTMP_PT_CHUNK_SIZE:
2317cabdff1aSopenharmony_ci        if ((ret = handle_chunk_size(s, pkt)) < 0)
2318cabdff1aSopenharmony_ci            return ret;
2319cabdff1aSopenharmony_ci        break;
2320cabdff1aSopenharmony_ci    case RTMP_PT_USER_CONTROL:
2321cabdff1aSopenharmony_ci        if ((ret = handle_user_control(s, pkt)) < 0)
2322cabdff1aSopenharmony_ci            return ret;
2323cabdff1aSopenharmony_ci        break;
2324cabdff1aSopenharmony_ci    case RTMP_PT_SET_PEER_BW:
2325cabdff1aSopenharmony_ci        if ((ret = handle_set_peer_bw(s, pkt)) < 0)
2326cabdff1aSopenharmony_ci            return ret;
2327cabdff1aSopenharmony_ci        break;
2328cabdff1aSopenharmony_ci    case RTMP_PT_WINDOW_ACK_SIZE:
2329cabdff1aSopenharmony_ci        if ((ret = handle_window_ack_size(s, pkt)) < 0)
2330cabdff1aSopenharmony_ci            return ret;
2331cabdff1aSopenharmony_ci        break;
2332cabdff1aSopenharmony_ci    case RTMP_PT_INVOKE:
2333cabdff1aSopenharmony_ci        if ((ret = handle_invoke(s, pkt)) < 0)
2334cabdff1aSopenharmony_ci            return ret;
2335cabdff1aSopenharmony_ci        break;
2336cabdff1aSopenharmony_ci    case RTMP_PT_VIDEO:
2337cabdff1aSopenharmony_ci    case RTMP_PT_AUDIO:
2338cabdff1aSopenharmony_ci    case RTMP_PT_METADATA:
2339cabdff1aSopenharmony_ci    case RTMP_PT_NOTIFY:
2340cabdff1aSopenharmony_ci        /* Audio, Video and Metadata packets are parsed in get_packet() */
2341cabdff1aSopenharmony_ci        break;
2342cabdff1aSopenharmony_ci    default:
2343cabdff1aSopenharmony_ci        av_log(s, AV_LOG_VERBOSE, "Unknown packet type received 0x%02X\n", pkt->type);
2344cabdff1aSopenharmony_ci        break;
2345cabdff1aSopenharmony_ci    }
2346cabdff1aSopenharmony_ci    return 0;
2347cabdff1aSopenharmony_ci}
2348cabdff1aSopenharmony_ci
2349cabdff1aSopenharmony_cistatic int handle_metadata(RTMPContext *rt, RTMPPacket *pkt)
2350cabdff1aSopenharmony_ci{
2351cabdff1aSopenharmony_ci    int ret, old_flv_size, type;
2352cabdff1aSopenharmony_ci    const uint8_t *next;
2353cabdff1aSopenharmony_ci    uint8_t *p;
2354cabdff1aSopenharmony_ci    uint32_t size;
2355cabdff1aSopenharmony_ci    uint32_t ts, cts, pts = 0;
2356cabdff1aSopenharmony_ci
2357cabdff1aSopenharmony_ci    old_flv_size = update_offset(rt, pkt->size);
2358cabdff1aSopenharmony_ci
2359cabdff1aSopenharmony_ci    if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
2360cabdff1aSopenharmony_ci        rt->flv_size = rt->flv_off = 0;
2361cabdff1aSopenharmony_ci        return ret;
2362cabdff1aSopenharmony_ci    }
2363cabdff1aSopenharmony_ci
2364cabdff1aSopenharmony_ci    next = pkt->data;
2365cabdff1aSopenharmony_ci    p    = rt->flv_data + old_flv_size;
2366cabdff1aSopenharmony_ci
2367cabdff1aSopenharmony_ci    /* copy data while rewriting timestamps */
2368cabdff1aSopenharmony_ci    ts = pkt->timestamp;
2369cabdff1aSopenharmony_ci
2370cabdff1aSopenharmony_ci    while (next - pkt->data < pkt->size - RTMP_HEADER) {
2371cabdff1aSopenharmony_ci        type = bytestream_get_byte(&next);
2372cabdff1aSopenharmony_ci        size = bytestream_get_be24(&next);
2373cabdff1aSopenharmony_ci        cts  = bytestream_get_be24(&next);
2374cabdff1aSopenharmony_ci        cts |= bytestream_get_byte(&next) << 24;
2375cabdff1aSopenharmony_ci        if (!pts)
2376cabdff1aSopenharmony_ci            pts = cts;
2377cabdff1aSopenharmony_ci        ts += cts - pts;
2378cabdff1aSopenharmony_ci        pts = cts;
2379cabdff1aSopenharmony_ci        if (size + 3 + 4 > pkt->data + pkt->size - next)
2380cabdff1aSopenharmony_ci            break;
2381cabdff1aSopenharmony_ci        bytestream_put_byte(&p, type);
2382cabdff1aSopenharmony_ci        bytestream_put_be24(&p, size);
2383cabdff1aSopenharmony_ci        bytestream_put_be24(&p, ts);
2384cabdff1aSopenharmony_ci        bytestream_put_byte(&p, ts >> 24);
2385cabdff1aSopenharmony_ci        memcpy(p, next, size + 3 + 4);
2386cabdff1aSopenharmony_ci        p    += size + 3;
2387cabdff1aSopenharmony_ci        bytestream_put_be32(&p, size + RTMP_HEADER);
2388cabdff1aSopenharmony_ci        next += size + 3 + 4;
2389cabdff1aSopenharmony_ci    }
2390cabdff1aSopenharmony_ci    if (p != rt->flv_data + rt->flv_size) {
2391cabdff1aSopenharmony_ci        av_log(rt, AV_LOG_WARNING, "Incomplete flv packets in "
2392cabdff1aSopenharmony_ci                                     "RTMP_PT_METADATA packet\n");
2393cabdff1aSopenharmony_ci        rt->flv_size = p - rt->flv_data;
2394cabdff1aSopenharmony_ci    }
2395cabdff1aSopenharmony_ci
2396cabdff1aSopenharmony_ci    return 0;
2397cabdff1aSopenharmony_ci}
2398cabdff1aSopenharmony_ci
2399cabdff1aSopenharmony_ci/**
2400cabdff1aSopenharmony_ci * Interact with the server by receiving and sending RTMP packets until
2401cabdff1aSopenharmony_ci * there is some significant data (media data or expected status notification).
2402cabdff1aSopenharmony_ci *
2403cabdff1aSopenharmony_ci * @param s          reading context
2404cabdff1aSopenharmony_ci * @param for_header non-zero value tells function to work until it
2405cabdff1aSopenharmony_ci * gets notification from the server that playing has been started,
2406cabdff1aSopenharmony_ci * otherwise function will work until some media data is received (or
2407cabdff1aSopenharmony_ci * an error happens)
2408cabdff1aSopenharmony_ci * @return 0 for successful operation, negative value in case of error
2409cabdff1aSopenharmony_ci */
2410cabdff1aSopenharmony_cistatic int get_packet(URLContext *s, int for_header)
2411cabdff1aSopenharmony_ci{
2412cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2413cabdff1aSopenharmony_ci    int ret;
2414cabdff1aSopenharmony_ci
2415cabdff1aSopenharmony_ci    if (rt->state == STATE_STOPPED)
2416cabdff1aSopenharmony_ci        return AVERROR_EOF;
2417cabdff1aSopenharmony_ci
2418cabdff1aSopenharmony_ci    for (;;) {
2419cabdff1aSopenharmony_ci        RTMPPacket rpkt = { 0 };
2420cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
2421cabdff1aSopenharmony_ci                                       rt->in_chunk_size, &rt->prev_pkt[0],
2422cabdff1aSopenharmony_ci                                       &rt->nb_prev_pkt[0])) <= 0) {
2423cabdff1aSopenharmony_ci            if (ret == 0) {
2424cabdff1aSopenharmony_ci                return AVERROR(EAGAIN);
2425cabdff1aSopenharmony_ci            } else {
2426cabdff1aSopenharmony_ci                return AVERROR(EIO);
2427cabdff1aSopenharmony_ci            }
2428cabdff1aSopenharmony_ci        }
2429cabdff1aSopenharmony_ci
2430cabdff1aSopenharmony_ci        // Track timestamp for later use
2431cabdff1aSopenharmony_ci        rt->last_timestamp = rpkt.timestamp;
2432cabdff1aSopenharmony_ci
2433cabdff1aSopenharmony_ci        rt->bytes_read += ret;
2434cabdff1aSopenharmony_ci        if (rt->bytes_read - rt->last_bytes_read > rt->receive_report_size) {
2435cabdff1aSopenharmony_ci            av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
2436cabdff1aSopenharmony_ci            if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0) {
2437cabdff1aSopenharmony_ci                ff_rtmp_packet_destroy(&rpkt);
2438cabdff1aSopenharmony_ci                return ret;
2439cabdff1aSopenharmony_ci            }
2440cabdff1aSopenharmony_ci            rt->last_bytes_read = rt->bytes_read;
2441cabdff1aSopenharmony_ci        }
2442cabdff1aSopenharmony_ci
2443cabdff1aSopenharmony_ci        ret = rtmp_parse_result(s, rt, &rpkt);
2444cabdff1aSopenharmony_ci
2445cabdff1aSopenharmony_ci        // At this point we must check if we are in the seek state and continue
2446cabdff1aSopenharmony_ci        // with the next packet. handle_invoke will get us out of this state
2447cabdff1aSopenharmony_ci        // when the right message is encountered
2448cabdff1aSopenharmony_ci        if (rt->state == STATE_SEEKING) {
2449cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2450cabdff1aSopenharmony_ci            // We continue, let the natural flow of things happen:
2451cabdff1aSopenharmony_ci            // AVERROR(EAGAIN) or handle_invoke gets us out of here
2452cabdff1aSopenharmony_ci            continue;
2453cabdff1aSopenharmony_ci        }
2454cabdff1aSopenharmony_ci
2455cabdff1aSopenharmony_ci        if (ret < 0) {//serious error in current packet
2456cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2457cabdff1aSopenharmony_ci            return ret;
2458cabdff1aSopenharmony_ci        }
2459cabdff1aSopenharmony_ci        if (rt->do_reconnect && for_header) {
2460cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2461cabdff1aSopenharmony_ci            return 0;
2462cabdff1aSopenharmony_ci        }
2463cabdff1aSopenharmony_ci        if (rt->state == STATE_STOPPED) {
2464cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2465cabdff1aSopenharmony_ci            return AVERROR_EOF;
2466cabdff1aSopenharmony_ci        }
2467cabdff1aSopenharmony_ci        if (for_header && (rt->state == STATE_PLAYING    ||
2468cabdff1aSopenharmony_ci                           rt->state == STATE_PUBLISHING ||
2469cabdff1aSopenharmony_ci                           rt->state == STATE_SENDING    ||
2470cabdff1aSopenharmony_ci                           rt->state == STATE_RECEIVING)) {
2471cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2472cabdff1aSopenharmony_ci            return 0;
2473cabdff1aSopenharmony_ci        }
2474cabdff1aSopenharmony_ci        if (!rpkt.size || !rt->is_input) {
2475cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2476cabdff1aSopenharmony_ci            continue;
2477cabdff1aSopenharmony_ci        }
2478cabdff1aSopenharmony_ci        if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO) {
2479cabdff1aSopenharmony_ci            ret = append_flv_data(rt, &rpkt, 0);
2480cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2481cabdff1aSopenharmony_ci            return ret;
2482cabdff1aSopenharmony_ci        } else if (rpkt.type == RTMP_PT_NOTIFY) {
2483cabdff1aSopenharmony_ci            ret = handle_notify(s, &rpkt);
2484cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2485cabdff1aSopenharmony_ci            return ret;
2486cabdff1aSopenharmony_ci        } else if (rpkt.type == RTMP_PT_METADATA) {
2487cabdff1aSopenharmony_ci            ret = handle_metadata(rt, &rpkt);
2488cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rpkt);
2489cabdff1aSopenharmony_ci            return ret;
2490cabdff1aSopenharmony_ci        }
2491cabdff1aSopenharmony_ci        ff_rtmp_packet_destroy(&rpkt);
2492cabdff1aSopenharmony_ci    }
2493cabdff1aSopenharmony_ci}
2494cabdff1aSopenharmony_ci
2495cabdff1aSopenharmony_cistatic int rtmp_close(URLContext *h)
2496cabdff1aSopenharmony_ci{
2497cabdff1aSopenharmony_ci    RTMPContext *rt = h->priv_data;
2498cabdff1aSopenharmony_ci    int ret = 0, i, j;
2499cabdff1aSopenharmony_ci
2500cabdff1aSopenharmony_ci    if (!rt->is_input) {
2501cabdff1aSopenharmony_ci        rt->flv_data = NULL;
2502cabdff1aSopenharmony_ci        if (rt->out_pkt.size)
2503cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rt->out_pkt);
2504cabdff1aSopenharmony_ci        if (rt->state > STATE_FCPUBLISH)
2505cabdff1aSopenharmony_ci            ret = gen_fcunpublish_stream(h, rt);
2506cabdff1aSopenharmony_ci    }
2507cabdff1aSopenharmony_ci    if (rt->state > STATE_HANDSHAKED)
2508cabdff1aSopenharmony_ci        ret = gen_delete_stream(h, rt);
2509cabdff1aSopenharmony_ci    for (i = 0; i < 2; i++) {
2510cabdff1aSopenharmony_ci        for (j = 0; j < rt->nb_prev_pkt[i]; j++)
2511cabdff1aSopenharmony_ci            ff_rtmp_packet_destroy(&rt->prev_pkt[i][j]);
2512cabdff1aSopenharmony_ci        av_freep(&rt->prev_pkt[i]);
2513cabdff1aSopenharmony_ci    }
2514cabdff1aSopenharmony_ci
2515cabdff1aSopenharmony_ci    free_tracked_methods(rt);
2516cabdff1aSopenharmony_ci    av_freep(&rt->flv_data);
2517cabdff1aSopenharmony_ci    ffurl_closep(&rt->stream);
2518cabdff1aSopenharmony_ci    return ret;
2519cabdff1aSopenharmony_ci}
2520cabdff1aSopenharmony_ci
2521cabdff1aSopenharmony_ci/**
2522cabdff1aSopenharmony_ci * Insert a fake onMetadata packet into the FLV stream to notify the FLV
2523cabdff1aSopenharmony_ci * demuxer about the duration of the stream.
2524cabdff1aSopenharmony_ci *
2525cabdff1aSopenharmony_ci * This should only be done if there was no real onMetadata packet sent by the
2526cabdff1aSopenharmony_ci * server at the start of the stream and if we were able to retrieve a valid
2527cabdff1aSopenharmony_ci * duration via a getStreamLength call.
2528cabdff1aSopenharmony_ci *
2529cabdff1aSopenharmony_ci * @return 0 for successful operation, negative value in case of error
2530cabdff1aSopenharmony_ci */
2531cabdff1aSopenharmony_cistatic int inject_fake_duration_metadata(RTMPContext *rt)
2532cabdff1aSopenharmony_ci{
2533cabdff1aSopenharmony_ci    // We need to insert the metadata packet directly after the FLV
2534cabdff1aSopenharmony_ci    // header, i.e. we need to move all other already read data by the
2535cabdff1aSopenharmony_ci    // size of our fake metadata packet.
2536cabdff1aSopenharmony_ci
2537cabdff1aSopenharmony_ci    uint8_t* p;
2538cabdff1aSopenharmony_ci    // Keep old flv_data pointer
2539cabdff1aSopenharmony_ci    uint8_t* old_flv_data = rt->flv_data;
2540cabdff1aSopenharmony_ci    // Allocate a new flv_data pointer with enough space for the additional package
2541cabdff1aSopenharmony_ci    if (!(rt->flv_data = av_malloc(rt->flv_size + 55))) {
2542cabdff1aSopenharmony_ci        rt->flv_data = old_flv_data;
2543cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
2544cabdff1aSopenharmony_ci    }
2545cabdff1aSopenharmony_ci
2546cabdff1aSopenharmony_ci    // Copy FLV header
2547cabdff1aSopenharmony_ci    memcpy(rt->flv_data, old_flv_data, 13);
2548cabdff1aSopenharmony_ci    // Copy remaining packets
2549cabdff1aSopenharmony_ci    memcpy(rt->flv_data + 13 + 55, old_flv_data + 13, rt->flv_size - 13);
2550cabdff1aSopenharmony_ci    // Increase the size by the injected packet
2551cabdff1aSopenharmony_ci    rt->flv_size += 55;
2552cabdff1aSopenharmony_ci    // Delete the old FLV data
2553cabdff1aSopenharmony_ci    av_freep(&old_flv_data);
2554cabdff1aSopenharmony_ci
2555cabdff1aSopenharmony_ci    p = rt->flv_data + 13;
2556cabdff1aSopenharmony_ci    bytestream_put_byte(&p, FLV_TAG_TYPE_META);
2557cabdff1aSopenharmony_ci    bytestream_put_be24(&p, 40); // size of data part (sum of all parts below)
2558cabdff1aSopenharmony_ci    bytestream_put_be24(&p, 0);  // timestamp
2559cabdff1aSopenharmony_ci    bytestream_put_be32(&p, 0);  // reserved
2560cabdff1aSopenharmony_ci
2561cabdff1aSopenharmony_ci    // first event name as a string
2562cabdff1aSopenharmony_ci    bytestream_put_byte(&p, AMF_DATA_TYPE_STRING);
2563cabdff1aSopenharmony_ci    // "onMetaData" as AMF string
2564cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 10);
2565cabdff1aSopenharmony_ci    bytestream_put_buffer(&p, "onMetaData", 10);
2566cabdff1aSopenharmony_ci
2567cabdff1aSopenharmony_ci    // mixed array (hash) with size and string/type/data tuples
2568cabdff1aSopenharmony_ci    bytestream_put_byte(&p, AMF_DATA_TYPE_MIXEDARRAY);
2569cabdff1aSopenharmony_ci    bytestream_put_be32(&p, 1); // metadata_count
2570cabdff1aSopenharmony_ci
2571cabdff1aSopenharmony_ci    // "duration" as AMF string
2572cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 8);
2573cabdff1aSopenharmony_ci    bytestream_put_buffer(&p, "duration", 8);
2574cabdff1aSopenharmony_ci    bytestream_put_byte(&p, AMF_DATA_TYPE_NUMBER);
2575cabdff1aSopenharmony_ci    bytestream_put_be64(&p, av_double2int(rt->duration));
2576cabdff1aSopenharmony_ci
2577cabdff1aSopenharmony_ci    // Finalise object
2578cabdff1aSopenharmony_ci    bytestream_put_be16(&p, 0); // Empty string
2579cabdff1aSopenharmony_ci    bytestream_put_byte(&p, AMF_END_OF_OBJECT);
2580cabdff1aSopenharmony_ci    bytestream_put_be32(&p, 40 + RTMP_HEADER); // size of data part (sum of all parts above)
2581cabdff1aSopenharmony_ci
2582cabdff1aSopenharmony_ci    return 0;
2583cabdff1aSopenharmony_ci}
2584cabdff1aSopenharmony_ci
2585cabdff1aSopenharmony_ci/**
2586cabdff1aSopenharmony_ci * Open RTMP connection and verify that the stream can be played.
2587cabdff1aSopenharmony_ci *
2588cabdff1aSopenharmony_ci * URL syntax: rtmp://server[:port][/app][/playpath]
2589cabdff1aSopenharmony_ci *             where 'app' is first one or two directories in the path
2590cabdff1aSopenharmony_ci *             (e.g. /ondemand/, /flash/live/, etc.)
2591cabdff1aSopenharmony_ci *             and 'playpath' is a file name (the rest of the path,
2592cabdff1aSopenharmony_ci *             may be prefixed with "mp4:")
2593cabdff1aSopenharmony_ci */
2594cabdff1aSopenharmony_cistatic int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts)
2595cabdff1aSopenharmony_ci{
2596cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2597cabdff1aSopenharmony_ci    char proto[8], hostname[256], path[1024], auth[100], *fname;
2598cabdff1aSopenharmony_ci    char *old_app, *qmark, *n, fname_buffer[1024];
2599cabdff1aSopenharmony_ci    uint8_t buf[2048];
2600cabdff1aSopenharmony_ci    int port;
2601cabdff1aSopenharmony_ci    int ret;
2602cabdff1aSopenharmony_ci
2603cabdff1aSopenharmony_ci    if (rt->listen_timeout > 0)
2604cabdff1aSopenharmony_ci        rt->listen = 1;
2605cabdff1aSopenharmony_ci
2606cabdff1aSopenharmony_ci    rt->is_input = !(flags & AVIO_FLAG_WRITE);
2607cabdff1aSopenharmony_ci
2608cabdff1aSopenharmony_ci    av_url_split(proto, sizeof(proto), auth, sizeof(auth),
2609cabdff1aSopenharmony_ci                 hostname, sizeof(hostname), &port,
2610cabdff1aSopenharmony_ci                 path, sizeof(path), s->filename);
2611cabdff1aSopenharmony_ci
2612cabdff1aSopenharmony_ci    n = strchr(path, ' ');
2613cabdff1aSopenharmony_ci    if (n) {
2614cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING,
2615cabdff1aSopenharmony_ci               "Detected librtmp style URL parameters, these aren't supported "
2616cabdff1aSopenharmony_ci               "by the libavformat internal RTMP handler currently enabled. "
2617cabdff1aSopenharmony_ci               "See the documentation for the correct way to pass parameters.\n");
2618cabdff1aSopenharmony_ci        *n = '\0'; // Trim not supported part
2619cabdff1aSopenharmony_ci    }
2620cabdff1aSopenharmony_ci
2621cabdff1aSopenharmony_ci    if (auth[0]) {
2622cabdff1aSopenharmony_ci        char *ptr = strchr(auth, ':');
2623cabdff1aSopenharmony_ci        if (ptr) {
2624cabdff1aSopenharmony_ci            *ptr = '\0';
2625cabdff1aSopenharmony_ci            av_strlcpy(rt->username, auth, sizeof(rt->username));
2626cabdff1aSopenharmony_ci            av_strlcpy(rt->password, ptr + 1, sizeof(rt->password));
2627cabdff1aSopenharmony_ci        }
2628cabdff1aSopenharmony_ci    }
2629cabdff1aSopenharmony_ci
2630cabdff1aSopenharmony_ci    if (rt->listen && strcmp(proto, "rtmp")) {
2631cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "rtmp_listen not available for %s\n",
2632cabdff1aSopenharmony_ci               proto);
2633cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
2634cabdff1aSopenharmony_ci    }
2635cabdff1aSopenharmony_ci    if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
2636cabdff1aSopenharmony_ci        if (!strcmp(proto, "rtmpts"))
2637cabdff1aSopenharmony_ci            av_dict_set(opts, "ffrtmphttp_tls", "1", 1);
2638cabdff1aSopenharmony_ci
2639cabdff1aSopenharmony_ci        /* open the http tunneling connection */
2640cabdff1aSopenharmony_ci        ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
2641cabdff1aSopenharmony_ci    } else if (!strcmp(proto, "rtmps")) {
2642cabdff1aSopenharmony_ci        /* open the tls connection */
2643cabdff1aSopenharmony_ci        if (port < 0)
2644cabdff1aSopenharmony_ci            port = RTMPS_DEFAULT_PORT;
2645cabdff1aSopenharmony_ci        ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
2646cabdff1aSopenharmony_ci    } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
2647cabdff1aSopenharmony_ci        if (!strcmp(proto, "rtmpte"))
2648cabdff1aSopenharmony_ci            av_dict_set(opts, "ffrtmpcrypt_tunneling", "1", 1);
2649cabdff1aSopenharmony_ci
2650cabdff1aSopenharmony_ci        /* open the encrypted connection */
2651cabdff1aSopenharmony_ci        ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
2652cabdff1aSopenharmony_ci        rt->encrypted = 1;
2653cabdff1aSopenharmony_ci    } else {
2654cabdff1aSopenharmony_ci        /* open the tcp connection */
2655cabdff1aSopenharmony_ci        if (port < 0)
2656cabdff1aSopenharmony_ci            port = RTMP_DEFAULT_PORT;
2657cabdff1aSopenharmony_ci        if (rt->listen)
2658cabdff1aSopenharmony_ci            ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port,
2659cabdff1aSopenharmony_ci                        "?listen&listen_timeout=%d&tcp_nodelay=%d",
2660cabdff1aSopenharmony_ci                        rt->listen_timeout * 1000, rt->tcp_nodelay);
2661cabdff1aSopenharmony_ci        else
2662cabdff1aSopenharmony_ci            ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, "?tcp_nodelay=%d", rt->tcp_nodelay);
2663cabdff1aSopenharmony_ci    }
2664cabdff1aSopenharmony_ci
2665cabdff1aSopenharmony_cireconnect:
2666cabdff1aSopenharmony_ci    if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
2667cabdff1aSopenharmony_ci                                    &s->interrupt_callback, opts,
2668cabdff1aSopenharmony_ci                                    s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
2669cabdff1aSopenharmony_ci        av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
2670cabdff1aSopenharmony_ci        goto fail;
2671cabdff1aSopenharmony_ci    }
2672cabdff1aSopenharmony_ci
2673cabdff1aSopenharmony_ci    if (rt->swfverify) {
2674cabdff1aSopenharmony_ci        if ((ret = rtmp_calc_swfhash(s)) < 0)
2675cabdff1aSopenharmony_ci            goto fail;
2676cabdff1aSopenharmony_ci    }
2677cabdff1aSopenharmony_ci
2678cabdff1aSopenharmony_ci    rt->state = STATE_START;
2679cabdff1aSopenharmony_ci    if (!rt->listen && (ret = rtmp_handshake(s, rt)) < 0)
2680cabdff1aSopenharmony_ci        goto fail;
2681cabdff1aSopenharmony_ci    if (rt->listen && (ret = rtmp_server_handshake(s, rt)) < 0)
2682cabdff1aSopenharmony_ci        goto fail;
2683cabdff1aSopenharmony_ci
2684cabdff1aSopenharmony_ci    rt->out_chunk_size = 128;
2685cabdff1aSopenharmony_ci    rt->in_chunk_size  = 128; // Probably overwritten later
2686cabdff1aSopenharmony_ci    rt->state = STATE_HANDSHAKED;
2687cabdff1aSopenharmony_ci
2688cabdff1aSopenharmony_ci    // Keep the application name when it has been defined by the user.
2689cabdff1aSopenharmony_ci    old_app = rt->app;
2690cabdff1aSopenharmony_ci
2691cabdff1aSopenharmony_ci    rt->app = av_malloc(APP_MAX_LENGTH);
2692cabdff1aSopenharmony_ci    if (!rt->app) {
2693cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
2694cabdff1aSopenharmony_ci        goto fail;
2695cabdff1aSopenharmony_ci    }
2696cabdff1aSopenharmony_ci
2697cabdff1aSopenharmony_ci    //extract "app" part from path
2698cabdff1aSopenharmony_ci    qmark = strchr(path, '?');
2699cabdff1aSopenharmony_ci    if (qmark && strstr(qmark, "slist=")) {
2700cabdff1aSopenharmony_ci        char* amp;
2701cabdff1aSopenharmony_ci        // After slist we have the playpath, the full path is used as app
2702cabdff1aSopenharmony_ci        av_strlcpy(rt->app, path + 1, APP_MAX_LENGTH);
2703cabdff1aSopenharmony_ci        fname = strstr(path, "slist=") + 6;
2704cabdff1aSopenharmony_ci        // Strip any further query parameters from fname
2705cabdff1aSopenharmony_ci        amp = strchr(fname, '&');
2706cabdff1aSopenharmony_ci        if (amp) {
2707cabdff1aSopenharmony_ci            av_strlcpy(fname_buffer, fname, FFMIN(amp - fname + 1,
2708cabdff1aSopenharmony_ci                                                  sizeof(fname_buffer)));
2709cabdff1aSopenharmony_ci            fname = fname_buffer;
2710cabdff1aSopenharmony_ci        }
2711cabdff1aSopenharmony_ci    } else if (!strncmp(path, "/ondemand/", 10)) {
2712cabdff1aSopenharmony_ci        fname = path + 10;
2713cabdff1aSopenharmony_ci        memcpy(rt->app, "ondemand", 9);
2714cabdff1aSopenharmony_ci    } else {
2715cabdff1aSopenharmony_ci        char *next = *path ? path + 1 : path;
2716cabdff1aSopenharmony_ci        char *p = strchr(next, '/');
2717cabdff1aSopenharmony_ci        if (!p) {
2718cabdff1aSopenharmony_ci            if (old_app) {
2719cabdff1aSopenharmony_ci                // If name of application has been defined by the user, assume that
2720cabdff1aSopenharmony_ci                // playpath is provided in the URL
2721cabdff1aSopenharmony_ci                fname = next;
2722cabdff1aSopenharmony_ci            } else {
2723cabdff1aSopenharmony_ci                fname = NULL;
2724cabdff1aSopenharmony_ci                av_strlcpy(rt->app, next, APP_MAX_LENGTH);
2725cabdff1aSopenharmony_ci            }
2726cabdff1aSopenharmony_ci        } else {
2727cabdff1aSopenharmony_ci            // make sure we do not mismatch a playpath for an application instance
2728cabdff1aSopenharmony_ci            char *c = strchr(p + 1, ':');
2729cabdff1aSopenharmony_ci            fname = strchr(p + 1, '/');
2730cabdff1aSopenharmony_ci            if (!fname || (c && c < fname)) {
2731cabdff1aSopenharmony_ci                fname = p + 1;
2732cabdff1aSopenharmony_ci                av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH));
2733cabdff1aSopenharmony_ci            } else {
2734cabdff1aSopenharmony_ci                fname++;
2735cabdff1aSopenharmony_ci                av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH));
2736cabdff1aSopenharmony_ci            }
2737cabdff1aSopenharmony_ci        }
2738cabdff1aSopenharmony_ci    }
2739cabdff1aSopenharmony_ci
2740cabdff1aSopenharmony_ci    if (old_app) {
2741cabdff1aSopenharmony_ci        // The name of application has been defined by the user, override it.
2742cabdff1aSopenharmony_ci        if (strlen(old_app) >= APP_MAX_LENGTH) {
2743cabdff1aSopenharmony_ci            ret = AVERROR(EINVAL);
2744cabdff1aSopenharmony_ci            goto fail;
2745cabdff1aSopenharmony_ci        }
2746cabdff1aSopenharmony_ci        av_free(rt->app);
2747cabdff1aSopenharmony_ci        rt->app = old_app;
2748cabdff1aSopenharmony_ci    }
2749cabdff1aSopenharmony_ci
2750cabdff1aSopenharmony_ci    if (!rt->playpath) {
2751cabdff1aSopenharmony_ci        int max_len = 1;
2752cabdff1aSopenharmony_ci        if (fname)
2753cabdff1aSopenharmony_ci            max_len = strlen(fname) + 5; // add prefix "mp4:"
2754cabdff1aSopenharmony_ci        rt->playpath = av_malloc(max_len);
2755cabdff1aSopenharmony_ci        if (!rt->playpath) {
2756cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
2757cabdff1aSopenharmony_ci            goto fail;
2758cabdff1aSopenharmony_ci        }
2759cabdff1aSopenharmony_ci
2760cabdff1aSopenharmony_ci        if (fname) {
2761cabdff1aSopenharmony_ci            int len = strlen(fname);
2762cabdff1aSopenharmony_ci            if (!strchr(fname, ':') && len >= 4 &&
2763cabdff1aSopenharmony_ci                (!strcmp(fname + len - 4, ".f4v") ||
2764cabdff1aSopenharmony_ci                 !strcmp(fname + len - 4, ".mp4"))) {
2765cabdff1aSopenharmony_ci                memcpy(rt->playpath, "mp4:", 5);
2766cabdff1aSopenharmony_ci            } else {
2767cabdff1aSopenharmony_ci                if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
2768cabdff1aSopenharmony_ci                    fname[len - 4] = '\0';
2769cabdff1aSopenharmony_ci                rt->playpath[0] = 0;
2770cabdff1aSopenharmony_ci            }
2771cabdff1aSopenharmony_ci            av_strlcat(rt->playpath, fname, max_len);
2772cabdff1aSopenharmony_ci        } else {
2773cabdff1aSopenharmony_ci            rt->playpath[0] = '\0';
2774cabdff1aSopenharmony_ci        }
2775cabdff1aSopenharmony_ci    }
2776cabdff1aSopenharmony_ci
2777cabdff1aSopenharmony_ci    if (!rt->tcurl) {
2778cabdff1aSopenharmony_ci        rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
2779cabdff1aSopenharmony_ci        if (!rt->tcurl) {
2780cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
2781cabdff1aSopenharmony_ci            goto fail;
2782cabdff1aSopenharmony_ci        }
2783cabdff1aSopenharmony_ci        ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
2784cabdff1aSopenharmony_ci                    port, "/%s", rt->app);
2785cabdff1aSopenharmony_ci    }
2786cabdff1aSopenharmony_ci
2787cabdff1aSopenharmony_ci    if (!rt->flashver) {
2788cabdff1aSopenharmony_ci        rt->flashver = av_malloc(FLASHVER_MAX_LENGTH);
2789cabdff1aSopenharmony_ci        if (!rt->flashver) {
2790cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
2791cabdff1aSopenharmony_ci            goto fail;
2792cabdff1aSopenharmony_ci        }
2793cabdff1aSopenharmony_ci        if (rt->is_input) {
2794cabdff1aSopenharmony_ci            snprintf(rt->flashver, FLASHVER_MAX_LENGTH, "%s %d,%d,%d,%d",
2795cabdff1aSopenharmony_ci                    RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, RTMP_CLIENT_VER2,
2796cabdff1aSopenharmony_ci                    RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
2797cabdff1aSopenharmony_ci        } else {
2798cabdff1aSopenharmony_ci            snprintf(rt->flashver, FLASHVER_MAX_LENGTH,
2799cabdff1aSopenharmony_ci                    "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
2800cabdff1aSopenharmony_ci        }
2801cabdff1aSopenharmony_ci    }
2802cabdff1aSopenharmony_ci
2803cabdff1aSopenharmony_ci    rt->receive_report_size = 1048576;
2804cabdff1aSopenharmony_ci    rt->bytes_read = 0;
2805cabdff1aSopenharmony_ci    rt->has_audio = 0;
2806cabdff1aSopenharmony_ci    rt->has_video = 0;
2807cabdff1aSopenharmony_ci    rt->received_metadata = 0;
2808cabdff1aSopenharmony_ci    rt->last_bytes_read = 0;
2809cabdff1aSopenharmony_ci    rt->max_sent_unacked = 2500000;
2810cabdff1aSopenharmony_ci    rt->duration = 0;
2811cabdff1aSopenharmony_ci
2812cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
2813cabdff1aSopenharmony_ci           proto, path, rt->app, rt->playpath);
2814cabdff1aSopenharmony_ci    if (!rt->listen) {
2815cabdff1aSopenharmony_ci        if ((ret = gen_connect(s, rt)) < 0)
2816cabdff1aSopenharmony_ci            goto fail;
2817cabdff1aSopenharmony_ci    } else {
2818cabdff1aSopenharmony_ci        if ((ret = read_connect(s, s->priv_data)) < 0)
2819cabdff1aSopenharmony_ci            goto fail;
2820cabdff1aSopenharmony_ci    }
2821cabdff1aSopenharmony_ci
2822cabdff1aSopenharmony_ci    do {
2823cabdff1aSopenharmony_ci        ret = get_packet(s, 1);
2824cabdff1aSopenharmony_ci    } while (ret == AVERROR(EAGAIN));
2825cabdff1aSopenharmony_ci    if (ret < 0)
2826cabdff1aSopenharmony_ci        goto fail;
2827cabdff1aSopenharmony_ci
2828cabdff1aSopenharmony_ci    if (rt->do_reconnect) {
2829cabdff1aSopenharmony_ci        int i;
2830cabdff1aSopenharmony_ci        ffurl_closep(&rt->stream);
2831cabdff1aSopenharmony_ci        rt->do_reconnect = 0;
2832cabdff1aSopenharmony_ci        rt->nb_invokes   = 0;
2833cabdff1aSopenharmony_ci        for (i = 0; i < 2; i++)
2834cabdff1aSopenharmony_ci            memset(rt->prev_pkt[i], 0,
2835cabdff1aSopenharmony_ci                   sizeof(**rt->prev_pkt) * rt->nb_prev_pkt[i]);
2836cabdff1aSopenharmony_ci        free_tracked_methods(rt);
2837cabdff1aSopenharmony_ci        goto reconnect;
2838cabdff1aSopenharmony_ci    }
2839cabdff1aSopenharmony_ci
2840cabdff1aSopenharmony_ci    if (rt->is_input) {
2841cabdff1aSopenharmony_ci        // generate FLV header for demuxer
2842cabdff1aSopenharmony_ci        rt->flv_size = 13;
2843cabdff1aSopenharmony_ci        if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
2844cabdff1aSopenharmony_ci            goto fail;
2845cabdff1aSopenharmony_ci        rt->flv_off  = 0;
2846cabdff1aSopenharmony_ci        memcpy(rt->flv_data, "FLV\1\0\0\0\0\011\0\0\0\0", rt->flv_size);
2847cabdff1aSopenharmony_ci
2848cabdff1aSopenharmony_ci        // Read packets until we reach the first A/V packet or read metadata.
2849cabdff1aSopenharmony_ci        // If there was a metadata package in front of the A/V packets, we can
2850cabdff1aSopenharmony_ci        // build the FLV header from this. If we do not receive any metadata,
2851cabdff1aSopenharmony_ci        // the FLV decoder will allocate the needed streams when their first
2852cabdff1aSopenharmony_ci        // audio or video packet arrives.
2853cabdff1aSopenharmony_ci        while (!rt->has_audio && !rt->has_video && !rt->received_metadata) {
2854cabdff1aSopenharmony_ci            if ((ret = get_packet(s, 0)) < 0)
2855cabdff1aSopenharmony_ci               goto fail;
2856cabdff1aSopenharmony_ci        }
2857cabdff1aSopenharmony_ci
2858cabdff1aSopenharmony_ci        // Either after we have read the metadata or (if there is none) the
2859cabdff1aSopenharmony_ci        // first packet of an A/V stream, we have a better knowledge about the
2860cabdff1aSopenharmony_ci        // streams, so set the FLV header accordingly.
2861cabdff1aSopenharmony_ci        if (rt->has_audio) {
2862cabdff1aSopenharmony_ci            rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
2863cabdff1aSopenharmony_ci        }
2864cabdff1aSopenharmony_ci        if (rt->has_video) {
2865cabdff1aSopenharmony_ci            rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
2866cabdff1aSopenharmony_ci        }
2867cabdff1aSopenharmony_ci
2868cabdff1aSopenharmony_ci        // If we received the first packet of an A/V stream and no metadata but
2869cabdff1aSopenharmony_ci        // the server returned a valid duration, create a fake metadata packet
2870cabdff1aSopenharmony_ci        // to inform the FLV decoder about the duration.
2871cabdff1aSopenharmony_ci        if (!rt->received_metadata && rt->duration > 0) {
2872cabdff1aSopenharmony_ci            if ((ret = inject_fake_duration_metadata(rt)) < 0)
2873cabdff1aSopenharmony_ci                goto fail;
2874cabdff1aSopenharmony_ci        }
2875cabdff1aSopenharmony_ci    } else {
2876cabdff1aSopenharmony_ci        rt->flv_size = 0;
2877cabdff1aSopenharmony_ci        rt->flv_data = NULL;
2878cabdff1aSopenharmony_ci        rt->flv_off  = 0;
2879cabdff1aSopenharmony_ci        rt->skip_bytes = 13;
2880cabdff1aSopenharmony_ci    }
2881cabdff1aSopenharmony_ci
2882cabdff1aSopenharmony_ci    s->max_packet_size = rt->stream->max_packet_size;
2883cabdff1aSopenharmony_ci    s->is_streamed     = 1;
2884cabdff1aSopenharmony_ci    return 0;
2885cabdff1aSopenharmony_ci
2886cabdff1aSopenharmony_cifail:
2887cabdff1aSopenharmony_ci    av_freep(&rt->playpath);
2888cabdff1aSopenharmony_ci    av_freep(&rt->tcurl);
2889cabdff1aSopenharmony_ci    av_freep(&rt->flashver);
2890cabdff1aSopenharmony_ci    av_dict_free(opts);
2891cabdff1aSopenharmony_ci    rtmp_close(s);
2892cabdff1aSopenharmony_ci    return ret;
2893cabdff1aSopenharmony_ci}
2894cabdff1aSopenharmony_ci
2895cabdff1aSopenharmony_cistatic int rtmp_read(URLContext *s, uint8_t *buf, int size)
2896cabdff1aSopenharmony_ci{
2897cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2898cabdff1aSopenharmony_ci    int orig_size = size;
2899cabdff1aSopenharmony_ci    int ret;
2900cabdff1aSopenharmony_ci
2901cabdff1aSopenharmony_ci    while (size > 0) {
2902cabdff1aSopenharmony_ci        int data_left = rt->flv_size - rt->flv_off;
2903cabdff1aSopenharmony_ci
2904cabdff1aSopenharmony_ci        if (data_left >= size) {
2905cabdff1aSopenharmony_ci            memcpy(buf, rt->flv_data + rt->flv_off, size);
2906cabdff1aSopenharmony_ci            rt->flv_off += size;
2907cabdff1aSopenharmony_ci            return orig_size;
2908cabdff1aSopenharmony_ci        }
2909cabdff1aSopenharmony_ci        if (data_left > 0) {
2910cabdff1aSopenharmony_ci            memcpy(buf, rt->flv_data + rt->flv_off, data_left);
2911cabdff1aSopenharmony_ci            buf  += data_left;
2912cabdff1aSopenharmony_ci            size -= data_left;
2913cabdff1aSopenharmony_ci            rt->flv_off = rt->flv_size;
2914cabdff1aSopenharmony_ci            return data_left;
2915cabdff1aSopenharmony_ci        }
2916cabdff1aSopenharmony_ci        if ((ret = get_packet(s, 0)) < 0)
2917cabdff1aSopenharmony_ci           return ret;
2918cabdff1aSopenharmony_ci    }
2919cabdff1aSopenharmony_ci    return orig_size;
2920cabdff1aSopenharmony_ci}
2921cabdff1aSopenharmony_ci
2922cabdff1aSopenharmony_cistatic int64_t rtmp_seek(URLContext *s, int stream_index, int64_t timestamp,
2923cabdff1aSopenharmony_ci                         int flags)
2924cabdff1aSopenharmony_ci{
2925cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2926cabdff1aSopenharmony_ci    int ret;
2927cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG,
2928cabdff1aSopenharmony_ci           "Seek on stream index %d at timestamp %"PRId64" with flags %08x\n",
2929cabdff1aSopenharmony_ci           stream_index, timestamp, flags);
2930cabdff1aSopenharmony_ci    if ((ret = gen_seek(s, rt, timestamp)) < 0) {
2931cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
2932cabdff1aSopenharmony_ci               "Unable to send seek command on stream index %d at timestamp "
2933cabdff1aSopenharmony_ci               "%"PRId64" with flags %08x\n",
2934cabdff1aSopenharmony_ci               stream_index, timestamp, flags);
2935cabdff1aSopenharmony_ci        return ret;
2936cabdff1aSopenharmony_ci    }
2937cabdff1aSopenharmony_ci    rt->flv_off = rt->flv_size;
2938cabdff1aSopenharmony_ci    rt->state = STATE_SEEKING;
2939cabdff1aSopenharmony_ci    return timestamp;
2940cabdff1aSopenharmony_ci}
2941cabdff1aSopenharmony_ci
2942cabdff1aSopenharmony_cistatic int rtmp_pause(URLContext *s, int pause)
2943cabdff1aSopenharmony_ci{
2944cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2945cabdff1aSopenharmony_ci    int ret;
2946cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, "Pause at timestamp %d\n",
2947cabdff1aSopenharmony_ci           rt->last_timestamp);
2948cabdff1aSopenharmony_ci    if ((ret = gen_pause(s, rt, pause, rt->last_timestamp)) < 0) {
2949cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Unable to send pause command at timestamp %d\n",
2950cabdff1aSopenharmony_ci               rt->last_timestamp);
2951cabdff1aSopenharmony_ci        return ret;
2952cabdff1aSopenharmony_ci    }
2953cabdff1aSopenharmony_ci    return 0;
2954cabdff1aSopenharmony_ci}
2955cabdff1aSopenharmony_ci
2956cabdff1aSopenharmony_cistatic int rtmp_write(URLContext *s, const uint8_t *buf, int size)
2957cabdff1aSopenharmony_ci{
2958cabdff1aSopenharmony_ci    RTMPContext *rt = s->priv_data;
2959cabdff1aSopenharmony_ci    int size_temp = size;
2960cabdff1aSopenharmony_ci    int pktsize, pkttype, copy;
2961cabdff1aSopenharmony_ci    uint32_t ts;
2962cabdff1aSopenharmony_ci    const uint8_t *buf_temp = buf;
2963cabdff1aSopenharmony_ci    uint8_t c;
2964cabdff1aSopenharmony_ci    int ret;
2965cabdff1aSopenharmony_ci
2966cabdff1aSopenharmony_ci    do {
2967cabdff1aSopenharmony_ci        if (rt->skip_bytes) {
2968cabdff1aSopenharmony_ci            int skip = FFMIN(rt->skip_bytes, size_temp);
2969cabdff1aSopenharmony_ci            buf_temp       += skip;
2970cabdff1aSopenharmony_ci            size_temp      -= skip;
2971cabdff1aSopenharmony_ci            rt->skip_bytes -= skip;
2972cabdff1aSopenharmony_ci            continue;
2973cabdff1aSopenharmony_ci        }
2974cabdff1aSopenharmony_ci
2975cabdff1aSopenharmony_ci        if (rt->flv_header_bytes < RTMP_HEADER) {
2976cabdff1aSopenharmony_ci            const uint8_t *header = rt->flv_header;
2977cabdff1aSopenharmony_ci            int channel = RTMP_AUDIO_CHANNEL;
2978cabdff1aSopenharmony_ci
2979cabdff1aSopenharmony_ci            copy = FFMIN(RTMP_HEADER - rt->flv_header_bytes, size_temp);
2980cabdff1aSopenharmony_ci            bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
2981cabdff1aSopenharmony_ci            rt->flv_header_bytes += copy;
2982cabdff1aSopenharmony_ci            size_temp            -= copy;
2983cabdff1aSopenharmony_ci            if (rt->flv_header_bytes < RTMP_HEADER)
2984cabdff1aSopenharmony_ci                break;
2985cabdff1aSopenharmony_ci
2986cabdff1aSopenharmony_ci            pkttype = bytestream_get_byte(&header);
2987cabdff1aSopenharmony_ci            pktsize = bytestream_get_be24(&header);
2988cabdff1aSopenharmony_ci            ts = bytestream_get_be24(&header);
2989cabdff1aSopenharmony_ci            ts |= bytestream_get_byte(&header) << 24;
2990cabdff1aSopenharmony_ci            bytestream_get_be24(&header);
2991cabdff1aSopenharmony_ci            rt->flv_size = pktsize;
2992cabdff1aSopenharmony_ci
2993cabdff1aSopenharmony_ci            if (pkttype == RTMP_PT_VIDEO)
2994cabdff1aSopenharmony_ci                channel = RTMP_VIDEO_CHANNEL;
2995cabdff1aSopenharmony_ci
2996cabdff1aSopenharmony_ci            if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) ||
2997cabdff1aSopenharmony_ci                pkttype == RTMP_PT_NOTIFY) {
2998cabdff1aSopenharmony_ci                if ((ret = ff_rtmp_check_alloc_array(&rt->prev_pkt[1],
2999cabdff1aSopenharmony_ci                                                     &rt->nb_prev_pkt[1],
3000cabdff1aSopenharmony_ci                                                     channel)) < 0)
3001cabdff1aSopenharmony_ci                    return ret;
3002cabdff1aSopenharmony_ci                // Force sending a full 12 bytes header by clearing the
3003cabdff1aSopenharmony_ci                // channel id, to make it not match a potential earlier
3004cabdff1aSopenharmony_ci                // packet in the same channel.
3005cabdff1aSopenharmony_ci                rt->prev_pkt[1][channel].channel_id = 0;
3006cabdff1aSopenharmony_ci            }
3007cabdff1aSopenharmony_ci
3008cabdff1aSopenharmony_ci            //this can be a big packet, it's better to send it right here
3009cabdff1aSopenharmony_ci            if ((ret = ff_rtmp_packet_create(&rt->out_pkt, channel,
3010cabdff1aSopenharmony_ci                                             pkttype, ts, pktsize)) < 0)
3011cabdff1aSopenharmony_ci                return ret;
3012cabdff1aSopenharmony_ci
3013cabdff1aSopenharmony_ci            rt->out_pkt.extra = rt->stream_id;
3014cabdff1aSopenharmony_ci            rt->flv_data = rt->out_pkt.data;
3015cabdff1aSopenharmony_ci        }
3016cabdff1aSopenharmony_ci
3017cabdff1aSopenharmony_ci        copy = FFMIN(rt->flv_size - rt->flv_off, size_temp);
3018cabdff1aSopenharmony_ci        bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, copy);
3019cabdff1aSopenharmony_ci        rt->flv_off += copy;
3020cabdff1aSopenharmony_ci        size_temp   -= copy;
3021cabdff1aSopenharmony_ci
3022cabdff1aSopenharmony_ci        if (rt->flv_off == rt->flv_size) {
3023cabdff1aSopenharmony_ci            rt->skip_bytes = 4;
3024cabdff1aSopenharmony_ci
3025cabdff1aSopenharmony_ci            if (rt->out_pkt.type == RTMP_PT_NOTIFY) {
3026cabdff1aSopenharmony_ci                // For onMetaData and |RtmpSampleAccess packets, we want
3027cabdff1aSopenharmony_ci                // @setDataFrame prepended to the packet before it gets sent.
3028cabdff1aSopenharmony_ci                // However, not all RTMP_PT_NOTIFY packets (e.g., onTextData
3029cabdff1aSopenharmony_ci                // and onCuePoint).
3030cabdff1aSopenharmony_ci                uint8_t commandbuffer[64];
3031cabdff1aSopenharmony_ci                int stringlen = 0;
3032cabdff1aSopenharmony_ci                GetByteContext gbc;
3033cabdff1aSopenharmony_ci
3034cabdff1aSopenharmony_ci                bytestream2_init(&gbc, rt->flv_data, rt->flv_size);
3035cabdff1aSopenharmony_ci                if (!ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer),
3036cabdff1aSopenharmony_ci                                        &stringlen)) {
3037cabdff1aSopenharmony_ci                    if (!strcmp(commandbuffer, "onMetaData") ||
3038cabdff1aSopenharmony_ci                        !strcmp(commandbuffer, "|RtmpSampleAccess")) {
3039cabdff1aSopenharmony_ci                        uint8_t *ptr;
3040cabdff1aSopenharmony_ci                        if ((ret = av_reallocp(&rt->out_pkt.data, rt->out_pkt.size + 16)) < 0) {
3041cabdff1aSopenharmony_ci                            rt->flv_size = rt->flv_off = rt->flv_header_bytes = 0;
3042cabdff1aSopenharmony_ci                            return ret;
3043cabdff1aSopenharmony_ci                        }
3044cabdff1aSopenharmony_ci                        memmove(rt->out_pkt.data + 16, rt->out_pkt.data, rt->out_pkt.size);
3045cabdff1aSopenharmony_ci                        rt->out_pkt.size += 16;
3046cabdff1aSopenharmony_ci                        ptr = rt->out_pkt.data;
3047cabdff1aSopenharmony_ci                        ff_amf_write_string(&ptr, "@setDataFrame");
3048cabdff1aSopenharmony_ci                    }
3049cabdff1aSopenharmony_ci                }
3050cabdff1aSopenharmony_ci            }
3051cabdff1aSopenharmony_ci
3052cabdff1aSopenharmony_ci            if ((ret = rtmp_send_packet(rt, &rt->out_pkt, 0)) < 0)
3053cabdff1aSopenharmony_ci                return ret;
3054cabdff1aSopenharmony_ci            rt->flv_size = 0;
3055cabdff1aSopenharmony_ci            rt->flv_off = 0;
3056cabdff1aSopenharmony_ci            rt->flv_header_bytes = 0;
3057cabdff1aSopenharmony_ci            rt->flv_nb_packets++;
3058cabdff1aSopenharmony_ci        }
3059cabdff1aSopenharmony_ci    } while (buf_temp - buf < size);
3060cabdff1aSopenharmony_ci
3061cabdff1aSopenharmony_ci    if (rt->flv_nb_packets < rt->flush_interval)
3062cabdff1aSopenharmony_ci        return size;
3063cabdff1aSopenharmony_ci    rt->flv_nb_packets = 0;
3064cabdff1aSopenharmony_ci
3065cabdff1aSopenharmony_ci    /* set stream into nonblocking mode */
3066cabdff1aSopenharmony_ci    rt->stream->flags |= AVIO_FLAG_NONBLOCK;
3067cabdff1aSopenharmony_ci
3068cabdff1aSopenharmony_ci    /* try to read one byte from the stream */
3069cabdff1aSopenharmony_ci    ret = ffurl_read(rt->stream, &c, 1);
3070cabdff1aSopenharmony_ci
3071cabdff1aSopenharmony_ci    /* switch the stream back into blocking mode */
3072cabdff1aSopenharmony_ci    rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;
3073cabdff1aSopenharmony_ci
3074cabdff1aSopenharmony_ci    if (ret == AVERROR(EAGAIN)) {
3075cabdff1aSopenharmony_ci        /* no incoming data to handle */
3076cabdff1aSopenharmony_ci        return size;
3077cabdff1aSopenharmony_ci    } else if (ret < 0) {
3078cabdff1aSopenharmony_ci        return ret;
3079cabdff1aSopenharmony_ci    } else if (ret == 1) {
3080cabdff1aSopenharmony_ci        RTMPPacket rpkt = { 0 };
3081cabdff1aSopenharmony_ci
3082cabdff1aSopenharmony_ci        if ((ret = ff_rtmp_packet_read_internal(rt->stream, &rpkt,
3083cabdff1aSopenharmony_ci                                                rt->in_chunk_size,
3084cabdff1aSopenharmony_ci                                                &rt->prev_pkt[0],
3085cabdff1aSopenharmony_ci                                                &rt->nb_prev_pkt[0], c)) <= 0)
3086cabdff1aSopenharmony_ci             return ret;
3087cabdff1aSopenharmony_ci
3088cabdff1aSopenharmony_ci        if ((ret = rtmp_parse_result(s, rt, &rpkt)) < 0)
3089cabdff1aSopenharmony_ci            return ret;
3090cabdff1aSopenharmony_ci
3091cabdff1aSopenharmony_ci        ff_rtmp_packet_destroy(&rpkt);
3092cabdff1aSopenharmony_ci    }
3093cabdff1aSopenharmony_ci
3094cabdff1aSopenharmony_ci    return size;
3095cabdff1aSopenharmony_ci}
3096cabdff1aSopenharmony_ci
3097cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(RTMPContext, x)
3098cabdff1aSopenharmony_ci#define DEC AV_OPT_FLAG_DECODING_PARAM
3099cabdff1aSopenharmony_ci#define ENC AV_OPT_FLAG_ENCODING_PARAM
3100cabdff1aSopenharmony_ci
3101cabdff1aSopenharmony_cistatic const AVOption rtmp_options[] = {
3102cabdff1aSopenharmony_ci    {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3103cabdff1aSopenharmony_ci    {"rtmp_buffer", "Set buffer time in milliseconds. The default is 3000.", OFFSET(client_buffer_time), AV_OPT_TYPE_INT, {.i64 = 3000}, 0, INT_MAX, DEC|ENC},
3104cabdff1aSopenharmony_ci    {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3105cabdff1aSopenharmony_ci    {"rtmp_flashver", "Version of the Flash plugin used to run the SWF player.", OFFSET(flashver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3106cabdff1aSopenharmony_ci    {"rtmp_flush_interval", "Number of packets flushed in the same request (RTMPT only).", OFFSET(flush_interval), AV_OPT_TYPE_INT, {.i64 = 10}, 0, INT_MAX, ENC},
3107cabdff1aSopenharmony_ci    {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = -2}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
3108cabdff1aSopenharmony_ci    {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
3109cabdff1aSopenharmony_ci    {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
3110cabdff1aSopenharmony_ci    {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
3111cabdff1aSopenharmony_ci    {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
3112cabdff1aSopenharmony_ci    {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3113cabdff1aSopenharmony_ci    {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
3114cabdff1aSopenharmony_ci    {"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC},
3115cabdff1aSopenharmony_ci    {"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC},
3116cabdff1aSopenharmony_ci    {"rtmp_swfurl", "URL of the SWF player. By default no value will be sent", OFFSET(swfurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3117cabdff1aSopenharmony_ci    {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically.", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
3118cabdff1aSopenharmony_ci    {"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3119cabdff1aSopenharmony_ci    {"rtmp_listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
3120cabdff1aSopenharmony_ci    {"listen",      "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
3121cabdff1aSopenharmony_ci    {"tcp_nodelay", "Use TCP_NODELAY to disable Nagle's algorithm", OFFSET(tcp_nodelay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC|ENC},
3122cabdff1aSopenharmony_ci    {"timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies -rtmp_listen 1",  OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
3123cabdff1aSopenharmony_ci    { NULL },
3124cabdff1aSopenharmony_ci};
3125cabdff1aSopenharmony_ci
3126cabdff1aSopenharmony_ci#define RTMP_PROTOCOL_0(flavor)
3127cabdff1aSopenharmony_ci#define RTMP_PROTOCOL_1(flavor)                  \
3128cabdff1aSopenharmony_cistatic const AVClass flavor##_class = {          \
3129cabdff1aSopenharmony_ci    .class_name = #flavor,                       \
3130cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,          \
3131cabdff1aSopenharmony_ci    .option     = rtmp_options,                  \
3132cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,         \
3133cabdff1aSopenharmony_ci};                                               \
3134cabdff1aSopenharmony_ci                                                 \
3135cabdff1aSopenharmony_ciconst URLProtocol ff_##flavor##_protocol = {     \
3136cabdff1aSopenharmony_ci    .name           = #flavor,                   \
3137cabdff1aSopenharmony_ci    .url_open2      = rtmp_open,                 \
3138cabdff1aSopenharmony_ci    .url_read       = rtmp_read,                 \
3139cabdff1aSopenharmony_ci    .url_read_seek  = rtmp_seek,                 \
3140cabdff1aSopenharmony_ci    .url_read_pause = rtmp_pause,                \
3141cabdff1aSopenharmony_ci    .url_write      = rtmp_write,                \
3142cabdff1aSopenharmony_ci    .url_close      = rtmp_close,                \
3143cabdff1aSopenharmony_ci    .priv_data_size = sizeof(RTMPContext),       \
3144cabdff1aSopenharmony_ci    .flags          = URL_PROTOCOL_FLAG_NETWORK, \
3145cabdff1aSopenharmony_ci    .priv_data_class= &flavor##_class,           \
3146cabdff1aSopenharmony_ci};
3147cabdff1aSopenharmony_ci#define RTMP_PROTOCOL_2(flavor, enabled)         \
3148cabdff1aSopenharmony_ci    RTMP_PROTOCOL_ ## enabled(flavor)
3149cabdff1aSopenharmony_ci#define RTMP_PROTOCOL_3(flavor, config)          \
3150cabdff1aSopenharmony_ci    RTMP_PROTOCOL_2(flavor, config)
3151cabdff1aSopenharmony_ci#define RTMP_PROTOCOL(flavor, uppercase)         \
3152cabdff1aSopenharmony_ci    RTMP_PROTOCOL_3(flavor, CONFIG_ ## uppercase ## _PROTOCOL)
3153cabdff1aSopenharmony_ci
3154cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmp,   RTMP)
3155cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmpe,  RTMPE)
3156cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmps,  RTMPS)
3157cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmpt,  RTMPT)
3158cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmpte, RTMPTE)
3159cabdff1aSopenharmony_ciRTMP_PROTOCOL(rtmpts, RTMPTS)
3160