typedef struct RTMPContext {
const AVClass *class;
URLContext* stream; ///< TCP stream used in interactions with RTMP server
- RTMPPacket prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets ([0] for reading, [1] for writing)
+ RTMPPacket *prev_pkt[2]; ///< packet history used when reading and sending packets ([0] for reading, [1] for writing)
+ int nb_prev_pkt[2]; ///< number of elements in prev_pkt
int in_chunk_size; ///< size of the chunks incoming RTMP packets are divided into
int out_chunk_size; ///< size of the chunks outgoing RTMP packets are divided into
int is_input; ///< input/output flag
if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) {
rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2;
if ((err = av_reallocp(&rt->tracked_methods, rt->tracked_methods_size *
- sizeof(*rt->tracked_methods))) < 0)
+ sizeof(*rt->tracked_methods))) < 0) {
+ rt->nb_tracked_methods = 0;
+ rt->tracked_methods_size = 0;
return err;
+ }
}
rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
}
ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
fail:
ff_rtmp_packet_destroy(pkt);
return ret;
GetByteContext gbc;
if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
- rt->prev_pkt[0])) < 0)
+ &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
return ret;
cp = pkt.data;
bytestream2_init(&gbc, cp, pkt.size);
bytestream_put_be32(&p, rt->server_bw);
pkt.size = p - pkt.data;
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
if (ret < 0)
return ret;
bytestream_put_byte(&p, 2); // dynamic
pkt.size = p - pkt.data;
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
if (ret < 0)
return ret;
bytestream_put_be16(&p, 0); // 0 -> Stream Begin
bytestream_put_be32(&p, 0);
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
if (ret < 0)
return ret;
p = pkt.data;
bytestream_put_be32(&p, rt->out_chunk_size);
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
if (ret < 0)
return ret;
pkt.size = p - pkt.data;
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
if (ret < 0)
return ret;
ff_amf_write_number(&p, 8192);
pkt.size = p - pkt.data;
ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&pkt);
return ret;
ff_amf_write_number(&p, ++rt->nb_invokes);
ff_amf_write_null(&p);
ff_amf_write_string(&p, rt->playpath);
- ff_amf_write_number(&p, rt->live);
+ ff_amf_write_number(&p, rt->live * 1000);
return rtmp_send_packet(rt, &pkt, 1);
}
for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
tosend[i] = av_lfg_get(&rnd) >> 24;
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* When the client wants to use RTMPE, we have to change the command
* byte to 0x06 which means to use encrypted data and we have to set
* the flash version to at least 9.0.115.0. */
if (ret < 0)
return ret;
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* Compute the shared secret key sent by the server and initialize
* the RC4 encryption. */
if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
if (ret < 0)
return ret;
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* Encrypt the signature to be send to the server. */
ff_rtmpe_encrypt_sig(rt->stream, tosend +
RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
return ret;
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* Set RC4 keys for encryption and update the keystreams. */
if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
return ret;
}
} else {
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* Compute the shared secret key sent by the server and initialize
* the RC4 encryption. */
if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
return ret;
- if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ if (CONFIG_FFRTMPCRYPT_PROTOCOL && rt->encrypted) {
/* Set RC4 keys for encryption and update the keystreams. */
if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
return ret;
/* Send the same chunk size change packet back to the server,
* setting the outgoing chunk size to the same as the incoming one. */
if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
- rt->prev_pkt[1])) < 0)
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1])) < 0)
return ret;
rt->out_chunk_size = AV_RB32(pkt->data);
}
bytestream2_put_be32(&pbc, rt->nb_streamid);
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&spkt);
spkt.size = pp - spkt.data;
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&spkt);
return ret;
}
spkt.size = pp - spkt.data;
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
- rt->prev_pkt[1]);
+ &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&spkt);
return ret;
}
t = ff_amf_get_field_value(ptr, data_end, "level", tmpstr, sizeof(tmpstr));
if (!t && !strcmp(tmpstr, "error")) {
- if (!ff_amf_get_field_value(ptr, data_end,
- "description", tmpstr, sizeof(tmpstr)))
+ t = ff_amf_get_field_value(ptr, data_end,
+ "description", tmpstr, sizeof(tmpstr));
+ if (t || !tmpstr[0])
+ t = ff_amf_get_field_value(ptr, data_end, "code",
+ tmpstr, sizeof(tmpstr));
+ if (!t)
av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr);
return -1;
}
if (rt->flv_off < rt->flv_size) {
// There is old unread data in the buffer, thus append at the end
old_flv_size = rt->flv_size;
- rt->flv_size += size + 15;
+ rt->flv_size += size;
} else {
// All data has been read, write the new data at the start of the buffer
old_flv_size = 0;
- rt->flv_size = size + 15;
+ rt->flv_size = size;
rt->flv_off = 0;
}
const int size = pkt->size - skip;
uint32_t ts = pkt->timestamp;
- old_flv_size = update_offset(rt, size);
+ old_flv_size = update_offset(rt, size + 15);
- if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+ if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
+ rt->flv_size = rt->flv_off = 0;
return ret;
+ }
bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
bytestream2_skip_p(&pbc, old_flv_size);
bytestream2_put_byte(&pbc, pkt->type);
old_flv_size = update_offset(rt, pkt->size);
- if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
+ if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
+ rt->flv_size = rt->flv_off = 0;
return ret;
+ }
next = pkt->data;
p = rt->flv_data + old_flv_size;
pts = cts;
ts += cts - pts;
pts = cts;
+ if (size + 3 + 4 > pkt->data + pkt->size - next)
+ break;
bytestream_put_byte(&p, type);
bytestream_put_be24(&p, size);
bytestream_put_be24(&p, ts);
next += size + 3 + 4;
p += size + 3 + 4;
}
- memcpy(p, next, RTMP_HEADER);
+ if (p != rt->flv_data + rt->flv_size) {
+ av_log(NULL, AV_LOG_WARNING, "Incomplete flv packets in "
+ "RTMP_PT_METADATA packet\n");
+ rt->flv_size = p - rt->flv_data;
+ }
return 0;
}
for (;;) {
RTMPPacket rpkt = { 0 };
if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
- rt->in_chunk_size, rt->prev_pkt[0])) <= 0) {
+ rt->in_chunk_size, &rt->prev_pkt[0],
+ &rt->nb_prev_pkt[0])) <= 0) {
if (ret == 0) {
return AVERROR(EAGAIN);
} else {
}
if (rt->state > STATE_HANDSHAKED)
ret = gen_delete_stream(h, rt);
- for (i = 0; i < 2; i++)
- for (j = 0; j < RTMP_CHANNELS; j++)
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < rt->nb_prev_pkt[i]; j++)
ff_rtmp_packet_destroy(&rt->prev_pkt[i][j]);
+ av_freep(&rt->prev_pkt[i]);
+ }
free_tracked_methods(rt);
av_freep(&rt->flv_data);
(!strcmp(fname + len - 4, ".f4v") ||
!strcmp(fname + len - 4, ".mp4"))) {
memcpy(rt->playpath, "mp4:", 5);
- } else if (len >= 4 && !strcmp(fname + len - 4, ".flv")) {
- fname[len - 4] = '\0';
} else {
+ if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
+ fname[len - 4] = '\0';
rt->playpath[0] = 0;
}
av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
goto fail;
if (rt->do_reconnect) {
+ int i;
ffurl_close(rt->stream);
rt->stream = NULL;
rt->do_reconnect = 0;
rt->nb_invokes = 0;
- memset(rt->prev_pkt, 0, sizeof(rt->prev_pkt));
+ for (i = 0; i < 2; i++)
+ memset(rt->prev_pkt[i], 0,
+ sizeof(**rt->prev_pkt) * rt->nb_prev_pkt[i]);
free_tracked_methods(rt);
goto reconnect;
}
pkttype == RTMP_PT_NOTIFY) {
if (pkttype == RTMP_PT_NOTIFY)
pktsize += 16;
+ if ((ret = ff_rtmp_check_alloc_array(&rt->prev_pkt[1],
+ &rt->nb_prev_pkt[1],
+ channel)) < 0)
+ return ret;
rt->prev_pkt[1][channel].channel_id = 0;
}
if ((ret = ff_rtmp_packet_read_internal(rt->stream, &rpkt,
rt->in_chunk_size,
- rt->prev_pkt[0], c)) <= 0)
+ &rt->prev_pkt[0],
+ &rt->nb_prev_pkt[0], c)) <= 0)
return ret;
if ((ret = rtmp_parse_result(s, rt, &rpkt)) < 0)
{"rtmp_swfverify", "URL to player swf file, compute hash/size automatically.", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
{"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},
{"rtmp_listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
+ {"listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
{"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" },
{ NULL },
};