Newer
Older
Fabrice Bellard
committed
}
static void rtsp_cmd_options(HTTPContext *c, const char *url)
{
// rtsp_reply_header(c, RTSP_STATUS_OK);
avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
avio_printf(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;
int content_length;
socklen_t len;
Fabrice Bellard
committed
struct sockaddr_in my_addr;
Fabrice Bellard
committed
/* find which url is asked */
av_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 && !strcmp(stream->fmt->name, "rtp") &&
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
if (content_length < 0) {
rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
return;
}
rtsp_reply_header(c, RTSP_STATUS_OK);
avio_printf(c->pb, "Content-Base: %s/\r\n", url);
avio_printf(c->pb, "Content-Type: application/sdp\r\n");
avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
avio_printf(c->pb, "\r\n");
avio_write(c->pb, content, content_length);
Fabrice Bellard
committed
}
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(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
Fabrice Bellard
committed
{
RTSPTransportField *th;
int i;
for(i=0;i<h->nb_transports;i++) {
th = &h->transports[i];
Ronald S. Bultje
committed
if (th->lower_transport == lower_transport)
Fabrice Bellard
committed
return th;
}
return NULL;
}
static void rtsp_cmd_setup(HTTPContext *c, const char *url,
RTSPMessageHeader *h)
Fabrice Bellard
committed
{
FFStream *stream;
Fabrice Bellard
committed
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 */
av_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 && !strcmp(stream->fmt->name, "rtp")) {
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') {
unsigned random0 = av_lfg_get(&random_state);
unsigned random1 = av_lfg_get(&random_state);
snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
Fabrice Bellard
committed
/* find rtp session, and create it if none found */
rtp_c = find_rtp_session(h->session_id);
if (!rtp_c) {
Ronald S. Bultje
committed
th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
Ronald S. Bultje
committed
th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
if (!th) {
rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
return;
}
}
rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
Ronald S. Bultje
committed
th->lower_transport);
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);
Ronald S. Bultje
committed
if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_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
/* setup stream */
if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
Fabrice Bellard
committed
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 */
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
Fabrice Bellard
committed
switch(rtp_c->rtp_protocol) {
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
Fabrice Bellard
committed
"client_port=%d-%d;server_port=%d-%d",
th->client_port_min, th->client_port_max,
rtp_port, rtcp_port);
Fabrice Bellard
committed
break;
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
Fabrice Bellard
committed
stream_index * 2, stream_index * 2 + 1);
break;
default:
break;
}
if (setup.transport_option[0] != '\0')
avio_printf(c->pb, ";%s", setup.transport_option);
avio_printf(c->pb, "\r\n");
Fabrice Bellard
committed
Fabrice Bellard
committed
}
/* 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;
int s, len;
Fabrice Bellard
committed
rtp_c = find_rtp_session(session_id);
if (!rtp_c)
return NULL;
/* find which url is asked */
av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
Fabrice Bellard
committed
path = path1;
if (*path == '/')
path++;
if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
for(s=0; s<rtp_c->stream->nb_streams; ++s) {
snprintf(buf, sizeof(buf), "%s/streamid=%d",
rtp_c->stream->filename, s);
if(!strncmp(path, buf, sizeof(buf))) {
// XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
return rtp_c;
}
}
len = strlen(path);
if (len > 0 && path[len - 1] == '/' &&
!strncmp(path, rtp_c->stream->filename, len - 1))
return rtp_c;
Fabrice Bellard
committed
}
static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
Fabrice Bellard
committed
{
HTTPContext *rtp_c;
rtp_c = find_rtp_session_with_url(url, h->session_id);
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_SESSION);
return;
}
Fabrice Bellard
committed
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
rtp_c->state != HTTPSTATE_WAIT_FEED &&
rtp_c->state != HTTPSTATE_READY) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
rtp_c->state = HTTPSTATE_SEND_DATA;
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
avio_printf(c->pb, "\r\n");
Fabrice Bellard
committed
}
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
Fabrice Bellard
committed
{
HTTPContext *rtp_c;
rtp_c = find_rtp_session_with_url(url, h->session_id);
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_SESSION);
return;
}
Fabrice Bellard
committed
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
rtp_c->state != HTTPSTATE_WAIT_FEED) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
Fabrice Bellard
committed
rtp_c->state = HTTPSTATE_READY;
Giancarlo Formicuccia
committed
rtp_c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
avio_printf(c->pb, "\r\n");
Fabrice Bellard
committed
}
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
Fabrice Bellard
committed
{
HTTPContext *rtp_c;
rtp_c = find_rtp_session_with_url(url, h->session_id);
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_SESSION);
return;
}
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
Mike Williams
committed
avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
Mike Williams
committed
/* abort the session */
close_connection(rtp_c);
Fabrice Bellard
committed
}
/********************************************************************/
/* RTP handling */
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
FFStream *stream, const char *session_id,
Ronald S. Bultje
committed
enum RTSPLowerTransport rtp_protocol)
Fabrice Bellard
committed
{
HTTPContext *c = NULL;
Fabrice Bellard
committed
/* XXX: should output a warning page when coming
close to the connection limit */
if (nb_connections >= nb_max_connections)
goto fail;
Fabrice Bellard
committed
/* add a new connection */
c = av_mallocz(sizeof(HTTPContext));
if (!c)
goto fail;
Fabrice Bellard
committed
c->fd = -1;
c->poll_entry = NULL;
c->from_addr = *from_addr;
Fabrice Bellard
committed
c->buffer_size = IOBUFFER_INIT_SIZE;
c->buffer = av_malloc(c->buffer_size);
if (!c->buffer)
goto fail;
nb_connections++;
c->stream = stream;
av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
Fabrice Bellard
committed
c->state = HTTPSTATE_READY;
c->is_packetized = 1;
Fabrice Bellard
committed
/* protocol is shown in statistics */
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
proto_str = "TCP";
break;
default:
proto_str = "???";
break;
}
av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
Fabrice Bellard
committed
current_bandwidth += stream->bandwidth;
Fabrice Bellard
committed
c->next = first_http_ctx;
first_http_ctx = c;
return c;
Fabrice Bellard
committed
fail:
if (c) {
av_free(c->buffer);
av_free(c);
}
return NULL;
}
/* add a new RTP stream in an RTP connection (used in RTSP SETUP
command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
Fabrice Bellard
committed
used. */
static int rtp_new_av_stream(HTTPContext *c,
int stream_index, struct sockaddr_in *dest_addr,
HTTPContext *rtsp_c)
Fabrice Bellard
committed
{
AVFormatContext *ctx;
AVStream *st;
char *ipaddr;
Fabrice Bellard
committed
/* now we can open the relevant output stream */
ctx = avformat_alloc_context();
Fabrice Bellard
committed
if (!ctx)
return -1;
ctx->oformat = av_guess_format("rtp", NULL, NULL);
Fabrice Bellard
committed
st = av_mallocz(sizeof(AVStream));
if (!st)
goto fail;
ctx->nb_streams = 1;
Mike Williams
committed
ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
Mike Williams
committed
goto fail;
Fabrice Bellard
committed
ctx->streams[0] = st;
c->stream->feed == c->stream)
Fabrice Bellard
committed
memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
Fabrice Bellard
committed
c->stream->feed->streams[c->stream->feed_streams[stream_index]],
sizeof(AVStream));
st->priv_data = NULL;
/* build destination RTP address */
ipaddr = inet_ntoa(dest_addr->sin_addr);
switch(c->rtp_protocol) {
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
/* XXX: also pass as parameter to function ? */
if (c->stream->is_multicast) {
int ttl;
ttl = c->stream->multicast_ttl;
if (!ttl)
ttl = 16;
snprintf(ctx->filename, sizeof(ctx->filename),
"rtp://%s:%d?multicast=1&ttl=%d",
ipaddr, ntohs(dest_addr->sin_port), ttl);
} else {
snprintf(ctx->filename, sizeof(ctx->filename),
"rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
}
Fabrice Bellard
committed
if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
Fabrice Bellard
committed
goto fail;
c->rtp_handles[stream_index] = h;
max_packet_size = h->max_packet_size;
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
/* RTP/TCP case */
c->rtsp_c = rtsp_c;
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
break;
default:
Fabrice Bellard
committed
goto fail;
}
http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
ipaddr, ntohs(dest_addr->sin_port),
c->stream->filename, stream_index, c->protocol);
Fabrice Bellard
committed
/* normally, no packets should be output here, but the packet size may be checked */
if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
Fabrice Bellard
committed
/* XXX: close stream */
goto fail;
}
if (avformat_write_header(ctx, NULL) < 0) {
Fabrice Bellard
committed
fail:
if (h)
Fabrice Bellard
committed
av_free(ctx);
return -1;
}
avio_close_dyn_buf(ctx->pb, &dummy_buf);
Fabrice Bellard
committed
av_free(dummy_buf);
Fabrice Bellard
committed
c->rtp_ctx[stream_index] = ctx;
return 0;
}
/********************************************************************/
/* ffserver initialization */
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
Fabrice Bellard
committed
{
AVStream *fst;
if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
return NULL;
Fabrice Bellard
committed
fst = av_mallocz(sizeof(AVStream));
if (!fst)
return NULL;
fst->codec = avcodec_alloc_context3(NULL);
memcpy(fst->codec, codec, sizeof(AVCodecContext));
if (codec->extradata_size) {
fst->codec->extradata = av_mallocz(codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(fst->codec->extradata, codec->extradata,
codec->extradata_size);
}
} else {
/* live streams must use the actual feed's codec since it may be
* updated later to carry extradata needed by the streams.
*/
fst->codec = codec;
}
Fabrice Bellard
committed
fst->priv_data = av_mallocz(sizeof(FeedData));
Philip Gladstone
committed
fst->index = stream->nb_streams;
avpriv_set_pts_info(fst, 33, 1, 90000);
fst->sample_aspect_ratio = codec->sample_aspect_ratio;
Fabrice Bellard
committed
stream->streams[stream->nb_streams++] = fst;
return fst;
}
static int add_av_stream(FFStream *feed, AVStream *st)
{
AVStream *fst;
AVCodecContext *av, *av1;
int i;
Michael Niedermayer
committed
av = st->codec;
Michael Niedermayer
committed
av1 = st->codec;
Philip Gladstone
committed
if (av1->codec_id == av->codec_id &&
av1->codec_type == av->codec_type &&
av1->bit_rate == av->bit_rate) {
switch(av->codec_type) {
case AVMEDIA_TYPE_AUDIO:
if (av1->channels == av->channels &&
av1->sample_rate == av->sample_rate)
return i;
case AVMEDIA_TYPE_VIDEO:
if (av1->width == av->width &&
av1->height == av->height &&
av1->time_base.den == av->time_base.den &&
av1->time_base.num == av->time_base.num &&
return i;
Philip Gladstone
committed
default:
if (!fst)
return -1;
return feed->nb_streams - 1;
}
static void remove_stream(FFStream *stream)
Fabrice Bellard
committed
{
FFStream **ps;
ps = &first_stream;
while (*ps != NULL) {
Fabrice Bellard
committed
*ps = (*ps)->next;
Fabrice Bellard
committed
ps = &(*ps)->next;
}
}
/* specific mpeg4 handling : we extract the raw parameters */
static void extract_mpeg4_header(AVFormatContext *infile)
{
int mpeg4_count, i, size;
AVPacket pkt;
AVStream *st;
infile->flags |= AVFMT_FLAG_NOFILLIN | AVFMT_FLAG_NOPARSE;
mpeg4_count = 0;
for(i=0;i<infile->nb_streams;i++) {
st = infile->streams[i];
if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
Michael Niedermayer
committed
st->codec->extradata_size == 0) {
mpeg4_count++;
}
}
if (!mpeg4_count)
return;
Philip Gladstone
committed
printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
while (mpeg4_count > 0) {
if (av_read_frame(infile, &pkt) < 0)
break;
st = infile->streams[pkt.stream_index];
if (st->codec->codec_id == AV_CODEC_ID_MPEG4 &&
Michael Niedermayer
committed
st->codec->extradata_size == 0) {
av_freep(&st->codec->extradata);
/* fill extradata with the header */
/* XXX: we make hard suppositions here ! */
p = pkt.data;
while (p < pkt.data + pkt.size - 4) {
/* stop when vop header is found */
if (p[0] == 0x00 && p[1] == 0x00 &&
p[2] == 0x01 && p[3] == 0xb6) {
size = p - pkt.data;
Panagiotis Issaris
committed
// av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
st->codec->extradata = av_mallocz(size + FF_INPUT_BUFFER_PADDING_SIZE);
Michael Niedermayer
committed
st->codec->extradata_size = size;
memcpy(st->codec->extradata, pkt.data, size);
break;
}
p++;
}
mpeg4_count--;
}
av_free_packet(&pkt);
}
}
Fabrice Bellard
committed
/* compute the needed AVStream for each file */
static void build_file_streams(void)
Fabrice Bellard
committed
{
FFStream *stream, *stream_next;
Fabrice Bellard
committed
/* gather all streams */
for(stream = first_stream; stream != NULL; stream = stream_next) {
Fabrice Bellard
committed
stream_next = stream->next;
if (stream->stream_type == STREAM_TYPE_LIVE &&
!stream->feed) {
/* the stream comes from a file */
/* try to open the file */
/* open stream */
if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
Fabrice Bellard
committed
/* specific case : if transport stream output to RTP,
we use a raw transport stream reader */
av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
Fabrice Bellard
committed
}
http_log("Opening file '%s'\n", stream->feed_filename);
if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
Fabrice Bellard
committed
/* remove stream (no need to spend more time on it) */
fail:
remove_stream(stream);
} else {
/* find all the AVStreams inside and reference them in
'stream' */
if (avformat_find_stream_info(infile, NULL) < 0) {
http_log("Could not find codec parameters from '%s'\n",
Fabrice Bellard
committed
stream->feed_filename);
avformat_close_input(&infile);
Fabrice Bellard
committed
goto fail;
}
extract_mpeg4_header(infile);
for(i=0;i<infile->nb_streams;i++)
add_av_stream1(stream, infile->streams[i]->codec, 1);
avformat_close_input(&infile);
Fabrice Bellard
committed
}
}
}
}
static void build_feed_streams(void)
/* gather all streams */
for(stream = first_stream; stream != NULL; stream = stream->next) {
feed = stream->feed;
if (feed) {
if (stream->is_feed) {
for(i=0;i<stream->nb_streams;i++)
} else {
/* we handle a stream coming from a feed */
for(i=0;i<stream->nb_streams;i++)
stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
}
}
}
/* create feed files if needed */
for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
int fd;
if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
/* See if it matches */
int matches = 0;
if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
Miroslav Slugeň
committed
/* set buffer size */
ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
/* Now see if it matches */
if (s->nb_streams == feed->nb_streams) {
matches = 1;
for(i=0;i<s->nb_streams;i++) {
AVStream *sf, *ss;
sf = feed->streams[i];
ss = s->streams[i];
if (sf->index != ss->index ||
sf->id != ss->id) {
http_log("Index & Id do not match for stream %d (%s)\n",
Fabrice Bellard
committed
i, feed->feed_filename);
matches = 0;
} else {
AVCodecContext *ccf, *ccs;
Michael Niedermayer
committed
ccf = sf->codec;
ccs = ss->codec;
#define CHECK_CODEC(x) (ccf->x != ccs->x)
if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
http_log("Codecs do not match for stream %d\n", i);
matches = 0;
} else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
http_log("Codec bitrates do not match for stream %d\n", i);
matches = 0;
} else if (ccf->codec_type == AVMEDIA_TYPE_VIDEO) {
if (CHECK_CODEC(time_base.den) ||
CHECK_CODEC(time_base.num) ||
CHECK_CODEC(width) ||
CHECK_CODEC(height)) {
http_log("Codec width, height and framerate do not match for stream %d\n", i);
matches = 0;
}
} else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
if (CHECK_CODEC(sample_rate) ||
CHECK_CODEC(channels) ||
CHECK_CODEC(frame_size)) {
http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
matches = 0;
}
} else {
http_log("Unknown codec type\n");
matches = 0;
}
}
break;
}
http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
feed->feed_filename, s->nb_streams, feed->nb_streams);
avformat_close_input(&s);
http_log("Deleting feed file '%s' as it appears to be corrupt\n",
feed->feed_filename);
Philip Gladstone
committed
if (!matches) {
if (feed->readonly) {
http_log("Unable to delete feed file '%s' as it is marked readonly\n",
Philip Gladstone
committed
feed->feed_filename);
exit(1);
}
unlink(feed->feed_filename);
Philip Gladstone
committed
}
}
if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
AVFormatContext s1 = {0}, *s = &s1;
Philip Gladstone
committed
if (feed->readonly) {
http_log("Unable to create feed file '%s' as it is marked readonly\n",
Philip Gladstone
committed
feed->feed_filename);
exit(1);
}
if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_filename);
s->oformat = feed->fmt;
if (avformat_write_header(s, NULL) < 0) {
http_log("Container doesn't support the required parameters\n");
Alex Beregszaszi
committed
exit(1);
}
/* XXX: need better api */
av_freep(&s->priv_data);
}
/* get feed size and write index */
fd = open(feed->feed_filename, O_RDONLY);
if (fd < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
feed->feed_size = lseek(fd, 0, SEEK_END);
/* ensure that we do not wrap before the end of file */
if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
feed->feed_max_size = feed->feed_size;
close(fd);
}
}
/* compute the bandwidth used by each stream */
static void compute_bandwidth(void)
{
FFStream *stream;
for(stream = first_stream; stream != NULL; stream = stream->next) {
bandwidth = 0;
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
Michael Niedermayer
committed
switch(st->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
case AVMEDIA_TYPE_VIDEO:
Michael Niedermayer
committed
bandwidth += st->codec->bit_rate;
break;
default:
break;
}
}
stream->bandwidth = (bandwidth + 999) / 1000;
}
}
static void add_codec(FFStream *stream, AVCodecContext *av)
if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
case AVMEDIA_TYPE_AUDIO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->sample_rate == 0)
av->sample_rate = 22050;
if (av->channels == 0)
av->channels = 1;
break;
case AVMEDIA_TYPE_VIDEO:
if (av->time_base.num == 0){
av->time_base.den = 5;
av->time_base.num = 1;
Michael Niedermayer
committed
}
if (av->width == 0 || av->height == 0) {
av->width = 160;
av->height = 128;
}
/* Bitrate tolerance is less for streaming */
if (av->bit_rate_tolerance == 0)
Baptiste Coudurier
committed
av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
(int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
if (av->qmin == 0)
av->qmin = 3;
if (av->qmax == 0)
av->qmax = 31;
if (av->max_qdiff == 0)
av->max_qdiff = 3;
av->qcompress = 0.5;
av->qblur = 0.5;
Philip Gladstone
committed
av->nsse_weight = 8;
av->frame_skip_cmp = FF_CMP_DCTMAX;
Philip Gladstone
committed
av->rc_buffer_aggressivity = 1.0;
if (!av->rc_eq)
av->rc_eq = av_strdup("tex^qComp");
if (!av->i_quant_factor)
Michael Niedermayer
committed
av->i_quant_factor = -0.8;
if (!av->b_quant_factor)
av->b_quant_factor = 1.25;
if (!av->b_quant_offset)
av->b_quant_offset = 1.25;
if (!av->rc_max_rate)
av->rc_max_rate = av->bit_rate * 2;
Philip Gladstone
committed
if (av->rc_max_rate && !av->rc_buffer_size) {
av->rc_buffer_size = av->rc_max_rate;
}
Philip Gladstone
committed
default:
}
st = av_mallocz(sizeof(AVStream));
if (!st)
return;
st->codec = avcodec_alloc_context3(NULL);
Michael Niedermayer
committed
memcpy(st->codec, av, sizeof(AVCodecContext));
static enum AVCodecID opt_audio_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
Philip Gladstone
committed
if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
Philip Gladstone
committed
return p->id;
}
static enum AVCodecID opt_video_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
Philip Gladstone
committed
if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
Philip Gladstone
committed
return p->id;
}
Michael Niedermayer
committed
static int ffserver_opt_default(const char *opt, const char *arg,
AVCodecContext *avctx, int type)
{
int ret = 0;
const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
if(o)
ret = av_opt_set(avctx, opt, arg, 0);
return ret;
static int ffserver_opt_preset(const char *arg,
AVCodecContext *avctx, int type,
enum AVCodecID *audio_id, enum AVCodecID *video_id)
{
FILE *f=NULL;
char filename[1000], tmp[1000], tmp2[1000], line[1000];
int ret = 0;
AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
codec ? codec->name : NULL))) {
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
fprintf(stderr, "File for preset '%s' not found\n", arg);
return 1;
}
while(!feof(f)){
int e= fscanf(f, "%999[^\n]\n", line) - 1;
if(line[0] == '#' && !e)
continue;
e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
if(e){
fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
ret = 1;
break;
}
if(!strcmp(tmp, "acodec")){
*audio_id = opt_audio_codec(tmp2);
}else if(!strcmp(tmp, "vcodec")){
*video_id = opt_video_codec(tmp2);
}else if(!strcmp(tmp, "scodec")){
/* opt_subtitle_codec(tmp2); */
}else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
ret = 1;
break;
}