return 0;
}
-int ff_amf_read_string(GetByteContext *bc, uint8_t *str,
- int strsize, int *length)
+int ff_amf_get_string(GetByteContext *bc, uint8_t *str,
+ int strsize, int *length)
{
int stringlen = 0;
int readsize;
- if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_STRING)
- return AVERROR_INVALIDDATA;
stringlen = bytestream2_get_be16(bc);
if (stringlen + 1 > strsize)
return AVERROR(EINVAL);
return 0;
}
+int ff_amf_read_string(GetByteContext *bc, uint8_t *str,
+ int strsize, int *length)
+{
+ if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_STRING)
+ return AVERROR_INVALIDDATA;
+ return ff_amf_get_string(bc, str, strsize, length);
+}
+
int ff_amf_read_null(GetByteContext *bc)
{
if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_NULL)
return 0;
}
+int ff_rtmp_check_alloc_array(RTMPPacket **prev_pkt, int *nb_prev_pkt,
+ int channel)
+{
+ int nb_alloc;
+ RTMPPacket *ptr;
+ if (channel < *nb_prev_pkt)
+ return 0;
+
+ nb_alloc = channel + 16;
+ // This can't use the av_reallocp family of functions, since we
+ // would need to free each element in the array before the array
+ // itself is freed.
+ ptr = av_realloc_array(*prev_pkt, nb_alloc, sizeof(**prev_pkt));
+ if (!ptr)
+ return AVERROR(ENOMEM);
+ memset(ptr + *nb_prev_pkt, 0, (nb_alloc - *nb_prev_pkt) * sizeof(*ptr));
+ *prev_pkt = ptr;
+ *nb_prev_pkt = nb_alloc;
+ return 0;
+}
+
int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p,
- int chunk_size, RTMPPacket *prev_pkt)
+ int chunk_size, RTMPPacket **prev_pkt, int *nb_prev_pkt)
{
uint8_t hdr;
if (ffurl_read(h, &hdr, 1) != 1)
return AVERROR(EIO);
- return ff_rtmp_packet_read_internal(h, p, chunk_size, prev_pkt, hdr);
+ return ff_rtmp_packet_read_internal(h, p, chunk_size, prev_pkt,
+ nb_prev_pkt, hdr);
}
static int rtmp_packet_read_one_chunk(URLContext *h, RTMPPacket *p,
- int chunk_size, RTMPPacket *prev_pkt,
- uint8_t hdr)
+ int chunk_size, RTMPPacket **prev_pkt_ptr,
+ int *nb_prev_pkt, uint8_t hdr)
{
uint8_t buf[16];
int channel_id, timestamp, size;
+ uint32_t ts_field; // non-extended timestamp or delta field
uint32_t extra = 0;
enum RTMPPacketType type;
int written = 0;
int ret, toread;
+ RTMPPacket *prev_pkt;
written++;
channel_id = hdr & 0x3F;
written += channel_id + 1;
channel_id = AV_RL16(buf) + 64;
}
+ if ((ret = ff_rtmp_check_alloc_array(prev_pkt_ptr, nb_prev_pkt,
+ channel_id)) < 0)
+ return ret;
+ prev_pkt = *prev_pkt_ptr;
size = prev_pkt[channel_id].size;
type = prev_pkt[channel_id].type;
extra = prev_pkt[channel_id].extra;
- hdr >>= 6;
+ hdr >>= 6; // header size indicator
if (hdr == RTMP_PS_ONEBYTE) {
- timestamp = prev_pkt[channel_id].ts_delta;
+ ts_field = prev_pkt[channel_id].ts_field;
} else {
if (ffurl_read_complete(h, buf, 3) != 3)
return AVERROR(EIO);
written += 3;
- timestamp = AV_RB24(buf);
+ ts_field = AV_RB24(buf);
if (hdr != RTMP_PS_FOURBYTES) {
if (ffurl_read_complete(h, buf, 3) != 3)
return AVERROR(EIO);
extra = AV_RL32(buf);
}
}
- if (timestamp == 0xFFFFFF) {
- if (ffurl_read_complete(h, buf, 4) != 4)
- return AVERROR(EIO);
- timestamp = AV_RB32(buf);
- }
+ }
+ if (ts_field == 0xFFFFFF) {
+ if (ffurl_read_complete(h, buf, 4) != 4)
+ return AVERROR(EIO);
+ timestamp = AV_RB32(buf);
+ } else {
+ timestamp = ts_field;
}
if (hdr != RTMP_PS_TWELVEBYTES)
timestamp += prev_pkt[channel_id].timestamp;
return ret;
p->read = written;
p->offset = 0;
- prev_pkt[channel_id].ts_delta = timestamp -
- prev_pkt[channel_id].timestamp;
+ prev_pkt[channel_id].ts_field = ts_field;
prev_pkt[channel_id].timestamp = timestamp;
} else {
// previous packet in this channel hasn't completed reading
p->size = prev->size;
p->channel_id = prev->channel_id;
p->type = prev->type;
- p->ts_delta = prev->ts_delta;
+ p->ts_field = prev->ts_field;
p->extra = prev->extra;
p->offset = prev->offset;
p->read = prev->read + written;
}
int ff_rtmp_packet_read_internal(URLContext *h, RTMPPacket *p, int chunk_size,
- RTMPPacket *prev_pkt, uint8_t hdr)
+ RTMPPacket **prev_pkt, int *nb_prev_pkt,
+ uint8_t hdr)
{
while (1) {
- int ret = rtmp_packet_read_one_chunk(h, p, chunk_size, prev_pkt, hdr);
+ int ret = rtmp_packet_read_one_chunk(h, p, chunk_size, prev_pkt,
+ nb_prev_pkt, hdr);
if (ret > 0 || ret != AVERROR(EAGAIN))
return ret;
}
int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
- int chunk_size, RTMPPacket *prev_pkt)
+ int chunk_size, RTMPPacket **prev_pkt_ptr,
+ int *nb_prev_pkt)
{
uint8_t pkt_hdr[16], *p = pkt_hdr;
int mode = RTMP_PS_TWELVEBYTES;
int off = 0;
int written = 0;
int ret;
+ RTMPPacket *prev_pkt;
+ int use_delta; // flag if using timestamp delta, not RTMP_PS_TWELVEBYTES
+ uint32_t timestamp; // full 32-bit timestamp or delta value
- pkt->ts_delta = pkt->timestamp - prev_pkt[pkt->channel_id].timestamp;
+ if ((ret = ff_rtmp_check_alloc_array(prev_pkt_ptr, nb_prev_pkt,
+ pkt->channel_id)) < 0)
+ return ret;
+ prev_pkt = *prev_pkt_ptr;
//if channel_id = 0, this is first presentation of prev_pkt, send full hdr.
- if (prev_pkt[pkt->channel_id].channel_id &&
- pkt->extra == prev_pkt[pkt->channel_id].extra) {
+ use_delta = prev_pkt[pkt->channel_id].channel_id &&
+ pkt->extra == prev_pkt[pkt->channel_id].extra &&
+ pkt->timestamp >= prev_pkt[pkt->channel_id].timestamp;
+
+ timestamp = pkt->timestamp;
+ if (use_delta) {
+ timestamp -= prev_pkt[pkt->channel_id].timestamp;
+ }
+ if (timestamp >= 0xFFFFFF) {
+ pkt->ts_field = 0xFFFFFF;
+ } else {
+ pkt->ts_field = timestamp;
+ }
+
+ if (use_delta) {
if (pkt->type == prev_pkt[pkt->channel_id].type &&
pkt->size == prev_pkt[pkt->channel_id].size) {
mode = RTMP_PS_FOURBYTES;
- if (pkt->ts_delta == prev_pkt[pkt->channel_id].ts_delta)
+ if (pkt->ts_field == prev_pkt[pkt->channel_id].ts_field)
mode = RTMP_PS_ONEBYTE;
} else {
mode = RTMP_PS_EIGHTBYTES;
bytestream_put_le16(&p, pkt->channel_id - 64);
}
if (mode != RTMP_PS_ONEBYTE) {
- uint32_t timestamp = pkt->timestamp;
- if (mode != RTMP_PS_TWELVEBYTES)
- timestamp = pkt->ts_delta;
- bytestream_put_be24(&p, timestamp >= 0xFFFFFF ? 0xFFFFFF : timestamp);
+ bytestream_put_be24(&p, pkt->ts_field);
if (mode != RTMP_PS_FOURBYTES) {
bytestream_put_be24(&p, pkt->size);
bytestream_put_byte(&p, pkt->type);
if (mode == RTMP_PS_TWELVEBYTES)
bytestream_put_le32(&p, pkt->extra);
}
- if (timestamp >= 0xFFFFFF)
- bytestream_put_be32(&p, timestamp);
}
+ if (pkt->ts_field == 0xFFFFFF)
+ bytestream_put_be32(&p, timestamp);
// save history
prev_pkt[pkt->channel_id].channel_id = pkt->channel_id;
prev_pkt[pkt->channel_id].type = pkt->type;
prev_pkt[pkt->channel_id].size = pkt->size;
prev_pkt[pkt->channel_id].timestamp = pkt->timestamp;
- if (mode != RTMP_PS_TWELVEBYTES) {
- prev_pkt[pkt->channel_id].ts_delta = pkt->ts_delta;
- } else {
- prev_pkt[pkt->channel_id].ts_delta = pkt->timestamp;
- }
+ prev_pkt[pkt->channel_id].ts_field = pkt->ts_field;
prev_pkt[pkt->channel_id].extra = pkt->extra;
if ((ret = ffurl_write(h, pkt_hdr, p - pkt_hdr)) < 0)
pkt->type = type;
pkt->timestamp = timestamp;
pkt->extra = 0;
- pkt->ts_delta = 0;
+ pkt->ts_field = 0;
return 0;
}
int ff_amf_tag_size(const uint8_t *data, const uint8_t *data_end)
{
const uint8_t *base = data;
+ AMFDataType type;
+ unsigned nb = -1;
+ int parse_key = 1;
if (data >= data_end)
return -1;
- switch (*data++) {
+ switch ((type = *data++)) {
case AMF_DATA_TYPE_NUMBER: return 9;
case AMF_DATA_TYPE_BOOL: return 2;
case AMF_DATA_TYPE_STRING: return 3 + AV_RB16(data);
case AMF_DATA_TYPE_LONG_STRING: return 5 + AV_RB32(data);
case AMF_DATA_TYPE_NULL: return 1;
case AMF_DATA_TYPE_ARRAY:
- data += 4;
+ parse_key = 0;
+ case AMF_DATA_TYPE_MIXEDARRAY:
+ nb = bytestream_get_be32(&data);
case AMF_DATA_TYPE_OBJECT:
- for (;;) {
- int size = bytestream_get_be16(&data);
+ while (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY) {
int t;
- if (!size) {
- data++;
- break;
+ if (parse_key) {
+ int size = bytestream_get_be16(&data);
+ if (!size) {
+ data++;
+ break;
+ }
+ if (size < 0 || size >= data_end - data)
+ return -1;
+ data += size;
}
- if (size < 0 || size >= data_end - data)
- return -1;
- data += size;
t = ff_amf_tag_size(data, data_end);
if (t < 0 || t >= data_end - data)
return -1;
static void amf_tag_contents(void *ctx, const uint8_t *data,
const uint8_t *data_end)
{
- unsigned int size;
+ unsigned int size, nb = -1;
char buf[1024];
+ AMFDataType type;
+ int parse_key = 1;
if (data >= data_end)
return;
- switch (*data++) {
+ switch ((type = *data++)) {
case AMF_DATA_TYPE_NUMBER:
av_log(ctx, AV_LOG_DEBUG, " number %g\n", av_int2double(AV_RB64(data)));
return;
return;
case AMF_DATA_TYPE_STRING:
case AMF_DATA_TYPE_LONG_STRING:
- if (data[-1] == AMF_DATA_TYPE_STRING) {
+ if (type == AMF_DATA_TYPE_STRING) {
size = bytestream_get_be16(&data);
} else {
size = bytestream_get_be32(&data);
av_log(ctx, AV_LOG_DEBUG, " NULL\n");
return;
case AMF_DATA_TYPE_ARRAY:
- data += 4;
+ parse_key = 0;
+ case AMF_DATA_TYPE_MIXEDARRAY:
+ nb = bytestream_get_be32(&data);
case AMF_DATA_TYPE_OBJECT:
av_log(ctx, AV_LOG_DEBUG, " {\n");
- for (;;) {
+ while (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY) {
int t;
- size = bytestream_get_be16(&data);
- av_strlcpy(buf, data, FFMIN(sizeof(buf), size + 1));
- if (!size) {
- av_log(ctx, AV_LOG_DEBUG, " }\n");
- data++;
- break;
+ if (parse_key) {
+ size = bytestream_get_be16(&data);
+ size = FFMIN(size, sizeof(buf) - 1);
+ if (!size) {
+ av_log(ctx, AV_LOG_DEBUG, " }\n");
+ data++;
+ break;
+ }
+ memcpy(buf, data, size);
+ buf[size] = 0;
+ if (size >= data_end - data)
+ return;
+ data += size;
+ av_log(ctx, AV_LOG_DEBUG, " %s: ", buf);
}
- if (size >= data_end - data)
- return;
- data += size;
- av_log(ctx, AV_LOG_DEBUG, " %s: ", buf);
amf_tag_contents(ctx, data, data_end);
t = ff_amf_tag_size(data, data_end);
if (t < 0 || t >= data_end - data)