rtmppkt.c 16.1 KB
Newer Older
1 2 3 4
/*
 * RTMP input format
 * Copyright (c) 2009 Kostya Shishkov
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; if not, write to the Free Software
19 20 21 22 23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavcodec/bytestream.h"
#include "libavutil/avstring.h"
24
#include "libavutil/intfloat.h"
25 26 27 28
#include "avformat.h"

#include "rtmppkt.h"
#include "flv.h"
29
#include "url.h"
30 31 32 33 34 35 36 37 38 39

void ff_amf_write_bool(uint8_t **dst, int val)
{
    bytestream_put_byte(dst, AMF_DATA_TYPE_BOOL);
    bytestream_put_byte(dst, val);
}

void ff_amf_write_number(uint8_t **dst, double val)
{
    bytestream_put_byte(dst, AMF_DATA_TYPE_NUMBER);
40
    bytestream_put_be64(dst, av_double2int(val));
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
}

void ff_amf_write_string(uint8_t **dst, const char *str)
{
    bytestream_put_byte(dst, AMF_DATA_TYPE_STRING);
    bytestream_put_be16(dst, strlen(str));
    bytestream_put_buffer(dst, str, strlen(str));
}

void ff_amf_write_null(uint8_t **dst)
{
    bytestream_put_byte(dst, AMF_DATA_TYPE_NULL);
}

void ff_amf_write_object_start(uint8_t **dst)
{
    bytestream_put_byte(dst, AMF_DATA_TYPE_OBJECT);
}

void ff_amf_write_field_name(uint8_t **dst, const char *str)
{
    bytestream_put_be16(dst, strlen(str));
    bytestream_put_buffer(dst, str, strlen(str));
}

void ff_amf_write_object_end(uint8_t **dst)
{
    /* first two bytes are field name length = 0,
     * AMF object should end with it and end marker
     */
    bytestream_put_be24(dst, AMF_DATA_TYPE_OBJECT_END);
}

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
int ff_amf_read_bool(GetByteContext *bc, int *val)
{
    if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_BOOL)
        return AVERROR_INVALIDDATA;
    *val = bytestream2_get_byte(bc);
    return 0;
}

int ff_amf_read_number(GetByteContext *bc, double *val)
{
    uint64_t read;
    if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_NUMBER)
        return AVERROR_INVALIDDATA;
    read = bytestream2_get_be64(bc);
    *val = av_int2double(read);
    return 0;
}

int ff_amf_read_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);
    readsize = bytestream2_get_buffer(bc, str, stringlen);
    if (readsize != stringlen) {
        av_log(NULL, AV_LOG_WARNING,
               "Unable to read as many bytes as AMF string signaled\n");
    }
    str[readsize] = '\0';
    *length = FFMIN(stringlen, readsize);
    return 0;
}

int ff_amf_read_null(GetByteContext *bc)
{
    if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_NULL)
        return AVERROR_INVALIDDATA;
    return 0;
}

119 120 121
int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p,
                        int chunk_size, RTMPPacket *prev_pkt)
{
122 123 124 125 126 127 128 129 130 131 132 133 134
    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);
}

int ff_rtmp_packet_read_internal(URLContext *h, RTMPPacket *p, int chunk_size,
                                 RTMPPacket *prev_pkt, uint8_t hdr)
{

    uint8_t t, buf[16];
135 136
    int channel_id, timestamp, data_size, offset = 0;
    uint32_t extra = 0;
137
    enum RTMPPacketType type;
138
    int size = 0;
139
    int ret;
140

141
    size++;
142 143
    channel_id = hdr & 0x3F;

144 145
    if (channel_id < 2) { //special case for channel number >= 64
        buf[1] = 0;
146
        if (ffurl_read_complete(h, buf, channel_id + 1) != channel_id + 1)
147
            return AVERROR(EIO);
148
        size += channel_id + 1;
149 150
        channel_id = AV_RL16(buf) + 64;
    }
151 152 153 154 155 156
    data_size = prev_pkt[channel_id].data_size;
    type      = prev_pkt[channel_id].type;
    extra     = prev_pkt[channel_id].extra;

    hdr >>= 6;
    if (hdr == RTMP_PS_ONEBYTE) {
157
        timestamp = prev_pkt[channel_id].ts_delta;
158
    } else {
159
        if (ffurl_read_complete(h, buf, 3) != 3)
160
            return AVERROR(EIO);
161
        size += 3;
162 163
        timestamp = AV_RB24(buf);
        if (hdr != RTMP_PS_FOURBYTES) {
164
            if (ffurl_read_complete(h, buf, 3) != 3)
165
                return AVERROR(EIO);
166
            size += 3;
167
            data_size = AV_RB24(buf);
168
            if (ffurl_read_complete(h, buf, 1) != 1)
169
                return AVERROR(EIO);
170
            size++;
171
            type = buf[0];
172
            if (hdr == RTMP_PS_TWELVEBYTES) {
173
                if (ffurl_read_complete(h, buf, 4) != 4)
174
                    return AVERROR(EIO);
175
                size += 4;
176 177 178
                extra = AV_RL32(buf);
            }
        }
179
        if (timestamp == 0xFFFFFF) {
180
            if (ffurl_read_complete(h, buf, 4) != 4)
181 182 183
                return AVERROR(EIO);
            timestamp = AV_RB32(buf);
        }
184
    }
185 186 187
    if (hdr != RTMP_PS_TWELVEBYTES)
        timestamp += prev_pkt[channel_id].timestamp;

188 189 190
    if ((ret = ff_rtmp_packet_create(p, channel_id, type, timestamp,
                                     data_size)) < 0)
        return ret;
191 192 193 194 195
    p->extra = extra;
    // save history
    prev_pkt[channel_id].channel_id = channel_id;
    prev_pkt[channel_id].type       = type;
    prev_pkt[channel_id].data_size  = data_size;
196
    prev_pkt[channel_id].ts_delta   = timestamp - prev_pkt[channel_id].timestamp;
197 198 199 200
    prev_pkt[channel_id].timestamp  = timestamp;
    prev_pkt[channel_id].extra      = extra;
    while (data_size > 0) {
        int toread = FFMIN(data_size, chunk_size);
201
        if (ffurl_read_complete(h, p->data + offset, toread) != toread) {
202 203 204 205 206
            ff_rtmp_packet_destroy(p);
            return AVERROR(EIO);
        }
        data_size -= chunk_size;
        offset    += chunk_size;
207
        size      += chunk_size;
208
        if (data_size > 0) {
209 210 211 212
            if ((ret = ffurl_read_complete(h, &t, 1)) < 0) { // marker
                ff_rtmp_packet_destroy(p);
                return ret;
            }
213
            size++;
214 215 216 217
            if (t != (0xC0 + channel_id))
                return -1;
        }
    }
218
    return size;
219 220 221 222 223 224 225 226
}

int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
                         int chunk_size, RTMPPacket *prev_pkt)
{
    uint8_t pkt_hdr[16], *p = pkt_hdr;
    int mode = RTMP_PS_TWELVEBYTES;
    int off = 0;
227
    int size = 0;
228
    int ret;
229

230
    pkt->ts_delta = pkt->timestamp - prev_pkt[pkt->channel_id].timestamp;
231

232 233 234 235 236 237 238 239 240 241 242 243 244
    //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) {
        if (pkt->type == prev_pkt[pkt->channel_id].type &&
            pkt->data_size == prev_pkt[pkt->channel_id].data_size) {
            mode = RTMP_PS_FOURBYTES;
            if (pkt->ts_delta == prev_pkt[pkt->channel_id].ts_delta)
                mode = RTMP_PS_ONEBYTE;
        } else {
            mode = RTMP_PS_EIGHTBYTES;
        }
    }

245 246 247 248 249 250 251 252 253
    if (pkt->channel_id < 64) {
        bytestream_put_byte(&p, pkt->channel_id | (mode << 6));
    } else if (pkt->channel_id < 64 + 256) {
        bytestream_put_byte(&p, 0               | (mode << 6));
        bytestream_put_byte(&p, pkt->channel_id - 64);
    } else {
        bytestream_put_byte(&p, 1               | (mode << 6));
        bytestream_put_le16(&p, pkt->channel_id - 64);
    }
254
    if (mode != RTMP_PS_ONEBYTE) {
255 256
        uint32_t timestamp = pkt->timestamp;
        if (mode != RTMP_PS_TWELVEBYTES)
257
            timestamp = pkt->ts_delta;
258
        bytestream_put_be24(&p, timestamp >= 0xFFFFFF ? 0xFFFFFF : timestamp);
259 260 261 262 263 264
        if (mode != RTMP_PS_FOURBYTES) {
            bytestream_put_be24(&p, pkt->data_size);
            bytestream_put_byte(&p, pkt->type);
            if (mode == RTMP_PS_TWELVEBYTES)
                bytestream_put_le32(&p, pkt->extra);
        }
265 266
        if (timestamp >= 0xFFFFFF)
            bytestream_put_be32(&p, timestamp);
267
    }
268 269 270 271 272 273 274 275 276 277 278 279
    // 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].data_size  = pkt->data_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].extra      = pkt->extra;

280 281
    if ((ret = ffurl_write(h, pkt_hdr, p - pkt_hdr)) < 0)
        return ret;
282
    size = p - pkt_hdr + pkt->data_size;
283 284
    while (off < pkt->data_size) {
        int towrite = FFMIN(chunk_size, pkt->data_size - off);
285 286
        if ((ret = ffurl_write(h, pkt->data + off, towrite)) < 0)
            return ret;
287 288 289
        off += towrite;
        if (off < pkt->data_size) {
            uint8_t marker = 0xC0 | pkt->channel_id;
290 291
            if ((ret = ffurl_write(h, &marker, 1)) < 0)
                return ret;
292
            size++;
293 294
        }
    }
295
    return size;
296 297 298 299 300
}

int ff_rtmp_packet_create(RTMPPacket *pkt, int channel_id, RTMPPacketType type,
                          int timestamp, int size)
{
301
    if (size) {
Martin Storsjö's avatar
Martin Storsjö committed
302 303 304
        pkt->data = av_malloc(size);
        if (!pkt->data)
            return AVERROR(ENOMEM);
305
    }
306 307 308 309 310
    pkt->data_size  = size;
    pkt->channel_id = channel_id;
    pkt->type       = type;
    pkt->timestamp  = timestamp;
    pkt->extra      = 0;
311
    pkt->ts_delta   = 0;
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

    return 0;
}

void ff_rtmp_packet_destroy(RTMPPacket *pkt)
{
    if (!pkt)
        return;
    av_freep(&pkt->data);
    pkt->data_size = 0;
}

int ff_amf_tag_size(const uint8_t *data, const uint8_t *data_end)
{
    const uint8_t *base = data;

    if (data >= data_end)
        return -1;
    switch (*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;
    case AMF_DATA_TYPE_OBJECT:
        for (;;) {
            int size = bytestream_get_be16(&data);
            int t;
            if (!size) {
                data++;
                break;
            }
            if (data + size >= data_end || data + size < data)
                return -1;
            data += size;
            t = ff_amf_tag_size(data, data_end);
            if (t < 0 || data + t >= data_end)
                return -1;
            data += t;
        }
        return data - base;
    case AMF_DATA_TYPE_OBJECT_END:  return 1;
    default:                        return -1;
    }
}

int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end,
                           const uint8_t *name, uint8_t *dst, int dst_size)
{
    int namelen = strlen(name);
    int len;

366 367 368 369 370 371
    while (*data != AMF_DATA_TYPE_OBJECT && data < data_end) {
        len = ff_amf_tag_size(data, data_end);
        if (len < 0)
            len = data_end - data;
        data += len;
    }
372 373
    if (data_end - data < 3)
        return -1;
374
    data++;
375 376 377 378 379 380 381 382 383 384
    for (;;) {
        int size = bytestream_get_be16(&data);
        if (!size)
            break;
        if (data + size >= data_end || data + size < data)
            return -1;
        data += size;
        if (size == namelen && !memcmp(data-size, name, namelen)) {
            switch (*data++) {
            case AMF_DATA_TYPE_NUMBER:
385
                snprintf(dst, dst_size, "%g", av_int2double(AV_RB64(data)));
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
                break;
            case AMF_DATA_TYPE_BOOL:
                snprintf(dst, dst_size, "%s", *data ? "true" : "false");
                break;
            case AMF_DATA_TYPE_STRING:
                len = bytestream_get_be16(&data);
                av_strlcpy(dst, data, FFMIN(len+1, dst_size));
                break;
            default:
                return -1;
            }
            return 0;
        }
        len = ff_amf_tag_size(data, data_end);
        if (len < 0 || data + len >= data_end || data + len < data)
            return -1;
        data += len;
    }
    return -1;
}
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

static const char* rtmp_packet_type(int type)
{
    switch (type) {
    case RTMP_PT_CHUNK_SIZE:     return "chunk size";
    case RTMP_PT_BYTES_READ:     return "bytes read";
    case RTMP_PT_PING:           return "ping";
    case RTMP_PT_SERVER_BW:      return "server bandwidth";
    case RTMP_PT_CLIENT_BW:      return "client bandwidth";
    case RTMP_PT_AUDIO:          return "audio packet";
    case RTMP_PT_VIDEO:          return "video packet";
    case RTMP_PT_FLEX_STREAM:    return "Flex shared stream";
    case RTMP_PT_FLEX_OBJECT:    return "Flex shared object";
    case RTMP_PT_FLEX_MESSAGE:   return "Flex shared message";
    case RTMP_PT_NOTIFY:         return "notification";
    case RTMP_PT_SHARED_OBJ:     return "shared object";
    case RTMP_PT_INVOKE:         return "invoke";
    case RTMP_PT_METADATA:       return "metadata";
    default:                     return "unknown";
    }
}

static void ff_amf_tag_contents(void *ctx, const uint8_t *data, const uint8_t *data_end)
{
430
    int size;
431 432 433 434 435 436
    char buf[1024];

    if (data >= data_end)
        return;
    switch (*data++) {
    case AMF_DATA_TYPE_NUMBER:
437
        av_log(ctx, AV_LOG_DEBUG, " number %g\n", av_int2double(AV_RB64(data)));
438 439 440 441 442 443 444 445 446
        return;
    case AMF_DATA_TYPE_BOOL:
        av_log(ctx, AV_LOG_DEBUG, " bool %d\n", *data);
        return;
    case AMF_DATA_TYPE_STRING:
    case AMF_DATA_TYPE_LONG_STRING:
        if (data[-1] == AMF_DATA_TYPE_STRING) {
            size = bytestream_get_be16(&data);
        } else {
447
            size = bytestream_get_be32(&data);
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
        }
        size = FFMIN(size, 1023);
        memcpy(buf, data, size);
        buf[size] = 0;
        av_log(ctx, AV_LOG_DEBUG, " string '%s'\n", buf);
        return;
    case AMF_DATA_TYPE_NULL:
        av_log(ctx, AV_LOG_DEBUG, " NULL\n");
        return;
    case AMF_DATA_TYPE_ARRAY:
        data += 4;
    case AMF_DATA_TYPE_OBJECT:
        av_log(ctx, AV_LOG_DEBUG, " {\n");
        for (;;) {
            int size = bytestream_get_be16(&data);
            int t;
            memcpy(buf, data, size);
            buf[size] = 0;
            if (!size) {
                av_log(ctx, AV_LOG_DEBUG, " }\n");
                data++;
                break;
            }
            if (data + size >= data_end || data + size < data)
                return;
            data += size;
            av_log(ctx, AV_LOG_DEBUG, "  %s: ", buf);
            ff_amf_tag_contents(ctx, data, data_end);
            t = ff_amf_tag_size(data, data_end);
            if (t < 0 || data + t >= data_end)
                return;
            data += t;
        }
        return;
    case AMF_DATA_TYPE_OBJECT_END:
        av_log(ctx, AV_LOG_DEBUG, " }\n");
        return;
    default:
        return;
    }
}

void ff_rtmp_packet_dump(void *ctx, RTMPPacket *p)
{
    av_log(ctx, AV_LOG_DEBUG, "RTMP packet type '%s'(%d) for channel %d, timestamp %d, extra field %d size %d\n",
           rtmp_packet_type(p->type), p->type, p->channel_id, p->timestamp, p->extra, p->data_size);
    if (p->type == RTMP_PT_INVOKE || p->type == RTMP_PT_NOTIFY) {
        uint8_t *src = p->data, *src_end = p->data + p->data_size;
        while (src < src_end) {
            int sz;
            ff_amf_tag_contents(ctx, src, src_end);
            sz = ff_amf_tag_size(src, src_end);
            if (sz < 0)
                break;
            src += sz;
        }
    } else if (p->type == RTMP_PT_SERVER_BW){
        av_log(ctx, AV_LOG_DEBUG, "Server BW = %d\n", AV_RB32(p->data));
    } else if (p->type == RTMP_PT_CLIENT_BW){
        av_log(ctx, AV_LOG_DEBUG, "Client BW = %d\n", AV_RB32(p->data));
    } else if (p->type != RTMP_PT_AUDIO && p->type != RTMP_PT_VIDEO && p->type != RTMP_PT_METADATA) {
        int i;
        for (i = 0; i < p->data_size; i++)
            av_log(ctx, AV_LOG_DEBUG, " %02X", p->data[i]);
        av_log(ctx, AV_LOG_DEBUG, "\n");
    }
}