#include "libavcodec/bytestream.h"
#include "libavutil/avstring.h"
+#include "libavutil/intfloat_readwrite.h"
#include "libavutil/lfg.h"
#include "libavutil/sha.h"
#include "avformat.h"
#include "flv.h"
#include "rtmp.h"
#include "rtmppkt.h"
-
-/* we can't use av_log() with URLContext yet... */
-#if FF_API_URL_CLASS
-#define LOG_CONTEXT s
-#else
-#define LOG_CONTEXT NULL
-#endif
+#include "url.h"
//#define DEBUG
uint32_t client_report_size; ///< number of bytes after which client should report to server
uint32_t bytes_read; ///< number of bytes read from server
uint32_t last_bytes_read; ///< number of bytes read last reported to server
+ int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
+ uint8_t flv_header[11]; ///< partial incoming flv packet header
+ int flv_header_bytes; ///< number of initialized bytes in flv_header
} RTMPContext;
#define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
29 + strlen(rt->playpath));
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Releasing stream...\n");
+ av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
p = pkt.data;
ff_amf_write_string(&p, "releaseStream");
ff_amf_write_number(&p, 2.0);
ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
25 + strlen(rt->playpath));
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "FCPublish stream...\n");
+ av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
p = pkt.data;
ff_amf_write_string(&p, "FCPublish");
ff_amf_write_number(&p, 3.0);
ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
27 + strlen(rt->playpath));
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "UnPublishing stream...\n");
+ av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
p = pkt.data;
ff_amf_write_string(&p, "FCUnpublish");
ff_amf_write_number(&p, 5.0);
RTMPPacket pkt;
uint8_t *p;
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Creating stream...\n");
+ av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 25);
p = pkt.data;
RTMPPacket pkt;
uint8_t *p;
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Deleting stream...\n");
+ av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 34);
p = pkt.data;
RTMPPacket pkt;
uint8_t *p;
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
+ av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0,
20 + strlen(rt->playpath));
pkt.extra = rt->main_channel_id;
RTMPPacket pkt;
uint8_t *p;
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
+ av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE, 0,
30 + strlen(rt->playpath));
pkt.extra = rt->main_channel_id;
int server_pos, client_pos;
uint8_t digest[32];
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Handshaking...\n");
+ av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
av_lfg_init(&rnd, 0xDEADC0DE);
// generate handshake packet - 1536 bytes of pseudorandom data
tosend[i] = av_lfg_get(&rnd) >> 24;
client_pos = rtmp_handshake_imprint_with_digest(tosend + 1);
- url_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1);
- i = url_read_complete(rt->stream, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1);
+ ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1);
+ i = ffurl_read_complete(rt->stream, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1);
if (i != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
return -1;
}
- i = url_read_complete(rt->stream, clientdata, RTMP_HANDSHAKE_PACKET_SIZE);
+ i = ffurl_read_complete(rt->stream, clientdata, RTMP_HANDSHAKE_PACKET_SIZE);
if (i != RTMP_HANDSHAKE_PACKET_SIZE) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
return -1;
}
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
+ av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
if (rt->is_input && serverdata[5] >= 3) {
if (!server_pos) {
server_pos = rtmp_validate_digest(serverdata + 1, 8);
if (!server_pos) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server response validating failed\n");
+ av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
return -1;
}
}
digest, 32,
digest);
if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Signature mismatch\n");
+ av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
return -1;
}
tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
// write reply back to the server
- url_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE);
+ ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE);
} else {
- url_write(rt->stream, serverdata+1, RTMP_HANDSHAKE_PACKET_SIZE);
+ ffurl_write(rt->stream, serverdata+1, RTMP_HANDSHAKE_PACKET_SIZE);
}
return 0;
const uint8_t *data_end = pkt->data + pkt->data_size;
#ifdef DEBUG
- ff_rtmp_packet_dump(LOG_CONTEXT, pkt);
+ ff_rtmp_packet_dump(s, pkt);
#endif
switch (pkt->type) {
case RTMP_PT_CHUNK_SIZE:
if (pkt->data_size != 4) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR,
+ av_log(s, AV_LOG_ERROR,
"Chunk size change packet is not 4 bytes long (%d)\n", pkt->data_size);
return -1;
}
ff_rtmp_packet_write(rt->stream, pkt, rt->chunk_size, rt->prev_pkt[1]);
rt->chunk_size = AV_RB32(pkt->data);
if (rt->chunk_size <= 0) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size);
+ av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size);
return -1;
}
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size);
+ av_log(s, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size);
break;
case RTMP_PT_PING:
t = AV_RB16(pkt->data);
break;
case RTMP_PT_CLIENT_BW:
if (pkt->data_size < 4) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR,
+ av_log(s, AV_LOG_ERROR,
"Client bandwidth report packet is less than 4 bytes long (%d)\n",
pkt->data_size);
return -1;
}
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data));
+ av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data));
rt->client_report_size = AV_RB32(pkt->data) >> 1;
break;
case RTMP_PT_INVOKE:
if (!ff_amf_get_field_value(pkt->data + 9, data_end,
"description", tmpstr, sizeof(tmpstr)))
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
+ av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
return -1;
} else if (!memcmp(pkt->data, "\002\000\007_result", 10)) {
switch (rt->state) {
case STATE_CONNECTING:
//extract a number from the result
if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) {
- av_log(LOG_CONTEXT, AV_LOG_WARNING, "Unexpected reply on connect()\n");
+ av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
} else {
rt->main_channel_id = (int) av_int2dbl(AV_RB64(pkt->data + 21));
}
if (!t && !strcmp(tmpstr, "error")) {
if (!ff_amf_get_field_value(ptr, data_end,
"description", tmpstr, sizeof(tmpstr)))
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
+ av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
return -1;
}
t = ff_amf_get_field_value(ptr, data_end,
return AVERROR_EOF;
for (;;) {
- RTMPPacket rpkt;
+ RTMPPacket rpkt = { 0 };
if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
rt->chunk_size, rt->prev_pkt[0])) <= 0) {
if (ret == 0) {
}
rt->bytes_read += ret;
if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending bytes read report\n");
+ av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
gen_bytes_read(s, rt, rpkt.timestamp + 1);
rt->last_bytes_read = rt->bytes_read;
}
}
ff_rtmp_packet_destroy(&rpkt);
}
- return 0;
}
static int rtmp_close(URLContext *h)
gen_delete_stream(h, rt);
av_freep(&rt->flv_data);
- url_close(rt->stream);
+ ffurl_close(rt->stream);
av_free(rt);
return 0;
}
if (!rt)
return AVERROR(ENOMEM);
s->priv_data = rt;
- rt->is_input = !(flags & URL_WRONLY);
+ rt->is_input = !(flags & AVIO_FLAG_WRITE);
av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
path, sizeof(path), s->filename);
port = RTMP_DEFAULT_PORT;
ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
- if (url_open(&rt->stream, buf, URL_RDWR) < 0) {
- av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot open connection %s\n", buf);
+ if (ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE) < 0) {
+ av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
goto fail;
}
rt->bytes_read = 0;
rt->last_bytes_read = 0;
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
+ av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
proto, path, rt->app, rt->playpath);
gen_connect(s, rt, proto, hostname, port);
rt->flv_size = 0;
rt->flv_data = NULL;
rt->flv_off = 0;
+ rt->skip_bytes = 13;
}
- s->max_packet_size = url_get_max_packet_size(rt->stream);
+ s->max_packet_size = rt->stream->max_packet_size;
s->is_streamed = 1;
return 0;
uint32_t ts;
const uint8_t *buf_temp = buf;
- if (size < 11) {
- av_log(LOG_CONTEXT, AV_LOG_DEBUG, "FLV packet too small %d\n", size);
- return 0;
- }
-
do {
- if (!rt->flv_off) {
- //skip flv header
- if (buf_temp[0] == 'F' && buf_temp[1] == 'L' && buf_temp[2] == 'V') {
- buf_temp += 9 + 4;
- size_temp -= 9 + 4;
- }
+ if (rt->skip_bytes) {
+ int skip = FFMIN(rt->skip_bytes, size_temp);
+ buf_temp += skip;
+ size_temp -= skip;
+ rt->skip_bytes -= skip;
+ continue;
+ }
+
+ if (rt->flv_header_bytes < 11) {
+ const uint8_t *header = rt->flv_header;
+ int copy = FFMIN(11 - rt->flv_header_bytes, size_temp);
+ bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
+ rt->flv_header_bytes += copy;
+ size_temp -= copy;
+ if (rt->flv_header_bytes < 11)
+ break;
- pkttype = bytestream_get_byte(&buf_temp);
- pktsize = bytestream_get_be24(&buf_temp);
- ts = bytestream_get_be24(&buf_temp);
- ts |= bytestream_get_byte(&buf_temp) << 24;
- bytestream_get_be24(&buf_temp);
- size_temp -= 11;
+ pkttype = bytestream_get_byte(&header);
+ pktsize = bytestream_get_be24(&header);
+ ts = bytestream_get_be24(&header);
+ ts |= bytestream_get_byte(&header) << 24;
+ bytestream_get_be24(&header);
rt->flv_size = pktsize;
//force 12bytes header
if (rt->flv_size - rt->flv_off > size_temp) {
bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, size_temp);
rt->flv_off += size_temp;
+ size_temp = 0;
} else {
bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, rt->flv_size - rt->flv_off);
+ size_temp -= rt->flv_size - rt->flv_off;
rt->flv_off += rt->flv_size - rt->flv_off;
}
if (rt->flv_off == rt->flv_size) {
- bytestream_get_be32(&buf_temp);
+ rt->skip_bytes = 4;
ff_rtmp_packet_write(rt->stream, &rt->out_pkt, rt->chunk_size, rt->prev_pkt[1]);
ff_rtmp_packet_destroy(&rt->out_pkt);
rt->flv_size = 0;
rt->flv_off = 0;
+ rt->flv_header_bytes = 0;
}
- } while (buf_temp - buf < size_temp);
+ } while (buf_temp - buf < size);
return size;
}
URLProtocol ff_rtmp_protocol = {
- "rtmp",
- rtmp_open,
- rtmp_read,
- rtmp_write,
- NULL, /* seek */
- rtmp_close,
+ .name = "rtmp",
+ .url_open = rtmp_open,
+ .url_read = rtmp_read,
+ .url_write = rtmp_write,
+ .url_close = rtmp_close,
};