[FFmpeg-soc] [soc]: r4382 - in rtmp: TODO checkout.sh rtmp.h rtmp.patch rtmpdec.c rtmppkt.c rtmppkt.h rtmpproto.c

kostya subversion at mplayerhq.hu
Thu Jun 4 08:57:37 CEST 2009


Author: kostya
Date: Thu Jun  4 08:57:37 2009
New Revision: 4382

Log:
Some initial work on RTMP client.

Added:
   rtmp/TODO
   rtmp/checkout.sh   (contents, props changed)
   rtmp/rtmp.h
   rtmp/rtmp.patch
   rtmp/rtmpdec.c
   rtmp/rtmppkt.c
   rtmp/rtmppkt.h
   rtmp/rtmpproto.c

Added: rtmp/TODO
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/TODO	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,4 @@
+Proper handshake
+Proper "connect" call (additional parameters and filename parsing)
+Factorize a lot of FLV demuxer code
+Set correct timestamps

Added: rtmp/checkout.sh
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/checkout.sh	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+echo "Checking out FFmpeg SVN trunk code..."
+svn co svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg && \
+cd ffmpeg
+
+echo "Patching build system"
+patch -p1 < ../rtmp.patch && \
+cd libavformat
+
+echo "Copying source code to libavformat dir"
+cp ../../rtmp*.[ch] .
+
+echo "Done! Now enter the ffmpeg dir, configure and make FFmpeg with some RTMP support"

Added: rtmp/rtmp.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmp.h	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,39 @@
+/*
+ * RTMP definitions
+ * Copyright (c) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * 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.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_RTMP_H
+#define AVFORMAT_RTMP_H
+#include "avformat.h"
+
+/** RTMP default port */
+#define RTMP_DEFAULT_PORT 1935
+
+/** RTMP handshake data size */
+#define RTMP_HANDSHAKE_PACKET_SIZE 1536
+
+#define RTMP_CLIENT_VER1    9
+#define RTMP_CLIENT_VER2    0
+#define RTMP_CLIENT_VER3  124
+#define RTMP_CLIENT_VER4    2
+
+#define RTMP_CLIENT_VER_STR  "LNX" #RTMP_CLIENT_VER1 "," #RTMP_CLIENT_VER2
+
+#endif /* AVFORMAT_RTMP_H */

Added: rtmp/rtmp.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmp.patch	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,40 @@
+Index: libavformat/Makefile
+===================================================================
+--- libavformat/Makefile	(revision 18770)
++++ libavformat/Makefile	(working copy)
+@@ -187,6 +187,7 @@
+ OBJS-$(CONFIG_ROQ_DEMUXER)               += idroq.o
+ OBJS-$(CONFIG_ROQ_MUXER)                 += raw.o
+ OBJS-$(CONFIG_RPL_DEMUXER)               += rpl.o
++OBJS-$(CONFIG_RTMP_DEMUXER)              += rtmpdec.o rtmppkt.o
+ OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o         \
+                                             rtp_aac.o     \
+                                             rtp_amr.o     \
+@@ -243,6 +244,7 @@
+ OBJS-$(CONFIG_GOPHER_PROTOCOL)           += gopher.o
+ OBJS-$(CONFIG_HTTP_PROTOCOL)             += http.o
+ OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
++OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o
+ OBJS-$(CONFIG_RTP_PROTOCOL)              += rtpproto.o
+ OBJS-$(CONFIG_TCP_PROTOCOL)              += tcp.o
+ OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o
+Index: libavformat/allformats.c
+===================================================================
+--- libavformat/allformats.c	(revision 18770)
++++ libavformat/allformats.c	(working copy)
+@@ -164,6 +164,7 @@
+     REGISTER_MUXDEMUX (RM, rm);
+     REGISTER_MUXDEMUX (ROQ, roq);
+     REGISTER_DEMUXER  (RPL, rpl);
++    REGISTER_DEMUXER  (RTMP, rtmp);
+     REGISTER_MUXER    (RTP, rtp);
+     REGISTER_DEMUXER  (RTSP, rtsp);
+     REGISTER_DEMUXER  (SDP, sdp);
+@@ -207,6 +208,7 @@
+     REGISTER_PROTOCOL (GOPHER, gopher);
+     REGISTER_PROTOCOL (HTTP, http);
+     REGISTER_PROTOCOL (PIPE, pipe);
++    REGISTER_PROTOCOL (RTMP, rtmp);
+     REGISTER_PROTOCOL (RTP, rtp);
+     REGISTER_PROTOCOL (TCP, tcp);
+     REGISTER_PROTOCOL (UDP, udp);

Added: rtmp/rtmpdec.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmpdec.c	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,350 @@
+/*
+ * RTMP input format
+ * Copyright (c) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * 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.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* needed for gethostname() */
+#define _XOPEN_SOURCE 600
+
+#include "libavcodec/bytestream.h"
+#include "libavutil/avstring.h"
+#include "avformat.h"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include "network.h"
+
+#include "flv.h"
+#include "rtmp.h"
+#include "rtmppkt.h"
+
+#define DEBUG 1
+
+typedef struct RTMPState {
+    URLContext *rtmp_hd;
+    RTMPPacketHistory rhist, whist;
+} RTMPState;
+
+static void gen_connect(AVFormatContext *s, RTMPState *rt, const char *name)
+{
+    uint8_t buf[65536], *p = buf;
+    double num = 1.0;
+    int size;
+
+    bytestream_put_byte(&p, 0x3);
+    bytestream_put_be24(&p, 1);
+    bytestream_put_be24(&p, 0);
+    bytestream_put_byte(&p, RTMP_PT_INVOKE);
+    bytestream_put_le32(&p, 0);
+
+    rtmp_amf_write_tag(&p, AMF_STRING, "connect");
+    rtmp_amf_write_tag(&p, AMF_NUMBER, &num);
+    rtmp_amf_write_tag(&p, AMF_OBJECT, NULL);
+    rtmp_amf_write_tag(&p, AMF_STRING_IN_OBJECT, "tcUrl");
+    rtmp_amf_write_tag(&p, AMF_STRING, name);
+    rtmp_amf_write_tag(&p, AMF_STRING_IN_OBJECT, "app");
+    rtmp_amf_write_tag(&p, AMF_STRING, "app");
+    rtmp_amf_write_tag(&p, AMF_STRING_IN_OBJECT, "flashVer");
+    rtmp_amf_write_tag(&p, AMF_STRING, "LNX 6,0,82,0");
+    rtmp_amf_write_tag(&p, AMF_STRING_IN_OBJECT, "playpath");
+    rtmp_amf_write_tag(&p, AMF_STRING, "1.mp4");
+    rtmp_amf_write_tag(&p, AMF_OBJECT_END, NULL);
+
+    size = p - buf;
+    p = buf + 4;
+    bytestream_put_be24(&p, size - 12);
+
+    url_write(rt->rtmp_hd, buf, size);
+}
+
+static void gen_play(AVFormatContext *s, RTMPState *rt)
+{
+    RTMPPacket pkt;
+    uint8_t *p;
+    double num;
+
+    rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 25);
+
+    num = 3.0;
+    p = pkt.data;
+    rtmp_amf_write_tag(&p, AMF_STRING, "createStream");
+    rtmp_amf_write_tag(&p, AMF_NUMBER, &num);
+    rtmp_amf_write_tag(&p, AMF_NULL, NULL);
+
+    rtmp_packet_write(s, rt->rtmp_hd, &pkt, &rt->whist);
+    rtmp_packet_destroy(&pkt);
+
+    rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 17);
+
+    num = 0.0;
+    p = pkt.data;
+    rtmp_amf_write_tag(&p, AMF_STRING, "play");
+    rtmp_amf_write_tag(&p, AMF_NUMBER, &num);
+    rtmp_amf_write_tag(&p, AMF_NULL, NULL);
+
+    rtmp_packet_write(s, rt->rtmp_hd, &pkt, &rt->whist);
+    rtmp_packet_destroy(&pkt);
+}
+
+static int rtmp_handshake(AVFormatContext *s, RTMPState *rt)
+{
+    uint8_t buf [1+RTMP_HANDSHAKE_PACKET_SIZE];
+    uint8_t buf2[1+RTMP_HANDSHAKE_PACKET_SIZE*2];
+    int i;
+
+    av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
+
+    // generate handshake packet - 1536 bytes of pseudorandom data
+    buf[0] = 3; //unencrypted data
+    memset(buf+1, 0, RTMP_HANDSHAKE_PACKET_SIZE);
+    //write client "version"
+    buf[5] = 9;
+    buf[5] = 0;
+    buf[7] = 124;
+    buf[8] = 2;
+
+    url_write(rt->rtmp_hd, buf, RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    i = url_read_complete(rt->rtmp_hd, buf2, RTMP_HANDSHAKE_PACKET_SIZE*2 + 1);
+    if (i != 1 + RTMP_HANDSHAKE_PACKET_SIZE*2 || memcmp(buf + 1, buf2 + 1 + RTMP_HANDSHAKE_PACKET_SIZE, RTMP_HANDSHAKE_PACKET_SIZE)) {
+        av_log(s, AV_LOG_ERROR, "RTMP handshake failed\n");
+        return -1;
+    }
+    // write reply back to server
+    url_write(rt->rtmp_hd, buf2 + 1, RTMP_HANDSHAKE_PACKET_SIZE);
+
+    return 0;
+}
+
+static void rtmp_init_hist(RTMPPacketHistory *hist)
+{
+    int i;
+
+    for (i = 0; i < RTMP_CHANNELS; i++) {
+        hist->chunk_size[i] = (i == RTMP_AUDIO_CHANNEL) ? 64 : 128;
+    }
+}
+
+static int rtmp_probe(AVProbeData *p)
+{
+    if (av_strstart(p->filename, "rtmp:", NULL))
+        return AVPROBE_SCORE_MAX;
+    return 0;
+}
+
+static int rtmp_read_header(AVFormatContext *s,
+                            AVFormatParameters *ap)
+{
+    RTMPState *rt = s->priv_data;
+    char hostname[256];
+    int port;
+    uint8_t buf[65536];
+    AVStream *st;
+
+    url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
+              NULL, 0, s->filename);
+
+    rtmp_init_hist(&rt->rhist);
+    rtmp_init_hist(&rt->whist);
+    if(port == -1)
+        port = RTMP_DEFAULT_PORT;
+    snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
+    url_open(&rt->rtmp_hd, buf, URL_RDWR);
+    if (rtmp_handshake(s, rt))
+        return -1;
+    gen_connect(s, rt, s->filename);
+    gen_play(s, rt);
+
+    st = av_new_stream(s, 0);
+    if (!st)
+        return -1;
+    st->codec->codec_type = CODEC_TYPE_VIDEO;
+    av_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
+
+    st = av_new_stream(s, 1);
+    if (!st)
+        return -1;
+    st->codec->codec_type = CODEC_TYPE_AUDIO;
+    av_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
+
+    return 0;
+}
+
+static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, int flv_codecid) {
+    AVCodecContext *acodec = astream->codec;
+    switch(flv_codecid) {
+        //no distinction between S16 and S8 PCM codec flags
+        case FLV_CODECID_PCM:
+            acodec->codec_id = acodec->bits_per_coded_sample == 8 ? CODEC_ID_PCM_S8 :
+#ifdef WORDS_BIGENDIAN
+                                CODEC_ID_PCM_S16BE;
+#else
+                                CODEC_ID_PCM_S16LE;
+#endif
+            break;
+        case FLV_CODECID_PCM_LE:
+            acodec->codec_id = acodec->bits_per_coded_sample == 8 ? CODEC_ID_PCM_S8 : CODEC_ID_PCM_S16LE; break;
+        case FLV_CODECID_AAC  : acodec->codec_id = CODEC_ID_AAC;                                    break;
+        case FLV_CODECID_ADPCM: acodec->codec_id = CODEC_ID_ADPCM_SWF;                              break;
+        case FLV_CODECID_SPEEX:
+            acodec->codec_id = CODEC_ID_SPEEX;
+            acodec->sample_rate = 16000;
+            break;
+        case FLV_CODECID_MP3  : acodec->codec_id = CODEC_ID_MP3      ; astream->need_parsing = AVSTREAM_PARSE_FULL; break;
+        case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
+            acodec->sample_rate = 8000; //in case metadata does not otherwise declare samplerate
+        case FLV_CODECID_NELLYMOSER:
+            acodec->codec_id = CODEC_ID_NELLYMOSER;
+            break;
+        default:
+            av_log(s, AV_LOG_INFO, "Unsupported audio codec (%x)\n", flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
+            acodec->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
+    }
+}
+
+static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_codecid) {
+    AVCodecContext *vcodec = vstream->codec;
+    switch(flv_codecid) {
+        case FLV_CODECID_H263  : vcodec->codec_id = CODEC_ID_FLV1   ; break;
+        case FLV_CODECID_SCREEN: vcodec->codec_id = CODEC_ID_FLASHSV; break;
+        case FLV_CODECID_VP6   : vcodec->codec_id = CODEC_ID_VP6F   ;
+        case FLV_CODECID_VP6A  :
+            if(flv_codecid == FLV_CODECID_VP6A)
+                vcodec->codec_id = CODEC_ID_VP6A;
+            if(vcodec->extradata_size != 1) {
+                vcodec->extradata_size = 1;
+                vcodec->extradata = av_malloc(1);
+            }
+            vcodec->extradata[0] = get_byte(s->pb);
+            return 1; // 1 byte body size adjustment for flv_read_packet()
+        case FLV_CODECID_H264:
+            vcodec->codec_id = CODEC_ID_H264;
+            return 3; // not 4, reading packet type will consume one byte
+        default:
+            av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid);
+            vcodec->codec_tag = flv_codecid;
+    }
+
+    return 0;
+}
+
+static int rtmp_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    RTMPState *rt = s->priv_data;
+    AVStream *st = NULL;
+    struct timespec ts;
+    int i, ret;
+
+    ts.tv_sec = 0;
+    ts.tv_nsec = 500000000;
+    for (;;) {
+        RTMPPacket rpkt;
+        int i;
+        if ((ret = rtmp_packet_read(s, rt->rtmp_hd, &rpkt, &rt->rhist)) != 0) {
+            if (ret > 0) {
+                nanosleep(&ts, NULL);
+                continue;
+            } else {
+                return AVERROR(EIO);
+            }
+        }
+
+#ifdef DEBUG
+if(rpkt.type != RTMP_PT_VIDEO && rpkt.type != RTMP_PT_AUDIO) rtmp_packet_inspect(s, &rpkt);
+//if(rpkt.data_size)for(i = 0; i < rpkt.data_size;i++)av_log(NULL,0," %02X",rpkt.data[i]);av_log(NULL,0,"\n");
+#endif
+
+        if (rpkt.type == RTMP_PT_CHUNK_SIZE)
+            for(i=0;i<RTMP_CHANNELS;i++)rt->rhist.chunk_size[i/*rpkt.stream_id*/] = AV_RB32(rpkt.data);
+        if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO) {
+            int is_audio = rpkt.type == RTMP_PT_AUDIO;
+            int flags;
+            int off = 1;
+
+            flags = rpkt.data[0];
+            for (i = 0; i < s->nb_streams; i++) {
+                st = s->streams[i];
+                if (st->id == is_audio)
+                    break;
+            }
+            if (is_audio && (!st->codec->channels || !st->codec->sample_rate || !st->codec->bits_per_coded_sample)) {
+                st->codec->channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
+                st->codec->sample_rate = (44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >> FLV_AUDIO_SAMPLERATE_OFFSET) >> 3);
+                st->codec->bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
+            }
+            if (!st->codec->codec_id) {
+                if (is_audio)
+                    flv_set_audio_codec(s, st, flags & FLV_AUDIO_CODECID_MASK);
+                else
+                    flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK);
+            }
+            if (st->codec->codec_id == CODEC_ID_AAC ||
+                st->codec->codec_id == CODEC_ID_H264) {
+                int type = rpkt.data[off++];
+
+                if (st->codec->codec_id == CODEC_ID_H264) {
+                    off += 3;
+                }
+                if (!type) {
+                    av_free(st->codec->extradata);
+                    st->codec->extradata = av_mallocz(rpkt.data_size - off + FF_INPUT_BUFFER_PADDING_SIZE);
+                    if (!st->codec->extradata) {
+                        av_log(s, AV_LOG_ERROR, "Cannot allocate extradata\n");
+                        return AVERROR(ENOMEM);
+                    }
+                    st->codec->extradata_size = rpkt.data_size - off;
+                    memcpy(st->codec->extradata, rpkt.data + off, rpkt.data_size - off);
+                    rtmp_packet_destroy(&rpkt);
+                    continue;
+                }
+            }
+            if (off < rpkt.data_size) {
+                if (av_new_packet(pkt, rpkt.data_size - off) < 0) {
+                    rtmp_packet_destroy(&rpkt);
+                    return -1;
+                }
+                memcpy(pkt->data, rpkt.data + off, rpkt.data_size - off);
+                pkt->stream_index = st->index;
+                //pkt->pts = rpkt.timestamp;
+                rtmp_packet_destroy(&rpkt);
+
+                return pkt->size;
+            }
+        }
+        rtmp_packet_destroy(&rpkt);
+    }
+    return AVERROR(EIO);
+}
+
+static int rtmp_read_close(AVFormatContext *s)
+{
+    RTMPState *rt = s->priv_data;
+
+    url_close(rt->rtmp_hd);
+    return 0;
+}
+
+AVInputFormat rtmp_demuxer = {
+    "rtmp",
+    NULL_IF_CONFIG_SMALL("RTMP"),
+    sizeof(RTMPState),
+    rtmp_probe,
+    rtmp_read_header,
+    rtmp_read_packet,
+    rtmp_read_close,
+};

Added: rtmp/rtmppkt.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmppkt.c	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,267 @@
+/*
+ * RTMP input format
+ * Copyright (c) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * 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.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* needed for gethostname() */
+#define _XOPEN_SOURCE 600
+
+#include "libavcodec/bytestream.h"
+#include "libavutil/avstring.h"
+#include "avformat.h"
+
+#include "rtmppkt.h"
+
+static const int8_t amf_tag_sizes[17] = {
+    8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0
+};
+
+int rtmp_amf_tag_size(int type, const void *data)
+{
+    if (type == AMF_STRING_IN_OBJECT || type == AMF_STRING)
+        return strlen(data) + 2;
+    if (type > AMF_TYPED_OBJECT)
+        return -1;
+    if (type == AMF_LONG_STRING)
+        return strlen(data) + 4;
+    return amf_tag_sizes[type];
+}
+
+void rtmp_amf_write_tag(uint8_t **dst, AMFType type, const void *data)
+{
+    if (type != AMF_OBJECT_END && type != AMF_STRING_IN_OBJECT)
+        bytestream_put_byte(dst, type);
+    switch(type){
+    case AMF_NUMBER:
+        bytestream_put_be64(dst, av_dbl2int(*(const double*)data));
+        break;
+    case AMF_BOOLEAN:
+        bytestream_put_byte(dst, *(const uint8_t*)data);
+        break;
+    case AMF_STRING:
+    case AMF_STRING_IN_OBJECT:
+        bytestream_put_be16(dst, strlen(data));
+        bytestream_put_buffer(dst, data, strlen(data));
+        break;
+    case AMF_OBJECT_END:
+        bytestream_put_be24(dst, AMF_OBJECT_END);
+        break;
+    case AMF_STRICT_ARRAY:
+        bytestream_put_be32(dst, *(const uint32_t*)data);
+        break;
+    }
+}
+
+int rtmp_packet_read(AVFormatContext *ctx, URLContext *h, RTMPPacket *p,
+                     RTMPPacketHistory *hist)
+{
+    uint8_t hdr, t, buf[16];
+    int stream_id, timestamp, data_size, offset = 0;
+    uint8_t type;
+
+    if (url_read(h, &hdr, 1) != 1) {
+        av_log(ctx, AV_LOG_ERROR, "Cannot read packet header\n");
+        return -1;
+    }
+    stream_id = hdr & 0x3F;
+
+    hdr >>= 6;
+    if (hdr == RTMP_PS_ONEBYTE) {
+        av_log(ctx, AV_LOG_ERROR, "Onebyte header!\n\n\n");
+        //todo
+        return -1;
+    } else {
+        if (url_read_complete(h, buf, 3) != 3) {
+            av_log(ctx, AV_LOG_ERROR, "reading timestamp failed\n");
+            return -1;
+        }
+        timestamp = AV_RB24(buf);
+        if (hdr != RTMP_PS_FOURBYTES) {
+            if (url_read_complete(h, buf, 3) != 3) {
+                av_log(ctx, AV_LOG_ERROR, "reading size failed\n");
+                return -1;
+            }
+            data_size = AV_RB24(buf);
+            if (url_read_complete(h, &type, 1) != 1) {
+                av_log(ctx, AV_LOG_ERROR, "reading type failed\n");
+                return -1;
+            }
+            if (hdr == RTMP_PS_TWELVEBYTES)
+                if (url_read(h, buf, 4) != 4) {
+                    av_log(ctx, AV_LOG_ERROR, "reading timestamp2 failed\n");
+                    return -1;
+                }
+        } else {
+            av_log(ctx, AV_LOG_ERROR, "Fourbytes!\n\n\n");
+            //todo
+            return -1;
+        }
+    }
+    rtmp_packet_create(p, stream_id, type, type, data_size);
+    while (data_size > 0) {
+        int toread = FFMIN(data_size, hist->chunk_size[stream_id]);
+        int r;
+        if ((r = url_read_complete(h, p->data + offset, toread)) != toread) {
+            av_log(ctx, AV_LOG_ERROR, "Need %d read %d\n", toread, r);
+            rtmp_packet_destroy(p);
+            return -1;
+        }
+        data_size -= hist->chunk_size[stream_id];
+        offset    += hist->chunk_size[stream_id];
+        if (data_size > 0) {
+            url_read_complete(h, &t, 1); //marker
+        }
+    }
+    return 0;
+}
+
+int rtmp_packet_write(AVFormatContext *ctx, URLContext *h, RTMPPacket *pkt,
+                      RTMPPacketHistory *hist)
+{
+    uint8_t pkt_hdr[16], *p = pkt_hdr;
+    int mode = RTMP_PS_TWELVEBYTES;
+
+    if (pkt->type != RTMP_PT_INVOKE)
+        mode = RTMP_PS_EIGHTBYTES;
+    bytestream_put_byte(&p, pkt->stream_id | (mode << 6));
+    if (mode != RTMP_PS_ONEBYTE) {
+        bytestream_put_be24(&p, pkt->timestamp);
+        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, 0); //FIXME put real data here
+        }
+    }
+    url_write(h, pkt_hdr, p-pkt_hdr);
+    url_write(h, pkt->data, pkt->data_size);
+    return 0;
+}
+
+int rtmp_packet_create(RTMPPacket *pkt, int stream_id, RTMPPacketType type,
+                       int timestamp, int size)
+{
+    pkt->data = av_malloc(size);
+    if (!pkt->data)
+        return -1;
+    pkt->data_size = size;
+    pkt->stream_id = stream_id;
+    pkt->type      = type;
+    pkt->timestamp = timestamp;
+
+    return 0;
+}
+
+void rtmp_packet_destroy(RTMPPacket *pkt)
+{
+    if (!pkt)
+        return;
+    av_freep(&pkt->data);
+    pkt->data_size = 0;
+}
+
+
+static void parse_amf(const uint8_t *data, int size)
+{
+    const uint8_t *ptr = data, *end = data+size;
+    int object = 0,array=0,i,len;
+    char buf[65536];
+    while(ptr < end){
+        if(object){
+            len=bytestream_get_be16(&ptr);
+            if(len){
+            for(i=0;i<object;i++)av_log(NULL,0,"    ");
+            bytestream_get_buffer(&ptr, buf, len);
+            buf[len] = 0;
+            av_log(NULL,0,"%s: ",buf);
+            }
+        }
+        switch (*ptr++) {
+        case AMF_NUMBER:
+            {
+                double d;
+                d = av_int2dbl(bytestream_get_be64(&ptr));
+                av_log(NULL,0,"Number %g\n",d);
+            }
+            break;
+        case AMF_BOOLEAN:
+            av_log(NULL,0,"Boolean %d\n",*ptr++);
+            break;
+        case AMF_STRING:
+            len=bytestream_get_be16(&ptr);
+            bytestream_get_buffer(&ptr, buf, len);
+            buf[len] = 0;
+            av_log(NULL,0,"String '%s'\n",buf);
+            break;
+        case AMF_NULL:
+            av_log(NULL,0,"NULL\n");
+            break;
+        case AMF_OBJECT:
+            for(i=0;i<object;i++)av_log(NULL,0,"    ");
+            av_log(NULL,0,"Object{\n");
+            object++;
+            break;
+        case AMF_ECMA_ARRAY:
+            array = bytestream_get_be32(&ptr);
+            av_log(NULL,0,"Array of %d elements: [\n",array);
+            object++;
+            break;
+        case AMF_OBJECT_END:
+            object--;
+            for(i=0;i<object;i++)av_log(NULL,0,"    ");
+            if(array){
+            array = 0;
+            av_log(NULL,0,"]\n");
+            }else
+            av_log(NULL,0,"}\n");
+            break;
+        default:
+            av_log(NULL,0,"Type %02X\n",ptr[-1]);
+            return;
+        }
+    }
+}
+
+void rtmp_packet_inspect(AVFormatContext *ctx, RTMPPacket *pkt)
+{
+    av_log(NULL,0,"Packet on ");
+    switch (pkt->stream_id) {
+    case RTMP_NETWORK_CHANNEL: av_log(NULL,0,"network channel");break;
+    case RTMP_SYSTEM_CHANNEL:  av_log(NULL,0,"system channel");break;
+    case RTMP_VIDEO_CHANNEL:   av_log(NULL,0,"video channel");break;
+    case RTMP_AUDIO_CHANNEL:   av_log(NULL,0,"audio channel");break;
+    default:                   av_log(NULL,0,"channel %d",pkt->stream_id);
+    }
+    av_log(NULL,0," type ");
+    switch (pkt->type) {
+    case RTMP_PT_CHUNK_SIZE:   av_log(NULL,0,"chunk size %d",AV_RB32(pkt->data));break;
+    case RTMP_PT_BYTES_READ:   av_log(NULL,0,"bytes read");break;
+    case RTMP_PT_PING:         av_log(NULL,0,"ping");break;
+    case RTMP_PT_SERVER_BW:    av_log(NULL,0,"server BW=%d",AV_RB32(pkt->data));break;
+    case RTMP_PT_CLIENT_BW:    av_log(NULL,0,"client BW=%d",AV_RB32(pkt->data));break;
+    case RTMP_PT_AUDIO:        av_log(NULL,0,"audio");break;
+    case RTMP_PT_VIDEO:        av_log(NULL,0,"video");break;
+    case RTMP_PT_NOTIFY:       av_log(NULL,0,"notify");break;
+    case RTMP_PT_INVOKE:       av_log(NULL,0,"invoke");break;
+    default:                   av_log(NULL,0,"%X",pkt->type);
+    }
+    av_log(NULL,0," ts %d size %d\n",pkt->timestamp, pkt->data_size);
+    if (pkt->type == RTMP_PT_INVOKE || pkt->type == RTMP_PT_NOTIFY)
+        parse_amf(pkt->data, pkt->data_size);
+}

Added: rtmp/rtmppkt.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmppkt.h	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,135 @@
+/*
+ * RTMP packet utilities
+ * Copyright (c) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * 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.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_RTMPPKT_H
+#define AVFORMAT_RTMPPKT_H
+
+#include "avformat.h"
+
+/* maximum possible number of different RTMP channels */
+#define RTMP_CHANNELS 64
+
+enum RTMPChannel {
+    RTMP_NETWORK_CHANNEL = 2,   ///< channel for network-related messages (bandwidth report, ping, etc)
+    RTMP_SYSTEM_CHANNEL,        ///< channel for sending server control messages
+    RTMP_VIDEO_CHANNEL = 8,     ///< channel for video data
+    RTMP_AUDIO_CHANNEL,         ///< channel for audio data
+};
+
+typedef enum RTMPPacketType {
+    RTMP_PT_CHUNK_SIZE   =  1,  ///< chunk size change
+    RTMP_PT_BYTES_READ   =  3,  ///< number of bytes read
+    RTMP_PT_PING,               ///< ping
+    RTMP_PT_SERVER_BW,          ///< server bandwidth
+    RTMP_PT_CLIENT_BW,          ///< client bandwidth
+    RTMP_PT_AUDIO        =  8,  ///< audio packet
+    RTMP_PT_VIDEO,              ///< video packet
+    RTMP_PT_FLEX_STREAM  = 15,  ///< Flex shared stream
+    RTMP_PT_FLEX_OBJECT,        ///< Flex shared object
+    RTMP_PT_FLEX_MESSAGE,       ///< Flex shared message
+    RTMP_PT_NOTIFY,             ///< some notification
+    RTMP_PT_SHARED_OBJ,         ///< shared object
+    RTMP_PT_INVOKE,             ///< invoke some stream action
+    RTMP_PT_METADATA     = 22,  ///< FLV metadata
+} RTMPPacketType;
+
+typedef enum RTMPObjectType {
+    RTMP_OT_CONNECT,
+    RTMP_OT_DISCONNECT,
+    RTMP_OT_SET_ATTR,
+    RTMP_OT_UPDATE_DATA,
+    RTMP_OT_UPDATE_ATTR,
+    RTMP_OT_SEND_MSG,
+    RTMP_OT_STATUS,
+    RTMP_OT_CLEAR_DATA,
+    RTMP_OT_DELETE_DATA,
+    RTMP_OT_DELETE_ATTR,
+    RTMP_OT_INITIAL_DATA,
+} RTMPObjectType;
+
+enum RTMPPacketSize {
+    RTMP_PS_TWELVEBYTES = 0,
+    RTMP_PS_EIGHTBYTES,
+    RTMP_PS_FOURBYTES,
+    RTMP_PS_ONEBYTE
+};
+
+typedef enum AMFType {
+    AMF_NUMBER = 0,
+    AMF_BOOLEAN,
+    AMF_STRING,
+    AMF_OBJECT,
+    AMF_MOVIE,
+    AMF_NULL,
+    AMF_UNDEFINED,
+    AMF_REFERENCE,
+    AMF_ECMA_ARRAY,
+    AMF_OBJECT_END,
+    AMF_STRICT_ARRAY,
+    AMF_DATE,
+    AMF_LONG_STRING,
+    AMF_UNSUPPORTED,
+    AMD_RECORD_SET,
+    AMF_XML_OBJECT,
+    AMF_TYPED_OBJECT,
+
+    AMF_STRING_IN_OBJECT = 99,
+} AMFType;
+
+/**
+ * structure for holding RTMP packets
+ */
+typedef struct RTMPPacket {
+    uint8_t        stream_id; ///< stream ID
+    RTMPPacketType type;      ///< packet type
+    int            timestamp; ///< packet timestamp
+    uint8_t        *data;     ///< packet payload
+    int            data_size; ///< packet payload size
+} RTMPPacket;
+
+/**
+ * saved parameters for reading RTMP packets
+ *
+ * Since RTMP server may choose to send partial packet for some channel we need
+ * to set missing parameters from the previous packet on the same channel.
+ */
+typedef struct RTMPPacketHistory {
+    RTMPPacket prev_pkt[RTMP_CHANNELS];    ///< previous read packet parameters
+    int        chunk_size[RTMP_CHANNELS];  ///< chunk size for each channel
+} RTMPPacketHistory;
+
+
+int rtmp_packet_create(RTMPPacket *pkt, int stream_id, RTMPPacketType type,
+                       int timestamp, int size);
+
+void rtmp_packet_destroy(RTMPPacket *pkt);
+
+int rtmp_packet_read(AVFormatContext *ctx, URLContext *h, RTMPPacket *p, RTMPPacketHistory *hist);
+
+int rtmp_packet_write(AVFormatContext *ctx, URLContext *h, RTMPPacket *p, RTMPPacketHistory *hist);
+
+int rtmp_amf_tag_size(int type, const void *data);
+
+void rtmp_amf_write_tag(uint8_t **dst, AMFType type, const void *data);
+
+void rtmp_packet_inspect(AVFormatContext *ctx, RTMPPacket *pkt);
+
+#endif /* AVFORMAT_RTMPPKT_H */

Added: rtmp/rtmpproto.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ rtmp/rtmpproto.c	Thu Jun  4 08:57:37 2009	(r4382)
@@ -0,0 +1,117 @@
+/*
+ * RTMP network protocol
+ * Copyright (c) 2009 Kostya Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * 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.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file libavformat/rtmpproto.c
+ * RTMP protocol
+ */
+
+#include "libavutil/avstring.h"
+#include "avformat.h"
+
+#include <unistd.h>
+#include <stdarg.h>
+#include "network.h"
+#include "os_support.h"
+#include <fcntl.h>
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define RTMP_DEFAULT_PORT 1935
+#define RTMP_HANDSHAKE_PACKET_SIZE 1536
+
+typedef struct RTMPContext {
+    URLContext *rtmp_hd;
+    int wrote;
+} RTMPContext;
+
+
+/**
+ * url syntax: rtp://host:port[?option=val...]
+ * option: 'ttl=n'       : set the ttl value (for multicast only)
+ *         'localport=n' : set the local port to n
+ *
+ */
+
+static int rtmp_open(URLContext *h, const char *uri, int flags)
+{
+    RTMPContext *s;
+    int port, is_input;
+    char hostname[256];
+    char buf[1024];
+    char path[1024];
+
+    is_input = !(flags & URL_WRONLY);
+
+    s = av_mallocz(sizeof(RTMPContext));
+    if (!s)
+        return AVERROR(ENOMEM);
+    h->priv_data = s;
+
+    url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
+              path, sizeof(path), uri);
+
+    if (port == -1)
+        port = RTMP_DEFAULT_PORT;
+    snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
+
+    if (url_open(&s->rtmp_hd, buf, URL_RDWR) < 0)
+        goto fail;
+
+    h->max_packet_size = url_get_max_packet_size(s->rtmp_hd);
+    h->is_streamed = 1;
+    url_close(s->rtmp_hd);
+    return 0;
+
+ fail:
+    if (s->rtmp_hd)
+        url_close(s->rtmp_hd);
+    av_free(s);
+    return AVERROR(EIO);
+}
+
+static int rtmp_read(URLContext *h, uint8_t *buf, int size)
+{
+    return 0;
+}
+
+static int rtmp_write(URLContext *h, uint8_t *buf, int size)
+{
+    return 0;
+}
+
+static int rtmp_close(URLContext *h)
+{
+    RTMPContext *s = h->priv_data;
+
+    av_free(s);
+    return 0;
+}
+
+URLProtocol rtmp_protocol = {
+    "rtmp",
+    rtmp_open,
+    rtmp_read,
+    rtmp_write,
+    NULL, /* seek */
+    rtmp_close,
+};


More information about the FFmpeg-soc mailing list