Newer
Older
audio_codec_name = codec->name;
break;
case AVMEDIA_TYPE_VIDEO:
video_bit_rate += st->codecpar->bit_rate;
if (codec) {
if (*video_codec_name)
video_codec_name_extra = "...";
video_codec_name = codec->name;
}
break;
case AVMEDIA_TYPE_DATA:
video_bit_rate += st->codecpar->bit_rate;
break;
default:
abort();
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
avio_printf(pb, "<td align=center> %s <td align=right> %d "
"<td align=right> %d <td> %s %s <td align=right> "
"%d <td> %s %s",
stream->fmt->name, stream->bandwidth,
video_bit_rate / 1000, video_codec_name,
video_codec_name_extra, audio_bit_rate / 1000,
audio_codec_name, audio_codec_name_extra);
if (stream->feed)
avio_printf(pb, "<td>%s", stream->feed->filename);
else
avio_printf(pb, "<td>%s", stream->feed_filename);
avio_printf(pb, "\n");
}
break;
default:
avio_printf(pb, "<td align=center> - <td align=right> - "
"<td align=right> - <td><td align=right> - <td>\n");
break;
}
stream = config.first_stream;
if (stream->feed != stream) {
stream = stream->next;
continue;
}
avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
if (stream->pid) {
avio_printf(pb, "Running as pid %"PRId64".\n", (int64_t) stream->pid);
{
FILE *pid_stat;
char ps_cmd[64];
/* This is somewhat linux specific I guess */
snprintf(ps_cmd, sizeof(ps_cmd),
"ps -o \"%%cpu,cputime\" --no-headers %"PRId64"",
(int64_t) stream->pid);
pid_stat = popen(ps_cmd, "r");
if (pid_stat) {
char cpuperc[10];
char cpuused[64];
if (fscanf(pid_stat, "%9s %63s", cpuperc, cpuused) == 2) {
avio_printf(pb, "Currently using %s%% of the cpu. "
"Total time used %s.\n",
cpuperc, cpuused);
}
fclose(pid_stat);
}
}
avio_printf(pb, "<p>");
}
print_stream_params(pb, stream);
stream = stream->next;
}
avio_printf(pb, "<h2>Connection Status</h2>\n");
avio_printf(pb, "Number of connections: %d / %d<br>\n",
nb_connections, config.nb_max_connections);
avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
current_bandwidth, config.max_bandwidth);
avio_printf(pb, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target "
"bit/s<th>Actual bit/s<th>Bytes transferred\n");
int bitrate;
int j;
bitrate = 0;
Fabrice Bellard
committed
if (c1->stream) {
for (j = 0; j < c1->stream->nb_streams; j++) {
bitrate += c1->stream->streams[j]->codecpar->bit_rate;
else if (c1->feed_streams[j] >= 0)
bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codecpar->bit_rate;
avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s"
"<td align=right>",
i, c1->stream ? c1->stream->filename : "",
c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "", p,
c1->protocol, http_state[c1->state]);
Fabrice Bellard
committed
fmt_bytecount(pb, bitrate);
avio_printf(pb, "<td align=right>");
Fabrice Bellard
committed
fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
avio_printf(pb, "<td align=right>");
Fabrice Bellard
committed
fmt_bytecount(pb, c1->data_count);
avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
avio_printf(pb, "</body>\n</html>\n");
len = avio_close_dyn_buf(pb, &c->pb_buffer);
Fabrice Bellard
committed
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
}
static int open_input_stream(HTTPContext *c, const char *info)
{
char buf[128];
char input_filename[1024];
/* find file name */
if (c->stream->feed) {
strcpy(input_filename, c->stream->feed->feed_filename);
buf_size = FFM_PACKET_SIZE;
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
http_log("Invalid date specification '%s' for stream\n", buf);
} else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
Philip Gladstone
committed
int prebuffer = strtol(buf, 0, 10);
stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
} else {
strcpy(input_filename, c->stream->feed_filename);
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
http_log("Invalid date specification '%s' for stream\n", buf);
if (!input_filename[0]) {
http_log("No filename was specified for stream\n");
return AVERROR(EINVAL);
}
ret = avformat_open_input(&s, input_filename, c->stream->ifmt,
&c->stream->in_opts);
if (ret < 0) {
http_log("Could not open input '%s': %s\n",
input_filename, av_err2str(ret));
Fabrice Bellard
committed
}
Miroslav Slugeň
committed
/* set buffer size */
if (buf_size > 0) {
ret = ffio_set_buf_size(s->pb, buf_size);
if (ret < 0) {
http_log("Failed to set buffer size\n");
return ret;
}
}
Miroslav Slugeň
committed
s->flags |= AVFMT_FLAG_GENPTS;
if (strcmp(s->iformat->name, "ffm") &&
(ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
http_log("Could not find stream info for input '%s'\n", input_filename);
avformat_close_input(&s);
/* choose stream as clock source (we favor the video stream if
* present) for packet sending */
Fabrice Bellard
committed
c->pts_stream_index = 0;
for(i=0;i<c->stream->nb_streams;i++) {
if (c->pts_stream_index == 0 &&
c->stream->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
Fabrice Bellard
committed
c->pts_stream_index = i;
}
}
if (c->fmt_in->iformat->read_seek)
Baptiste Coudurier
committed
av_seek_frame(c->fmt_in, -1, stream_pos, 0);
Fabrice Bellard
committed
/* set the start time (needed for maxtime and RTP packet timing) */
c->start_time = cur_time;
c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard
committed
/* return the server clock (in us) */
static int64_t get_server_clock(HTTPContext *c)
Fabrice Bellard
committed
{
Fabrice Bellard
committed
/* compute current pts value from system time */
return (cur_time - c->start_time) * 1000;
Fabrice Bellard
committed
}
/* return the estimated time (in us) at which the current packet must be sent */
Fabrice Bellard
committed
static int64_t get_packet_send_clock(HTTPContext *c)
Fabrice Bellard
committed
{
Fabrice Bellard
committed
int bytes_left, bytes_sent, frame_bytes;
Fabrice Bellard
committed
frame_bytes = c->cur_frame_bytes;
if (frame_bytes <= 0)
Fabrice Bellard
committed
return c->cur_pts;
bytes_left = c->buffer_end - c->buffer_ptr;
bytes_sent = frame_bytes - bytes_left;
return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
Fabrice Bellard
committed
}
static int http_prepare_data(HTTPContext *c)
{
int i, len, ret;
AVFormatContext *ctx;
Fabrice Bellard
committed
switch(c->state) {
case HTTPSTATE_SEND_DATA_HEADER:
ctx = avformat_alloc_context();
if (!ctx)
return AVERROR(ENOMEM);
c->pfmt_ctx = ctx;
av_dict_copy(&(c->pfmt_ctx->metadata), c->stream->metadata, 0);
c->pfmt_ctx->streams = av_mallocz_array(c->stream->nb_streams,
sizeof(AVStream *));
if (!c->pfmt_ctx->streams)
return AVERROR(ENOMEM);
Mike Williams
committed
for(i=0;i<c->stream->nb_streams;i++) {
c->pfmt_ctx->streams[i] = av_mallocz(sizeof(AVStream));
/* if file or feed, then just take streams from FFServerStream
* struct */
Fabrice Bellard
committed
c->stream->feed == c->stream)
Fabrice Bellard
committed
else
src = c->stream->feed->streams[c->stream->feed_streams[i]];
*(c->pfmt_ctx->streams[i]) = *src;
c->pfmt_ctx->streams[i]->priv_data = 0;
/* XXX: should be done in AVStream, not in codec */
c->pfmt_ctx->streams[i]->codec->frame_number = 0;
Fabrice Bellard
committed
}
/* set output format parameters */
c->pfmt_ctx->oformat = c->stream->fmt;
c->pfmt_ctx->nb_streams = c->stream->nb_streams;
Fabrice Bellard
committed
c->got_key_frame = 0;
/* prepare header and save header data in a stream */
if (avio_open_dyn_buf(&c->pfmt_ctx->pb) < 0) {
Fabrice Bellard
committed
/* XXX: potential leak */
return -1;
}
c->pfmt_ctx->pb->seekable = 0;
Fabrice Bellard
committed
Baptiste Coudurier
committed
/*
* HACK to avoid MPEG-PS muxer to spit many underflow errors
Baptiste Coudurier
committed
* Default value from FFmpeg
* Try to set it using configuration option
Baptiste Coudurier
committed
*/
c->pfmt_ctx->max_delay = (int)(0.7*AV_TIME_BASE);
Baptiste Coudurier
committed
if ((ret = avformat_write_header(c->pfmt_ctx, NULL)) < 0) {
http_log("Error writing output header for stream '%s': %s\n",
c->stream->filename, av_err2str(ret));
return ret;
av_dict_free(&c->pfmt_ctx->metadata);
Fabrice Bellard
committed
len = avio_close_dyn_buf(c->pfmt_ctx->pb, &c->pb_buffer);
Fabrice Bellard
committed
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 */
/* 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;
else {
AVPacket pkt;
redo:
ret = av_read_frame(c->fmt_in, &pkt);
if (ret < 0) {
if (c->stream->feed) {
/* 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 */
}
if (ret == AVERROR(EAGAIN)) {
/* input not ready, come back later */
return 0;
}
if (c->stream->loop) {
avformat_close_input(&c->fmt_in);
if (open_input_stream(c, "") < 0)
goto no_loop;
goto redo;
Fabrice Bellard
committed
} else {
/* must send trailer now because EOF or error */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
}
} else {
int source_index = pkt.stream_index;
if (c->first_pts == AV_NOPTS_VALUE && pkt.dts != AV_NOPTS_VALUE) {
c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
c->start_time = cur_time;
}
/* 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;
if (c->switch_feed_streams[i] == pkt.stream_index)
if (pkt.flags & AV_PKT_FLAG_KEY)
if (c->switch_feed_streams[i] >= 0)
c->switch_pending = 1;
if (c->stream->feed_streams[i] == pkt.stream_index) {
AVStream *st = c->fmt_in->streams[source_index];
if (pkt.flags & AV_PKT_FLAG_KEY &&
(st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
c->stream->nb_streams == 1))
Baptiste Coudurier
committed
c->got_key_frame = 1;
if (!c->stream->send_on_key || c->got_key_frame)
goto send_it;
}
}
} else {
AVCodecContext *codec;
AVStream *ist, *ost;
send_it:
ist = c->fmt_in->streams[source_index];
* output streams (one for each RTP connection).
* XXX: need more abstract handling */
if (c->is_packetized) {
/* compute send time and duration */
if (pkt.dts != AV_NOPTS_VALUE) {
c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
c->cur_pts -= c->first_pts;
}
c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
/* find RTP context */
c->packet_stream_index = pkt.stream_index;
ctx = c->rtp_ctx[c->packet_stream_index];
if(!ctx) {
av_packet_unref(&pkt);
codec = ctx->streams[0]->codec;
/* only one stream per RTP connection */
pkt.stream_index = 0;
} else {
codec = ctx->streams[pkt.stream_index]->codec;
}
if (c->is_packetized) {
int max_packet_size;
Ronald S. Bultje
committed
if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
else
max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
ret = ffio_open_dyn_packet_buf(&ctx->pb,
max_packet_size);
} else
ret = avio_open_dyn_buf(&ctx->pb);
if (ret < 0) {
/* XXX: potential leak */
return -1;
}
ost = ctx->streams[pkt.stream_index];
pkt.dts = av_rescale_q(pkt.dts, ist->time_base,
ost->time_base);
pkt.pts = av_rescale_q(pkt.pts, ist->time_base,
ost->time_base);
pkt.duration = av_rescale_q(pkt.duration, ist->time_base,
ost->time_base);
if ((ret = av_write_frame(ctx, &pkt)) < 0) {
http_log("Error writing frame to output for stream '%s': %s\n",
c->stream->filename, av_err2str(ret));
len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
c->cur_frame_bytes = len;
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
codec->frame_number++;
if (len == 0) {
av_packet_unref(&pkt);
Philip Gladstone
committed
}
av_packet_unref(&pkt);
break;
default:
case HTTPSTATE_SEND_DATA_TRAILER:
/* last packet test ? */
Fabrice Bellard
committed
if (c->last_packet_sent || c->is_packetized)
if (avio_open_dyn_buf(&ctx->pb) < 0) {
Fabrice Bellard
committed
/* XXX: potential leak */
return -1;
}
c->pfmt_ctx->pb->seekable = 0;
Fabrice Bellard
committed
av_write_trailer(ctx);
len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
Fabrice Bellard
committed
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)
*/
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)
/* state change requested */
break;
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;
Ronald S. Bultje
committed
if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
/* RTP packets are sent inside the RTSP TCP connection */
int interleaved_index, size;
uint8_t header[4];
HTTPContext *rtsp_c;
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)
if (avio_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;
avio_write(pb, header, 4);
/* write RTP packet data */
c->buffer_ptr += 4;
avio_write(pb, c->buffer_ptr, len);
size = avio_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
rtsp_c->packet_buffer_ptr += len;
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 */
Fabrice Bellard
committed
rtsp_c->state = RTSPSTATE_SEND_PACKET;
break;
Fabrice Bellard
committed
/* all data has been sent */
av_freep(&c->packet_buffer);
} else {
/* send RTP packet directly in UDP */
ffurl_write(c->rtp_handles[c->packet_stream_index],
c->buffer_ptr, len);
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 (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
/* error : close connection */
return -1;
}
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) {
http_log("Stream feed '%s' was not opened\n",
c->stream->feed_filename);
return AVERROR(EINVAL);
}
Philip Gladstone
committed
/* Don't permit writing to this one */
if (c->stream->readonly) {
http_log("Cannot write to read-only file '%s'\n",
c->stream->feed_filename);
return AVERROR(EINVAL);
}
Philip Gladstone
committed
/* open feed */
fd = open(c->stream->feed_filename, O_RDWR);
ret = AVERROR(errno);
http_log("Could not open feed file '%s': %s\n",
c->stream->feed_filename, strerror(errno));
return ret;
if (c->stream->truncate) {
/* truncate feed file */
ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
ret = AVERROR(errno);
http_log("Error truncating feed file '%s': %s\n",
c->stream->feed_filename, strerror(errno));
return ret;
} else {
ret64 = ffm_read_write_index(fd);
if (ret64 < 0) {
http_log("Error reading write index from feed file '%s': %s\n",
c->stream->feed_filename, strerror(errno));
c->stream->feed_write_index = ret64;
}
c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd),
FFM_PACKET_SIZE);
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;
c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
static int http_receive_data(HTTPContext *c)
{
HTTPContext *c1;
Ronald S. Bultje
committed
int len, loop_run = 0;
Ronald S. Bultje
committed
while (c->chunked_encoding && !c->chunk_size &&
c->buffer_end > c->buffer_ptr) {
/* read chunk header, if present */
len = recv(c->fd, c->buffer_ptr, 1, 0);
if (len < 0) {
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
Ronald S. Bultje
committed
/* error : close connection */
goto fail;
Ronald S. Bultje
committed
} else if (len == 0) {
/* end of connection : close it */
goto fail;
} else if (c->buffer_ptr - c->buffer >= 2 &&
!memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
c->chunk_size = strtol(c->buffer, 0, 16);
if (c->chunk_size == 0) // end of stream
goto fail;
c->buffer_ptr = c->buffer;
break;
} else if (++loop_run > 10)
Ronald S. Bultje
committed
/* no chunk header, abort */
goto fail;
else
Ronald S. Bultje
committed
c->buffer_ptr++;
}
Ronald S. Bultje
committed
if (c->buffer_end > c->buffer_ptr) {
len = recv(c->fd, c->buffer_ptr,
FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
if (len < 0) {
if (ff_neterrno() != AVERROR(EAGAIN) &&
ff_neterrno() != AVERROR(EINTR))
/* error : close connection */
goto fail;
} else if (len == 0)
/* end of connection : close it */
goto fail;
Ronald S. Bultje
committed
c->chunk_size -= len;
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;
}
}
FFServerStream *feed = c->stream;
* if header */
/* XXX: use llseek or url_seek
* XXX: Should probably fail? */
if (lseek(c->feed_fd, feed->feed_write_index, SEEK_SET) == -1)
http_log("Seek to %"PRId64" failed\n", feed->feed_write_index);
if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
http_log("Error writing to feed file: %s\n", strerror(errno));
goto fail;
}
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 */
if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
http_log("Error writing index to feed file: %s\n",
strerror(errno));
goto fail;
}
for(c1 = first_http_ctx; c1; c1 = c1->next) {
if (c1->state == HTTPSTATE_WAIT_FEED &&
c1->stream->feed == c->stream->feed)
Philip Gladstone
committed
} else {
/* We have a header in our hands that contains useful data */
AVFormatContext *s = avformat_alloc_context();
Philip Gladstone
committed
int i;
/* use feed output format name to find corresponding input format */
fmt_in = av_find_input_format(feed->fmt->name);
if (!fmt_in)
goto fail;
pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
0, NULL, NULL, NULL, NULL);
if (!pb)
goto fail;
s->pb = pb;
if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
av_freep(&pb);
Philip Gladstone
committed
/* Now we have the actual streams */
avformat_close_input(&s);
av_freep(&pb);
http_log("Feed '%s' stream number does not match registered feed\n",
c->stream->feed_filename);
Philip Gladstone
committed
goto fail;
}
for (i = 0; i < s->nb_streams; i++) {
AVStream *fst = feed->streams[i];
AVStream *st = s->streams[i];
Martin Storsjö
committed
avcodec_copy_context(fst->codec, st->codec);
avformat_close_input(&s);
av_freep(&pb);
}
c->buffer_ptr = c->buffer;
}
return 0;
fail:
c->stream->feed_opened = 0;
close(c->feed_fd);
/* wake up any waiting connections to stop waiting for feed */
for(c1 = first_http_ctx; c1; c1 = c1->next) {
if (c1->state == HTTPSTATE_WAIT_FEED &&
c1->stream->feed == c->stream->feed)
c1->state = HTTPSTATE_SEND_DATA_TRAILER;
}
Fabrice Bellard
committed
/********************************************************************/
/* RTSP handling */
static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
{
const char *str;
time_t ti;
Fabrice Bellard
committed
char buf2[32];
str = RTSP_STATUS_CODE2STRING(error_number);
if (!str)
Fabrice Bellard
committed
str = "Unknown Error";
avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
Fabrice Bellard
committed
/* output GMT time */
ti = time(NULL);
tm = gmtime(&ti);
strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
Fabrice Bellard
committed
}
static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
{
rtsp_reply_header(c, error_number);
Fabrice Bellard
committed
}
static int rtsp_parse_request(HTTPContext *c)
{
const char *p, *p1, *p2;
char cmd[32];
char url[1024];
char protocol[32];
char line[1024];
int len;
RTSPMessageHeader header1 = { 0 }, *header = &header1;
Fabrice Bellard
committed
c->buffer_ptr[0] = '\0';
p = c->buffer;
Fabrice Bellard
committed
get_word(cmd, sizeof(cmd), &p);
get_word(url, sizeof(url), &p);
get_word(protocol, sizeof(protocol), &p);
av_strlcpy(c->method, cmd, sizeof(c->method));
av_strlcpy(c->url, url, sizeof(c->url));
av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
Fabrice Bellard
committed
if (avio_open_dyn_buf(&c->pb) < 0) {
Fabrice Bellard
committed
/* XXX: cannot do more */
c->pb = NULL; /* safety */
return -1;
}
/* check version name */
if (strcmp(protocol, "RTSP/1.0")) {
Fabrice Bellard
committed
rtsp_reply_error(c, RTSP_STATUS_VERSION);
goto the_end;
}
/* parse each header line */
/* skip to next line */
while (*p != '\n' && *p != '\0')
p++;
if (*p == '\n')
p++;
while (*p != '\0') {
p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
Fabrice Bellard
committed
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';
ff_rtsp_parse_line(NULL, header, line, NULL, NULL);
Fabrice Bellard
committed
p = p1 + 1;
}
/* handle sequence number */
c->seq = header->seq;
if (!strcmp(cmd, "DESCRIBE"))
Fabrice Bellard
committed
rtsp_cmd_describe(c, url);
else if (!strcmp(cmd, "OPTIONS"))
rtsp_cmd_options(c, url);
else if (!strcmp(cmd, "SETUP"))
Fabrice Bellard
committed
rtsp_cmd_setup(c, url, header);
else if (!strcmp(cmd, "PLAY"))
Fabrice Bellard
committed
rtsp_cmd_play(c, url, header);
else if (!strcmp(cmd, "PAUSE"))
rtsp_cmd_interrupt(c, url, header, 1);
else if (!strcmp(cmd, "TEARDOWN"))
rtsp_cmd_interrupt(c, url, header, 0);
Fabrice Bellard
committed
rtsp_reply_error(c, RTSP_STATUS_METHOD);
Fabrice Bellard
committed
the_end:
len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
Fabrice Bellard
committed
c->pb = NULL; /* safety */
if (len < 0)
Fabrice Bellard
committed
/* XXX: cannot do more */
return -1;
Fabrice Bellard
committed
c->buffer_ptr = c->pb_buffer;
c->buffer_end = c->pb_buffer + len;
c->state = RTSPSTATE_SEND_REPLY;
return 0;
}
static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
Fabrice Bellard
committed
struct in_addr my_ip)
Fabrice Bellard
committed
{
AVStream *avs = NULL;
AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
*pbuffer = NULL;
avc = avformat_alloc_context();
if (!avc || !rtp_format)
Fabrice Bellard
committed
return -1;
av_dict_set(&avc->metadata, "title",
entry ? entry->value : "No Title", 0);
avc->nb_streams = stream->nb_streams;
if (stream->is_multicast) {
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
inet_ntoa(stream->multicast_ip),
stream->multicast_port, stream->multicast_ttl);
} else
snprintf(avc->filename, 1024, "rtp://0.0.0.0");
avc->streams = av_malloc_array(avc->nb_streams, sizeof(*avc->streams));
if (!avc->streams)
goto sdp_done;
avs = av_malloc_array(avc->nb_streams, sizeof(*avs));
if (!avs)
goto sdp_done;
Fabrice Bellard
committed
for(i = 0; i < stream->nb_streams; i++) {
avc->streams[i] = &avs[i];
avc->streams[i]->codec = stream->streams[i]->codec;
avcodec_parameters_from_context(stream->streams[i]->codecpar, stream->streams[i]->codec);
avc->streams[i]->codecpar = stream->streams[i]->codecpar;
Fabrice Bellard
committed
}
#define PBUFFER_SIZE 2048
*pbuffer = av_mallocz(PBUFFER_SIZE);