Newer
Older
Fabrice Bellard
committed
c->stream->author);
pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
Fabrice Bellard
committed
c->stream->comment);
pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
Fabrice Bellard
committed
c->stream->copyright);
pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
st = av_mallocz(sizeof(AVStream));
st->codec= avcodec_alloc_context();
Fabrice Bellard
committed
c->fmt_ctx.streams[i] = st;
/* if file or feed, then just take streams from FFStream struct */
Fabrice Bellard
committed
c->stream->feed == c->stream)
Fabrice Bellard
committed
else
src = c->stream->feed->streams[c->stream->feed_streams[i]];
*st = *src;
st->priv_data = 0;
Michael Niedermayer
committed
st->codec->frame_number = 0; /* XXX: should be done in
Fabrice Bellard
committed
AVStream, not in codec */
Philip Gladstone
committed
/* I'm pretty sure that this is not correct...
* However, without it, we crash
*/
Michael Niedermayer
committed
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;
Alex Beregszaszi
committed
if (av_write_header(&c->fmt_ctx) < 0)
return -1;
Fabrice Bellard
committed
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;
/* 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);
}
c->stream->max_time + c->start_time - cur_time < 0) {
/* We have timed out */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
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) {
c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
Giancarlo Formicuccia
committed
c->start_time = cur_time;
}
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
Fabrice Bellard
committed
* we start to send. This logic is not quite
* right, but it works for the case of a
Fabrice Bellard
committed
* single video stream with one or more
* audio streams (for which every frame is
* typically a key frame).
Fabrice Bellard
committed
*/
if (!c->stream->send_on_key ||
Fabrice Bellard
committed
((c->got_key_frame + 1) >> c->stream->nb_streams)) {
goto send_it;
Fabrice Bellard
committed
} else {
AVCodecContext *codec;
Fabrice Bellard
committed
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) {
Fabrice Bellard
committed
AVStream *st;
/* compute send time and duration */
st = c->fmt_in->streams[pkt.stream_index];
c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
Fabrice Bellard
committed
if (st->start_time != AV_NOPTS_VALUE)
c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
Fabrice Bellard
committed
#if 0
printf("index=%d pts=%0.3f duration=%0.6f\n",
pkt.stream_index,
Fabrice Bellard
committed
AV_TIME_BASE,
(double)c->cur_frame_duration /
Fabrice Bellard
committed
AV_TIME_BASE);
#endif
/* find RTP context */
Fabrice Bellard
committed
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;
}
Michael Niedermayer
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 */
Michael Niedermayer
committed
codec = ctx->streams[pkt.stream_index]->codec;
codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
Fabrice Bellard
committed
if (c->is_packetized) {
int max_packet_size;
if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
else
max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
Fabrice Bellard
committed
} else {
ret = url_open_dyn_buf(&ctx->pb);
}
if (ret < 0) {
/* XXX: potential leak */
return -1;
}
if (pkt.dts != AV_NOPTS_VALUE)
pkt.dts = av_rescale_q(pkt.dts,
c->fmt_in->streams[pkt.stream_index]->time_base,
ctx->streams[pkt.stream_index]->time_base);
if (pkt.pts != AV_NOPTS_VALUE)
pkt.pts = av_rescale_q(pkt.pts,
c->fmt_in->streams[pkt.stream_index]->time_base,
ctx->streams[pkt.stream_index]->time_base);
if (av_write_frame(ctx, &pkt)) {
Fabrice Bellard
committed
c->state = HTTPSTATE_SEND_DATA_TRAILER;
}
Fabrice Bellard
committed
len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
Fabrice Bellard
committed
c->cur_frame_bytes = len;
Fabrice Bellard
committed
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
Fabrice Bellard
committed
codec->frame_number++;
Fabrice Bellard
committed
if (len == 0)
goto redo;
Philip Gladstone
committed
}
Fabrice Bellard
committed
av_free_packet(&pkt);
}
}
}
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;
}
/* should convert the format at the same time */
/* send data starting at c->buffer_ptr to the output connection
(either UDP or TCP connection) */
static int http_send_data(HTTPContext *c)
Fabrice Bellard
committed
int len, ret;
for(;;) {
if (c->buffer_ptr >= c->buffer_end) {
ret = http_prepare_data(c);
if (ret < 0)
return -1;
else if (ret != 0) {
/* state change requested */
break;
Philip Gladstone
committed
}
Fabrice Bellard
committed
} else {
if (c->is_packetized) {
/* RTP data output */
len = c->buffer_end - c->buffer_ptr;
if (len < 4) {
/* fail safe - should never happen */
fail1:
c->buffer_ptr = c->buffer_end;
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
/* nothing to send yet: we can wait */
return 0;
}
c->data_count += len;
update_datarate(&c->datarate, c->data_count);
if (c->stream)
c->stream->bytes_served += len;
if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
/* RTP packets are sent inside the RTSP TCP connection */
ByteIOContext pb1, *pb = &pb1;
int interleaved_index, size;
uint8_t header[4];
HTTPContext *rtsp_c;
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
rtsp_c = c->rtsp_c;
/* if no RTSP connection left, error */
if (!rtsp_c)
return -1;
/* if already sending something, then wait. */
if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
break;
}
if (url_open_dyn_buf(pb) < 0)
goto fail1;
interleaved_index = c->packet_stream_index * 2;
/* RTCP packets are sent at odd indexes */
if (c->buffer_ptr[1] == 200)
interleaved_index++;
/* write RTSP TCP header */
header[0] = '$';
header[1] = interleaved_index;
header[2] = len >> 8;
header[3] = len;
put_buffer(pb, header, 4);
/* write RTP packet data */
c->buffer_ptr += 4;
put_buffer(pb, c->buffer_ptr, len);
size = url_close_dyn_buf(pb, &c->packet_buffer);
/* prepare asynchronous TCP sending */
rtsp_c->packet_buffer_ptr = c->packet_buffer;
rtsp_c->packet_buffer_end = c->packet_buffer + size;
Fabrice Bellard
committed
c->buffer_ptr += len;
Fabrice Bellard
committed
/* send everything we can NOW */
len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
Fabrice Bellard
committed
if (len > 0) {
rtsp_c->packet_buffer_ptr += len;
Fabrice Bellard
committed
if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
/* if we could not send all the data, we will
send it later, so a new state is needed to
"lock" the RTSP TCP connection */
rtsp_c->state = RTSPSTATE_SEND_PACKET;
break;
} else {
/* all data has been sent */
av_freep(&c->packet_buffer);
}
} else {
/* send RTP packet directly in UDP */
url_write(c->rtp_handles[c->packet_stream_index],
Fabrice Bellard
committed
c->buffer_ptr += len;
/* here we continue as we can send several packets per 10 ms slot */
}
} else {
/* TCP data output */
len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
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;
break;
Fabrice Bellard
committed
}
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;
/* 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 = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
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
if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
if (c->buffer[0] != 'f' ||
c->buffer[1] != 'm') {
http_log("Feed stream has become desynchronized -- disconnecting\n");
goto fail;
}
}
Philip Gladstone
committed
FFStream *feed = c->stream;
/* a packet has been received : write it in the store, except
if header */
if (c->data_count > FFM_PACKET_SIZE) {
// printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\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 (c->stream->feed_max_size && 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,
Michael Niedermayer
committed
s.streams[i]->codec, sizeof(AVCodecContext));
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
/********************************************************************/
/* 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) {
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
case RTSP_STATUS_OK:
str = "OK";
break;
case RTSP_STATUS_METHOD:
str = "Method Not Allowed";
break;
case RTSP_STATUS_BANDWIDTH:
str = "Not Enough Bandwidth";
break;
case RTSP_STATUS_SESSION:
str = "Session Not Found";
break;
case RTSP_STATUS_STATE:
str = "Method Not Valid in This State";
break;
case RTSP_STATUS_AGGREGATE:
str = "Aggregate operation not allowed";
break;
case RTSP_STATUS_ONLY_AGGREGATE:
str = "Only aggregate operation allowed";
break;
case RTSP_STATUS_TRANSPORT:
str = "Unsupported transport";
break;
case RTSP_STATUS_INTERNAL:
str = "Internal Server Error";
break;
case RTSP_STATUS_SERVICE:
str = "Service Unavailable";
break;
case RTSP_STATUS_VERSION:
str = "RTSP Version not supported";
break;
Fabrice Bellard
committed
default:
str = "Unknown Error";
break;
}
Fabrice Bellard
committed
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
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;
Fabrice Bellard
committed
c->buffer_ptr[0] = '\0';
p = c->buffer;
Fabrice Bellard
committed
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
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
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
} 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;
Fabrice Bellard
committed
if (url_open_dyn_buf(pb) < 0)
return -1;
Fabrice Bellard
committed
/* 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 */
Fabrice Bellard
committed
private_payload_type = RTP_PT_PRIVATE;
Fabrice Bellard
committed
for(i = 0; i < stream->nb_streams; i++) {
st = stream->streams[i];
Michael Niedermayer
committed
if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
Fabrice Bellard
committed
mediatype = "video";
Fabrice Bellard
committed
} else {
Michael Niedermayer
committed
switch(st->codec->codec_type) {
Fabrice Bellard
committed
case CODEC_TYPE_AUDIO:
mediatype = "audio";
break;
case CODEC_TYPE_VIDEO:
mediatype = "video";
break;
default:
mediatype = "application";
break;
}
Fabrice Bellard
committed
}
Fabrice Bellard
committed
/* NOTE: the port indication is not correct in case of
unicast. It is not an issue because RTSP gives it */
Michael Niedermayer
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;
}
url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
Fabrice Bellard
committed
mediatype, port, payload_type);
Fabrice Bellard
committed
if (payload_type >= RTP_PT_PRIVATE) {
/* for private payload type, we need to give more info */
Michael Niedermayer
committed
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 */
Michael Niedermayer
committed
data = st->codec->extradata;
if (data) {
Michael Niedermayer
committed
url_fprintf(pb, "a=fmtp:%d config=", payload_type);
Michael Niedermayer
committed
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 */
Petr Doubek
committed
url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
Fabrice Bellard
committed
path = path1;
if (*path == '/')
path++;
for(stream = first_stream; stream != NULL; stream = stream->next) {
if (!stream->is_feed && stream->fmt == &rtp_muxer &&
Fabrice Bellard
committed
!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
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
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
{
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,
Fabrice Bellard
committed
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;
Fabrice Bellard
committed
/* find which url is asked */
Petr Doubek
committed
url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
Fabrice Bellard
committed
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_muxer) {
Fabrice Bellard
committed
/* 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;
}
Fabrice Bellard
committed
for(stream_index = 0; stream_index < stream->nb_streams;
stream_index++) {
snprintf(buf, sizeof(buf), "%s/streamid=%d",
Fabrice Bellard
committed
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",
av_random(&random_state), av_random(&random_state));
Fabrice Bellard
committed
}
/* find rtp session, and create it if none found */
rtp_c = find_rtp_session(h->session_id);
if (!rtp_c) {
/* 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_new_connection(&c->from_addr, stream, h->session_id,
th->protocol);
Fabrice Bellard
committed
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;
}
}
Fabrice Bellard
committed
/* 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;
}
Fabrice Bellard
committed
/* 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 &&
Fabrice Bellard
committed
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);
Fabrice Bellard
committed
/* 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,
Fabrice Bellard
committed
(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);
}
Fabrice Bellard
committed
/* setup stream */
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
Fabrice Bellard
committed
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
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");
Fabrice Bellard
committed
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,
Fabrice Bellard
committed
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 */
Petr Doubek
committed
url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
Fabrice Bellard
committed
path = path1;