[FFmpeg-devel] [PATCH] rtmp-output
Stefano Sabatini
stefano.sabatini-lala
Tue Dec 1 01:50:22 CET 2009
On date Monday 2009-11-30 23:21:46 +0200, Sergiy encoded:
> 2009/11/30 Diego Biurrun <diego at biurrun.de>:
> > Please respect the surrounding (K&R) style and be consistent, you mix
> > styles even in the code you add: Place spaces after if/for/while/switch.
> >
>
> >
> > .. and space before {
>
> Added.
> I also fix code for sending channel_id >= 64 in ff_rtmp_packet_write().
> diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c
> index e1f0647..a07e996 100644
> --- a/libavformat/rtmppkt.c
> +++ b/libavformat/rtmppkt.c
> @@ -111,6 +111,13 @@ int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p,
> }
> }
> }
> + //extract ts extension
> + if (timestamp == 0xffffff) {
> + if (url_read_complete(h, buf, 4) != 4)
> + return AVERROR(EIO);
> + timestamp = AV_RB32(buf);
> + }
> +
This belongs to a separate patch.
> if (ff_rtmp_packet_create(p, channel_id, type, timestamp, data_size))
> return -1;
> p->extra = extra;
> @@ -144,17 +151,47 @@ int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt,
> int mode = RTMP_PS_TWELVEBYTES;
> int off = 0;
>
> - //TODO: header compression
Don't remove the todo if you didn't fixed it yet.
> - bytestream_put_byte(&p, pkt->channel_id | (mode << 6));
> + //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->timestamp == prev_pkt[pkt->channel_id].timestamp)
> + mode = RTMP_PS_ONEBYTE;
> + } else
> + mode = RTMP_PS_EIGHTBYTES;
> + }
> +
> + 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);
> + }
This belongs to a separate patch.
> if (mode != RTMP_PS_ONEBYTE) {
> - bytestream_put_be24(&p, pkt->timestamp);
> + bytestream_put_be24(&p, pkt->timestamp < 0xffffff ? pkt->timestamp : 0xffffff);
> 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);
> }
> + //send ts extension
> + if (pkt->timestamp >= 0xffffff)
> + bytestream_put_be32(&p, pkt->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].data_size = pkt->data_size;
> + prev_pkt[pkt->channel_id].timestamp = pkt->timestamp;
> + prev_pkt[pkt->channel_id].extra = pkt->extra;
> +
> url_write(h, pkt_hdr, p-pkt_hdr);
> while (off < pkt->data_size) {
> int towrite = FFMIN(chunk_size, pkt->data_size - off);
> diff --git a/libavformat/rtmppkt.h b/libavformat/rtmppkt.h
> index f50996f..2c4f92b 100644
> --- a/libavformat/rtmppkt.h
> +++ b/libavformat/rtmppkt.h
> @@ -34,6 +34,7 @@
> 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_SOURCE_CHANNEL, ///< channel for sending a/v to server
> RTMP_VIDEO_CHANNEL = 8, ///< channel for video data
> RTMP_AUDIO_CHANNEL, ///< channel for audio data
> };
> diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c
> index 6d52214..4640da3 100644
> --- a/libavformat/rtmpproto.c
> +++ b/libavformat/rtmpproto.c
> @@ -57,12 +57,16 @@ typedef struct RTMPContext {
> URLContext* stream; ///< TCP stream used in interactions with RTMP server
> RTMPPacket prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
> int chunk_size; ///< size of the chunks RTMP packets are divided into
> + double pkt_id; ///< each packet sends number, that increased by 1
> + int is_input; ///< input/output flag
> char playpath[256]; ///< path to filename to play (with possible "mp4:" prefix)
> + char app[128]; ///< application
This belongs to a separate patch.
> ClientState state; ///< current state
> int main_channel_id; ///< an additional channel ID which is used for some invocations
> uint8_t* flv_data; ///< buffer with data for demuxer
> int flv_size; ///< current buffer size
> int flv_off; ///< number of bytes read from current buffer
> + RTMPPacket out_pkt; ///< type of flv packet (a/v or metadata)
> uint32_t video_ts; ///< current video timestamp in milliseconds
> uint32_t audio_ts; ///< current audio timestamp in milliseconds
> } RTMPContext;
> @@ -94,42 +98,53 @@ static const uint8_t rtmp_server_key[] = {
> * Generates 'connect' call and sends it to the server.
> */
> static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto,
> - const char *host, int port, const char *app)
> + const char *host, int port)
> {
> RTMPPacket pkt;
> - uint8_t ver[32], *p;
> + uint8_t ver[64], *p;
> char tcurl[512];
>
> - ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 4096);
> + ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096);
> p = pkt.data;
>
> - snprintf(tcurl, sizeof(tcurl), "%s://%s:%d/%s", proto, host, port, app);
> + snprintf(tcurl, sizeof(tcurl), "%s://%s:%d/%s", proto, host, port, rt->app);
> ff_amf_write_string(&p, "connect");
> - ff_amf_write_number(&p, 1.0);
> + ff_amf_write_number(&p, rt->pkt_id);
> + rt->pkt_id++;
> ff_amf_write_object_start(&p);
> ff_amf_write_field_name(&p, "app");
> - ff_amf_write_string(&p, app);
> + ff_amf_write_string(&p, rt->app);
> +
> + if (rt->is_input) {
> + snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1,
> + RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
> + } else {
> + snprintf(ver, sizeof(ver), "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
> + ff_amf_write_field_name(&p, "type");
> + ff_amf_write_string(&p, "nonprivate");
> + }
>
> - snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1,
> - RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
> ff_amf_write_field_name(&p, "flashVer");
> ff_amf_write_string(&p, ver);
> ff_amf_write_field_name(&p, "tcUrl");
> ff_amf_write_string(&p, tcurl);
> - ff_amf_write_field_name(&p, "fpad");
> - ff_amf_write_bool(&p, 0);
> - ff_amf_write_field_name(&p, "capabilities");
> - ff_amf_write_number(&p, 15.0);
> - ff_amf_write_field_name(&p, "audioCodecs");
> - ff_amf_write_number(&p, 1639.0);
> - ff_amf_write_field_name(&p, "videoCodecs");
> - ff_amf_write_number(&p, 252.0);
> - ff_amf_write_field_name(&p, "videoFunction");
> - ff_amf_write_number(&p, 1.0);
> + ff_amf_write_field_name(&p, "swfUrl"); //justin.tv want this
> + ff_amf_write_string(&p, tcurl);
> + if (rt->is_input) {
> + ff_amf_write_field_name(&p, "fpad");
> + ff_amf_write_bool(&p, 0);
> + ff_amf_write_field_name(&p, "capabilities");
> + ff_amf_write_number(&p, 15.0);
> + ff_amf_write_field_name(&p, "audioCodecs");
> + ff_amf_write_number(&p, 1639.0);
> + ff_amf_write_field_name(&p, "videoCodecs");
> + ff_amf_write_number(&p, 252.0);
> + ff_amf_write_field_name(&p, "videoFunction");
> + ff_amf_write_number(&p, 1.0);
> + }
Leave the reindent for a separate cosmetic patch, this way the patch
is easier to read.
> ff_amf_write_object_end(&p);
>
> pkt.data_size = p - pkt.data;
> -
> ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
Cosmetics.
> }
>
> @@ -142,13 +157,37 @@ static void gen_create_stream(URLContext *s, RTMPContext *rt)
> RTMPPacket pkt;
> uint8_t *p;
>
> - av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Creating stream...\n");
> - ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 25);
> + ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096);
> +
> + if (!rt->is_input) {
> + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Releasing stream...\n");
> + p = pkt.data;
> + ff_amf_write_string(&p, "releaseStream");
> + ff_amf_write_number(&p, rt->pkt_id);
> + rt->pkt_id++;
> + ff_amf_write_null(&p);
> + ff_amf_write_string(&p, rt->playpath);
> + pkt.data_size = p - pkt.data;
> + ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> +
> + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Publishing stream...\n");
Add some trace for debugging is fine but again it belongs to a
separate patch.
> + p = pkt.data;
> + ff_amf_write_string(&p, "FCPublish");
> + ff_amf_write_number(&p, rt->pkt_id);
> + rt->pkt_id++;
> + ff_amf_write_null(&p);
> + ff_amf_write_string(&p, rt->playpath);
> + pkt.data_size = p - pkt.data;
> + ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> + }
>
> + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Creating stream...\n");
> p = pkt.data;
> ff_amf_write_string(&p, "createStream");
> - ff_amf_write_number(&p, 3.0);
> + ff_amf_write_number(&p, rt->pkt_id);
> + rt->pkt_id++;
> ff_amf_write_null(&p);
> + pkt.data_size = p - pkt.data;
>
> ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> ff_rtmp_packet_destroy(&pkt);
> @@ -163,30 +202,40 @@ static void gen_play(URLContext *s, RTMPContext *rt)
> RTMPPacket pkt;
> uint8_t *p;
>
> - av_log(LOG_CONTEXT, 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));
> + av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Sending %s command for '%s'\n", rt->is_input ? "play" : "publish",
> + rt->playpath);
> + ff_rtmp_packet_create(&pkt, rt->is_input ? RTMP_VIDEO_CHANNEL : RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE, 0,
> + 4096);
> pkt.extra = rt->main_channel_id;
>
> p = pkt.data;
> - ff_amf_write_string(&p, "play");
> + if (rt->is_input)
> + ff_amf_write_string(&p, "play");
Again cosmetics.
> + else
> + ff_amf_write_string(&p, "publish");
> +
> ff_amf_write_number(&p, 0.0);
> ff_amf_write_null(&p);
> ff_amf_write_string(&p, rt->playpath);
> + if (!rt->is_input)
> + ff_amf_write_string(&p, "live");
> + pkt.data_size = p - pkt.data;
>
> ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> ff_rtmp_packet_destroy(&pkt);
>
> - // set client buffer time disguised in ping packet
> - ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, 1, 10);
> + if (rt->is_input) {
> + // set client buffer time disguised in ping packet
> + ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, 1, 10);
>
> - p = pkt.data;
> - bytestream_put_be16(&p, 3);
> - bytestream_put_be32(&p, 1);
> - bytestream_put_be32(&p, 256); //TODO: what is a good value here?
> + p = pkt.data;
> + bytestream_put_be16(&p, 3);
> + bytestream_put_be32(&p, 1);
> + bytestream_put_be32(&p, 256); //TODO: what is a good value here?
>
> - ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> - ff_rtmp_packet_destroy(&pkt);
> + ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> + ff_rtmp_packet_destroy(&pkt);
> + }
> }
>
> /**
> @@ -205,6 +254,25 @@ static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
> ff_rtmp_packet_destroy(&pkt);
> }
>
> +/**
> + * Generates client/server bandwidth reply and sends it to the server.
> + */
> +static void gen_bw(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
> +{
> + RTMPPacket pkt;
> + uint8_t *p;
> +
> + if ((rt->is_input && ppkt->type == RTMP_PT_CLIENT_BW) ||
> + (!rt->is_input && ppkt->type == RTMP_PT_SERVER_BW))
> + return;
> + ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, rt->is_input ? RTMP_PT_CLIENT_BW : RTMP_PT_SERVER_BW,
> + 0, 4);
> + p = pkt.data;
> + bytestream_put_be32(&p, AV_RB32(ppkt->data));
> + ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
> + ff_rtmp_packet_destroy(&pkt);
> +}
Also this seems to belong to a separate patch.
[...]
This feature is definitively welcome, but please try to split the
patch in separate minimal changes.
Regards.
--
FFmpeg = Fiendish and Foolish Multimedia Peaceless Eccentric Gadget
More information about the ffmpeg-devel
mailing list