Newer
Older
Fabrice Bellard
committed
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
if (ret)
return ret;
/* open parsers for each new streams */
for(i = old_nb_streams; i < s->nb_streams; i++)
open_parser(s, i);
st = s->streams[s->cur_pkt.stream_index];
/* update current pts (XXX: dts handling) from packet, or
use current pts if none given */
if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
av_frac_set(&st->pts, s->cur_pkt.pts);
} else {
s->cur_pkt.pts = st->pts.val;
}
if (!st->codec.codec) {
/* no codec opened: just return the raw packet */
*pkt = s->cur_pkt;
/* no codec opened: just update the pts by considering we
have one frame and free the packet */
if (st->pts.den == 0) {
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
st->pts_incr = (int64_t)s->pts_den * st->codec.frame_size;
Fabrice Bellard
committed
av_frac_init(&st->pts, st->pts.val, 0,
(int64_t)s->pts_num * st->codec.sample_rate);
Fabrice Bellard
committed
break;
case CODEC_TYPE_VIDEO:
Michael Niedermayer
committed
st->pts_incr = (int64_t)s->pts_den * st->codec.frame_rate_base;
Fabrice Bellard
committed
av_frac_init(&st->pts, st->pts.val, 0,
(int64_t)s->pts_num * st->codec.frame_rate);
Fabrice Bellard
committed
break;
default:
av_abort();
}
}
av_frac_add(&st->pts, st->pts_incr);
return 0;
} else {
s->cur_ptr = s->cur_pkt.data;
s->cur_len = s->cur_pkt.size;
Fabrice Bellard
committed
}
}
static int compute_send_delay(HTTPContext *c)
{
int64_t cur_pts, delta_pts, next_pts;
Fabrice Bellard
committed
int delay1;
/* compute current pts value from system time */
cur_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
Fabrice Bellard
committed
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
(c->fmt_in->pts_num * 1000LL);
/* compute the delta from the stream we choose as
main clock (we do that to avoid using explicit
buffers to do exact packet reordering for each
stream */
/* XXX: really need to fix the number of streams */
if (c->pts_stream_index >= c->fmt_in->nb_streams)
next_pts = cur_pts;
else
next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
delta_pts = next_pts - cur_pts;
if (delta_pts <= 0) {
delay1 = 0;
} else {
delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
}
return delay1;
}
#else
/* just fall backs */
static int av_read_frame(AVFormatContext *s, AVPacket *pkt)
Fabrice Bellard
committed
{
return av_read_packet(s, pkt);
}
static int compute_send_delay(HTTPContext *c)
{
int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
Giancarlo Formicuccia
committed
int64_t delta_pts;
int64_t time_pts;
int m_delay;
if (datarate > c->stream->bandwidth * 2000) {
return 1000;
}
Giancarlo Formicuccia
committed
if(!c->stream->feed && c->first_pts!=AV_NOPTS_VALUE) {
time_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
((int64_t) c->fmt_in->pts_num*1000);
delta_pts = c->cur_pts - time_pts;
m_delay = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
return m_delay>0 ? m_delay : 0;
} else return 0;
Fabrice Bellard
committed
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
}
#endif
static int http_prepare_data(HTTPContext *c)
{
int i, len, ret;
AVFormatContext *ctx;
switch(c->state) {
case HTTPSTATE_SEND_DATA_HEADER:
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
c->stream->author);
pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
c->stream->comment);
pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
c->stream->copyright);
pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
c->stream->title);
/* open output stream by using specified codecs */
c->fmt_ctx.oformat = c->stream->fmt;
c->fmt_ctx.nb_streams = c->stream->nb_streams;
for(i=0;i<c->fmt_ctx.nb_streams;i++) {
AVStream *st;
st = av_mallocz(sizeof(AVStream));
c->fmt_ctx.streams[i] = st;
/* if file or feed, then just take streams from FFStream struct */
if (!c->stream->feed ||
c->stream->feed == c->stream)
memcpy(st, c->stream->streams[i], sizeof(AVStream));
else
memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
sizeof(AVStream));
st->codec.frame_number = 0; /* XXX: should be done in
AVStream, not in codec */
Philip Gladstone
committed
/* I'm pretty sure that this is not correct...
* However, without it, we crash
*/
st->codec.coded_frame = &dummy_frame;
Fabrice Bellard
committed
}
c->got_key_frame = 0;
/* prepare header and save header data in a stream */
if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
/* XXX: potential leak */
return -1;
}
c->fmt_ctx.pb.is_streamed = 1;
Fabrice Bellard
committed
av_write_header(&c->fmt_ctx);
len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
c->state = HTTPSTATE_SEND_DATA;
c->last_packet_sent = 0;
break;
case HTTPSTATE_SEND_DATA:
/* find a new packet */
{
AVPacket pkt;
Fabrice Bellard
committed
/* read a packet from the input stream */
if (c->stream->feed) {
ffm_set_write_index(c->fmt_in,
c->stream->feed->feed_write_index,
c->stream->feed->feed_size);
}
if (c->stream->max_time &&
c->stream->max_time + c->start_time - cur_time < 0) {
/* We have timed out */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
if (1 || c->is_packetized) {
Fabrice Bellard
committed
if (compute_send_delay(c) > 0) {
c->state = HTTPSTATE_WAIT;
return 1; /* state changed */
}
}
redo:
Fabrice Bellard
committed
if (av_read_frame(c->fmt_in, &pkt) < 0) {
if (c->stream->feed && c->stream->feed->feed_opened) {
/* if coming from feed, it means we reached the end of the
ffm file, so must wait for more data */
c->state = HTTPSTATE_WAIT_FEED;
return 1; /* state changed */
} else {
if (c->stream->loop) {
av_close_input_file(c->fmt_in);
c->fmt_in = NULL;
if (open_input_stream(c, "") < 0)
goto no_loop;
goto redo;
} else {
no_loop:
/* must send trailer now because eof or error */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
}
Fabrice Bellard
committed
}
} else {
/* update first pts if needed */
Giancarlo Formicuccia
committed
if (c->first_pts == AV_NOPTS_VALUE) {
Fabrice Bellard
committed
c->first_pts = pkt.pts;
Giancarlo Formicuccia
committed
c->start_time = cur_time;
}
c->cur_pts = pkt.pts;
Fabrice Bellard
committed
/* send it to the appropriate stream */
if (c->stream->feed) {
/* if coming from a feed, select the right stream */
if (c->switch_pending) {
c->switch_pending = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->switch_feed_streams[i] == pkt.stream_index) {
if (pkt.flags & PKT_FLAG_KEY) {
do_switch_stream(c, i);
}
}
if (c->switch_feed_streams[i] >= 0) {
c->switch_pending = 1;
}
}
}
Fabrice Bellard
committed
if (c->feed_streams[i] == pkt.stream_index) {
pkt.stream_index = i;
Fabrice Bellard
committed
c->got_key_frame |= 1 << i;
}
/* See if we have all the key frames, then
* we start to send. This logic is not quite
* right, but it works for the case of a
* single video stream with one or more
* audio streams (for which every frame is
* typically a key frame).
*/
if (!c->stream->send_on_key ||
((c->got_key_frame + 1) >> c->stream->nb_streams)) {
goto send_it;
Fabrice Bellard
committed
} else {
AVCodecContext *codec;
send_it:
/* specific handling for RTP: we use several
output stream (one for each RTP
connection). XXX: need more abstract handling */
if (c->is_packetized) {
c->packet_stream_index = pkt.stream_index;
ctx = c->rtp_ctx[c->packet_stream_index];
if(!ctx) {
av_free_packet(&pkt);
Giancarlo Formicuccia
committed
break;
}
Fabrice Bellard
committed
codec = &ctx->streams[0]->codec;
/* only one stream per RTP connection */
pkt.stream_index = 0;
Fabrice Bellard
committed
} else {
ctx = &c->fmt_ctx;
/* Fudge here */
codec = &ctx->streams[pkt.stream_index]->codec;
Fabrice Bellard
committed
codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
Fabrice Bellard
committed
Philip Gladstone
committed
#ifdef PJSG
Fabrice Bellard
committed
if (codec->codec_type == CODEC_TYPE_AUDIO) {
codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
/* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
}
#endif
if (c->is_packetized) {
ret = url_open_dyn_packet_buf(&ctx->pb,
url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
c->packet_byte_count = 0;
c->packet_start_time_us = av_gettime();
} else {
ret = url_open_dyn_buf(&ctx->pb);
}
if (ret < 0) {
/* XXX: potential leak */
return -1;
}
if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
Fabrice Bellard
committed
c->state = HTTPSTATE_SEND_DATA_TRAILER;
}
len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
codec->frame_number++;
Philip Gladstone
committed
}
Fabrice Bellard
committed
#ifndef AV_READ_FRAME
av_free_packet(&pkt);
Philip Gladstone
committed
#endif
}
}
}
break;
default:
case HTTPSTATE_SEND_DATA_TRAILER:
/* last packet test ? */
Fabrice Bellard
committed
if (c->last_packet_sent || c->is_packetized)
Fabrice Bellard
committed
ctx = &c->fmt_ctx;
Fabrice Bellard
committed
if (url_open_dyn_buf(&ctx->pb) < 0) {
/* XXX: potential leak */
return -1;
}
av_write_trailer(ctx);
len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
c->last_packet_sent = 1;
break;
}
return 0;
}
Fabrice Bellard
committed
/* in bit/s */
#define SHORT_TERM_BANDWIDTH 8000000
static int http_send_data(HTTPContext *c)
Fabrice Bellard
committed
int len, ret, dt;
Fabrice Bellard
committed
av_freep(&c->pb_buffer);
ret = http_prepare_data(c);
continue;
} else {
/* state change requested */
return 0;
}
}
Fabrice Bellard
committed
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
if (c->buffer_ptr < c->buffer_end) {
if (c->is_packetized) {
/* RTP/UDP data output */
len = c->buffer_end - c->buffer_ptr;
if (len < 4) {
/* fail safe - should never happen */
fail1:
c->buffer_ptr = c->buffer_end;
return 0;
}
len = (c->buffer_ptr[0] << 24) |
(c->buffer_ptr[1] << 16) |
(c->buffer_ptr[2] << 8) |
(c->buffer_ptr[3]);
if (len > (c->buffer_end - c->buffer_ptr))
goto fail1;
/* short term bandwidth limitation */
dt = av_gettime() - c->packet_start_time_us;
if (dt < 1)
dt = 1;
if ((c->packet_byte_count + len) * (int64_t)1000000 >=
(SHORT_TERM_BANDWIDTH / 8) * (int64_t)dt) {
Fabrice Bellard
committed
/* bandwidth overflow : wait at most one tick and retry */
c->state = HTTPSTATE_WAIT_SHORT;
return 0;
Philip Gladstone
committed
}
Fabrice Bellard
committed
c->buffer_ptr += 4;
url_write(c->rtp_handles[c->packet_stream_index],
c->buffer_ptr, len);
Philip Gladstone
committed
c->buffer_ptr += len;
Fabrice Bellard
committed
c->packet_byte_count += len;
} else {
/* TCP data output */
len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
if (len < 0) {
if (errno != EAGAIN && errno != EINTR) {
/* error : close connection */
return -1;
} else {
return 0;
}
} else {
c->buffer_ptr += len;
}
Fabrice Bellard
committed
c->data_count += len;
update_datarate(&c->datarate, c->data_count);
if (c->stream)
c->stream->bytes_served += len;
}
return 0;
}
static int http_start_receive_data(HTTPContext *c)
{
int fd;
if (c->stream->feed_opened)
return -1;
Philip Gladstone
committed
/* Don't permit writing to this one */
if (c->stream->readonly)
return -1;
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
/* open feed */
fd = open(c->stream->feed_filename, O_RDWR);
if (fd < 0)
return -1;
c->feed_fd = fd;
c->stream->feed_write_index = ffm_read_write_index(fd);
c->stream->feed_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
/* init buffer input */
c->buffer_ptr = c->buffer;
c->buffer_end = c->buffer + FFM_PACKET_SIZE;
c->stream->feed_opened = 1;
return 0;
}
static int http_receive_data(HTTPContext *c)
{
HTTPContext *c1;
if (c->buffer_end > c->buffer_ptr) {
int len;
len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
if (len < 0) {
if (errno != EAGAIN && errno != EINTR) {
/* error : close connection */
goto fail;
}
} else if (len == 0) {
/* end of connection : close it */
goto fail;
} else {
c->buffer_ptr += len;
c->data_count += len;
update_datarate(&c->datarate, c->data_count);
}
}
Philip Gladstone
committed
FFStream *feed = c->stream;
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
/* a packet has been received : write it in the store, except
if header */
if (c->data_count > FFM_PACKET_SIZE) {
// printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
/* XXX: use llseek or url_seek */
lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
feed->feed_write_index += FFM_PACKET_SIZE;
/* update file size */
if (feed->feed_write_index > c->stream->feed_size)
feed->feed_size = feed->feed_write_index;
/* handle wrap around if max file size reached */
if (feed->feed_write_index >= c->stream->feed_max_size)
feed->feed_write_index = FFM_PACKET_SIZE;
/* write index */
ffm_write_write_index(c->feed_fd, feed->feed_write_index);
/* wake up any waiting connections */
for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
if (c1->state == HTTPSTATE_WAIT_FEED &&
c1->stream->feed == c->stream->feed) {
c1->state = HTTPSTATE_SEND_DATA;
}
}
Philip Gladstone
committed
} else {
/* We have a header in our hands that contains useful data */
AVFormatContext s;
Philip Gladstone
committed
ByteIOContext *pb = &s.pb;
int i;
memset(&s, 0, sizeof(s));
url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
pb->buf_end = c->buffer_end; /* ?? */
pb->is_streamed = 1;
/* use feed output format name to find corresponding input format */
fmt_in = av_find_input_format(feed->fmt->name);
if (!fmt_in)
goto fail;
if (fmt_in->priv_data_size > 0) {
s.priv_data = av_mallocz(fmt_in->priv_data_size);
if (!s.priv_data)
goto fail;
} else
s.priv_data = NULL;
if (fmt_in->read_header(&s, 0) < 0) {
av_freep(&s.priv_data);
Philip Gladstone
committed
goto fail;
}
/* Now we have the actual streams */
if (s.nb_streams != feed->nb_streams) {
av_freep(&s.priv_data);
Philip Gladstone
committed
goto fail;
}
for (i = 0; i < s.nb_streams; i++) {
memcpy(&feed->streams[i]->codec,
&s.streams[i]->codec, sizeof(AVCodecContext));
Philip Gladstone
committed
}
av_freep(&s.priv_data);
}
c->buffer_ptr = c->buffer;
}
return 0;
fail:
c->stream->feed_opened = 0;
close(c->feed_fd);
return -1;
}
Fabrice Bellard
committed
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
/********************************************************************/
/* RTSP handling */
static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
{
const char *str;
time_t ti;
char *p;
char buf2[32];
switch(error_number) {
#define DEF(n, c, s) case c: str = s; break;
#include "rtspcodes.h"
#undef DEF
default:
str = "Unknown Error";
break;
}
url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
/* output GMT time */
ti = time(NULL);
p = ctime(&ti);
strcpy(buf2, p);
p = buf2 + strlen(p) - 1;
if (*p == '\n')
*p = '\0';
url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
}
static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
{
rtsp_reply_header(c, error_number);
url_fprintf(c->pb, "\r\n");
}
static int rtsp_parse_request(HTTPContext *c)
{
const char *p, *p1, *p2;
char cmd[32];
char url[1024];
char protocol[32];
char line[1024];
ByteIOContext pb1;
int len;
RTSPHeader header1, *header = &header1;
c->buffer_ptr[0] = '\0';
p = c->buffer;
get_word(cmd, sizeof(cmd), &p);
get_word(url, sizeof(url), &p);
get_word(protocol, sizeof(protocol), &p);
pstrcpy(c->method, sizeof(c->method), cmd);
pstrcpy(c->url, sizeof(c->url), url);
pstrcpy(c->protocol, sizeof(c->protocol), protocol);
c->pb = &pb1;
if (url_open_dyn_buf(c->pb) < 0) {
/* XXX: cannot do more */
c->pb = NULL; /* safety */
return -1;
}
/* check version name */
if (strcmp(protocol, "RTSP/1.0") != 0) {
rtsp_reply_error(c, RTSP_STATUS_VERSION);
goto the_end;
}
/* parse each header line */
memset(header, 0, sizeof(RTSPHeader));
/* skip to next line */
while (*p != '\n' && *p != '\0')
p++;
if (*p == '\n')
p++;
while (*p != '\0') {
p1 = strchr(p, '\n');
if (!p1)
break;
p2 = p1;
if (p2 > p && p2[-1] == '\r')
p2--;
/* skip empty line */
if (p2 == p)
break;
len = p2 - p;
if (len > sizeof(line) - 1)
len = sizeof(line) - 1;
memcpy(line, p, len);
line[len] = '\0';
rtsp_parse_line(header, line);
p = p1 + 1;
}
/* handle sequence number */
c->seq = header->seq;
if (!strcmp(cmd, "DESCRIBE")) {
rtsp_cmd_describe(c, url);
} else if (!strcmp(cmd, "OPTIONS")) {
rtsp_cmd_options(c, url);
Fabrice Bellard
committed
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
} else if (!strcmp(cmd, "SETUP")) {
rtsp_cmd_setup(c, url, header);
} else if (!strcmp(cmd, "PLAY")) {
rtsp_cmd_play(c, url, header);
} else if (!strcmp(cmd, "PAUSE")) {
rtsp_cmd_pause(c, url, header);
} else if (!strcmp(cmd, "TEARDOWN")) {
rtsp_cmd_teardown(c, url, header);
} else {
rtsp_reply_error(c, RTSP_STATUS_METHOD);
}
the_end:
len = url_close_dyn_buf(c->pb, &c->pb_buffer);
c->pb = NULL; /* safety */
if (len < 0) {
/* XXX: cannot do more */
return -1;
}
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
c->state = RTSPSTATE_SEND_REPLY;
return 0;
}
Fabrice Bellard
committed
/* XXX: move that to rtsp.c, but would need to replace FFStream by
AVFormatContext */
static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
Fabrice Bellard
committed
struct in_addr my_ip)
Fabrice Bellard
committed
{
ByteIOContext pb1, *pb = &pb1;
int i, payload_type, port, private_payload_type, j;
Fabrice Bellard
committed
const char *ipstr, *title, *mediatype;
AVStream *st;
if (url_open_dyn_buf(pb) < 0)
return -1;
/* general media info */
url_fprintf(pb, "v=0\n");
Fabrice Bellard
committed
ipstr = inet_ntoa(my_ip);
Fabrice Bellard
committed
url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
title = stream->title;
if (title[0] == '\0')
title = "No Title";
url_fprintf(pb, "s=%s\n", title);
if (stream->comment[0] != '\0')
url_fprintf(pb, "i=%s\n", stream->comment);
Fabrice Bellard
committed
if (stream->is_multicast) {
url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
}
Fabrice Bellard
committed
/* for each stream, we output the necessary info */
private_payload_type = 96;
Fabrice Bellard
committed
for(i = 0; i < stream->nb_streams; i++) {
st = stream->streams[i];
switch(st->codec.codec_type) {
case CODEC_TYPE_AUDIO:
mediatype = "audio";
break;
case CODEC_TYPE_VIDEO:
mediatype = "video";
break;
default:
mediatype = "application";
break;
}
Fabrice Bellard
committed
/* NOTE: the port indication is not correct in case of
unicast. It is not an issue because RTSP gives it */
Fabrice Bellard
committed
payload_type = rtp_get_payload_type(&st->codec);
if (payload_type < 0)
payload_type = private_payload_type++;
Fabrice Bellard
committed
if (stream->is_multicast) {
port = stream->multicast_port + 2 * i;
} else {
port = 0;
}
Fabrice Bellard
committed
url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
Fabrice Bellard
committed
mediatype, port, payload_type);
if (payload_type >= 96) {
/* for private payload type, we need to give more info */
switch(st->codec.codec_id) {
case CODEC_ID_MPEG4:
{
uint8_t *data;
url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
payload_type, 90000);
/* we must also add the mpeg4 header */
data = st->codec.extradata;
if (data) {
Michael Niedermayer
committed
url_fprintf(pb, "a=fmtp:%d config=", payload_type);
for(j=0;j<st->codec.extradata_size;j++) {
url_fprintf(pb, "%02x", data[j]);
}
url_fprintf(pb, "\n");
}
}
break;
default:
/* XXX: add other codecs ? */
goto fail;
}
}
Fabrice Bellard
committed
url_fprintf(pb, "a=control:streamid=%d\n", i);
}
return url_close_dyn_buf(pb, pbuffer);
fail:
url_close_dyn_buf(pb, pbuffer);
av_free(*pbuffer);
return -1;
Fabrice Bellard
committed
}
static void rtsp_cmd_options(HTTPContext *c, const char *url)
{
// rtsp_reply_header(c, RTSP_STATUS_OK);
url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
url_fprintf(c->pb, "\r\n");
}
Fabrice Bellard
committed
static void rtsp_cmd_describe(HTTPContext *c, const char *url)
{
FFStream *stream;
char path1[1024];
const char *path;
Fabrice Bellard
committed
int content_length, len;
struct sockaddr_in my_addr;
Fabrice Bellard
committed
/* find which url is asked */
url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
path = path1;
if (*path == '/')
path++;
for(stream = first_stream; stream != NULL; stream = stream->next) {
if (!stream->is_feed && stream->fmt == &rtp_mux &&
!strcmp(path, stream->filename)) {
goto found;
}
}
/* no stream found */
rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
return;
found:
/* prepare the media description in sdp format */
Fabrice Bellard
committed
/* get the host IP */
len = sizeof(my_addr);
getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
Fabrice Bellard
committed
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
if (content_length < 0) {
rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
return;
}
rtsp_reply_header(c, RTSP_STATUS_OK);
url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
url_fprintf(c->pb, "\r\n");
put_buffer(c->pb, content, content_length);
}
static HTTPContext *find_rtp_session(const char *session_id)
{
HTTPContext *c;
if (session_id[0] == '\0')
return NULL;
for(c = first_http_ctx; c != NULL; c = c->next) {
if (!strcmp(c->session_id, session_id))
return c;
}
return NULL;
}
static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
Fabrice Bellard
committed
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
{
RTSPTransportField *th;
int i;
for(i=0;i<h->nb_transports;i++) {
th = &h->transports[i];
if (th->protocol == protocol)
return th;
}
return NULL;
}
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
RTSPHeader *h)
{
FFStream *stream;
int stream_index, port;
char buf[1024];
char path1[1024];
const char *path;
HTTPContext *rtp_c;
RTSPTransportField *th;
struct sockaddr_in dest_addr;
RTSPActionServerSetup setup;
/* find which url is asked */
url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
path = path1;
if (*path == '/')
path++;
/* now check each stream */
for(stream = first_stream; stream != NULL; stream = stream->next) {
if (!stream->is_feed && stream->fmt == &rtp_mux) {
/* accept aggregate filenames only if single stream */
if (!strcmp(path, stream->filename)) {
if (stream->nb_streams != 1) {
rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
return;
}
stream_index = 0;
goto found;
}
for(stream_index = 0; stream_index < stream->nb_streams;
stream_index++) {
snprintf(buf, sizeof(buf), "%s/streamid=%d",
stream->filename, stream_index);
if (!strcmp(path, buf))
goto found;
}
}
}
/* no stream found */
rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
return;
found:
/* generate session id if needed */
if (h->session_id[0] == '\0') {
snprintf(h->session_id, sizeof(h->session_id),
"%08x%08x", (int)random(), (int)random());
}
/* find rtp session, and create it if none found */
rtp_c = find_rtp_session(h->session_id);
if (!rtp_c) {
rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
Fabrice Bellard
committed
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
return;
}
/* open input stream */
if (open_input_stream(rtp_c, "") < 0) {
rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
return;
}
/* always prefer UDP */
th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
if (!th) {
th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
if (!th) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
}
rtp_c->rtp_protocol = th->protocol;
}
/* test if stream is OK (test needed because several SETUP needs
to be done for a given file) */
if (rtp_c->stream != stream) {
rtsp_reply_error(c, RTSP_STATUS_SERVICE);
return;
}
/* test if stream is already set up */
if (rtp_c->rtp_ctx[stream_index]) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
/* check transport */
th = find_transport(h, rtp_c->rtp_protocol);
if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
th->client_port_min <= 0)) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
/* setup default options */
setup.transport_option[0] = '\0';
dest_addr = rtp_c->from_addr;
dest_addr.sin_port = htons(th->client_port_min);
/* add transport option if needed */
if (ff_rtsp_callback) {
setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
(char *)&setup, sizeof(setup),
stream->rtsp_option) < 0) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
}
/* setup stream */
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
switch(rtp_c->rtp_protocol) {
case RTSP_PROTOCOL_RTP_UDP:
port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
"client_port=%d-%d;server_port=%d-%d",
th->client_port_min, th->client_port_min + 1,
port, port + 1);
break;
case RTSP_PROTOCOL_RTP_TCP:
url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
stream_index * 2, stream_index * 2 + 1);
break;
default:
break;
}
if (setup.transport_option[0] != '\0') {
url_fprintf(c->pb, ";%s", setup.transport_option);
}
url_fprintf(c->pb, "\r\n");
url_fprintf(c->pb, "\r\n");
}
/* find an rtp connection by using the session ID. Check consistency
with filename */
static HTTPContext *find_rtp_session_with_url(const char *url,
const char *session_id)
{
HTTPContext *rtp_c;
char path1[1024];
const char *path;
Fabrice Bellard
committed
rtp_c = find_rtp_session(session_id);
if (!rtp_c)
return NULL;
/* find which url is asked */