📄 ffserver.c
字号:
h->data[3] = c->key_frame;
h->payload_size = htons(payload_size);
}
int test_header(PacketHeader *h, AVEncodeContext *c)
{
if (!c)
return 0;
if (h->codec_type == c->codec->type &&
h->codec_id == c->codec->id &&
h->bit_rate == htons(c->bit_rate / 1000)) {
switch(c->codec->type) {
case CODEC_TYPE_VIDEO:
if (h->data[0] == c->rate &&
h->data[1] == (c->width / 16) &&
h->data[2] == (c->height / 16))
goto found;
break;
case CODEC_TYPE_AUDIO:
if (h->data[0] == (c->rate / 1000) &&
(h->data[1] == c->channels))
goto found;
break;
}
}
return 0;
found:
c->frame_number++;
c->key_frame = h->data[3];
return 1;
}
static int http_prepare_data(HTTPContext *c)
{
PacketHeader hdr;
UINT8 *start_rptr, *payload;
int payload_size, ret;
long long fifo_total_size;
switch(c->state) {
case HTTPSTATE_SEND_DATA_HEADER:
if (c->stream->stream_type != STREAM_TYPE_MASTER) {
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
c->fmt_ctx.format = c->stream->fmt;
if (c->fmt_ctx.format->audio_codec != CODEC_ID_NONE) {
/* create a fake new codec instance */
c->fmt_ctx.audio_enc = malloc(sizeof(AVEncodeContext));
memcpy(c->fmt_ctx.audio_enc, c->stream->audio_enc,
sizeof(AVEncodeContext));
c->fmt_ctx.audio_enc->frame_number = 0;
}
if (c->fmt_ctx.format->video_codec != CODEC_ID_NONE) {
c->fmt_ctx.video_enc = malloc(sizeof(AVEncodeContext));
memcpy(c->fmt_ctx.video_enc, c->stream->video_enc,
sizeof(AVEncodeContext));
c->fmt_ctx.video_enc->frame_number = 0;
}
init_put_byte(&c->fmt_ctx.pb, c->buffer, IOBUFFER_MAX_SIZE,
c, http_write_packet, NULL);
c->fmt_ctx.is_streamed = 1;
c->got_key_frame[0] = 0;
c->got_key_frame[1] = 0;
/* prepare header */
c->fmt_ctx.format->write_header(&c->fmt_ctx);
}
c->state = HTTPSTATE_SEND_DATA;
c->last_packet_sent = 0;
c->rptr = http_fifo.wptr;
c->last_http_fifo_write_count = http_fifo_write_count;
break;
case HTTPSTATE_SEND_DATA:
/* find a new packet */
fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
/* overflow : resync. We suppose that wptr is at this
point a pointer to a valid packet */
c->rptr = http_fifo.wptr;
c->got_key_frame[0] = 0;
c->got_key_frame[1] = 0;
}
start_rptr = c->rptr;
if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
return 0;
payload_size = ntohs(hdr.payload_size);
payload = malloc(payload_size);
if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
/* cannot read all the payload */
free(payload);
c->rptr = start_rptr;
return 0;
}
c->last_http_fifo_write_count = http_fifo_write_count -
fifo_size(&http_fifo, c->rptr);
if (c->stream->stream_type != STREAM_TYPE_MASTER) {
/* test if the packet can be handled by this format */
ret = 0;
if (test_header(&hdr, c->fmt_ctx.audio_enc)) {
/* only begin sending when got a key frame */
if (c->fmt_ctx.audio_enc->key_frame)
c->got_key_frame[1] = 1;
if (c->got_key_frame[1]) {
ret = c->fmt_ctx.format->write_audio_frame(&c->fmt_ctx,
payload, payload_size);
}
} else if (test_header(&hdr, c->fmt_ctx.video_enc)) {
if (c->fmt_ctx.video_enc->key_frame)
c->got_key_frame[0] = 1;
if (c->got_key_frame[0]) {
ret = c->fmt_ctx.format->write_video_picture(&c->fmt_ctx,
payload, payload_size);
}
}
if (ret) {
/* must send trailer now */
c->state = HTTPSTATE_SEND_DATA_TRAILER;
}
} else {
/* master case : send everything */
char *q;
q = c->buffer;
memcpy(q, &hdr, sizeof(hdr));
q += sizeof(hdr);
memcpy(q, payload, payload_size);
q += payload_size;
c->buffer_ptr = c->buffer;
c->buffer_end = q;
}
free(payload);
break;
default:
case HTTPSTATE_SEND_DATA_TRAILER:
/* last packet test ? */
if (c->last_packet_sent)
return -1;
/* prepare header */
c->fmt_ctx.format->write_trailer(&c->fmt_ctx);
c->last_packet_sent = 1;
break;
}
return 0;
}
/* should convert the format at the same time */
static int http_send_data(HTTPContext *c)
{
int len;
while (c->buffer_ptr >= c->buffer_end) {
if (http_prepare_data(c) < 0)
return -1;
}
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 {
c->buffer_ptr += len;
c->data_count += len;
}
return 0;
}
static int master_receive(int fd)
{
int len, size;
FifoBuffer *f = &http_fifo;
UINT8 *rptr;
size = f->end - f->wptr;
if (size > master_count)
size = master_count;
len = read(fd, f->wptr, size);
if (len == -1) {
if (errno != EAGAIN && errno != EINTR)
return -1;
} else if (len == 0) {
return -1;
} else {
master_wptr += len;
if (master_wptr >= f->end)
master_wptr = f->buffer;
master_count -= len;
if (master_count == 0) {
if (master_state == MASTERSTATE_RECEIVE_HEADER) {
/* XXX: use generic fifo read to extract packet header */
rptr = master_wptr;
if (rptr == f->buffer)
rptr = f->end - 1;
else
rptr--;
master_count = *rptr;
if (rptr == f->buffer)
rptr = f->end - 1;
else
rptr--;
master_count |= *rptr << 8;
master_state = MASTERSTATE_RECEIVE_DATA;
} else {
/* update fifo wptr */
f->wptr = master_wptr;
master_state = MASTERSTATE_RECEIVE_HEADER;
}
}
}
return 0;
}
static void get_arg(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
p = *pp;
while (isspace(*p)) p++;
q = buf;
while (!isspace(*p) && *p != '\0') {
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
*q = '\0';
*pp = p;
}
/* add a codec and check if it does not already exists */
AVEncodeContext *add_codec(int codec_id,
AVEncodeContext *av)
{
AVEncoder *codec;
FFCodec *ctx, **pctx;
AVEncodeContext *av1;
codec = avencoder_find(codec_id);
if (!codec)
return NULL;
/* compute default parameters */
av->codec = codec;
switch(codec->type) {
case CODEC_TYPE_AUDIO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->rate == 0)
av->rate = 22050;
if (av->channels == 0)
av->channels = 1;
break;
case CODEC_TYPE_VIDEO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->rate == 0)
av->rate = 5;
if (av->width == 0 || av->height == 0) {
av->width = 160;
av->height = 128;
}
break;
}
/* find if the codec already exists */
pctx = &first_codec;
while (*pctx != NULL) {
av1 = &(*pctx)->enc;
if (av1->codec == av->codec &&
av1->bit_rate == av->bit_rate &&
av1->rate == av->rate) {
switch(av->codec->type) {
case CODEC_TYPE_AUDIO:
if (av1->channels == av->channels)
goto found;
break;
case CODEC_TYPE_VIDEO:
if (av1->width == av->width &&
av1->height == av->height &&
av1->gop_size == av->gop_size)
goto found;
break;
}
}
pctx = &(*pctx)->next;
}
ctx = malloc(sizeof(FFCodec));
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(FFCodec));
*pctx = ctx;
memcpy(&ctx->enc, av, sizeof(AVEncodeContext));
return &ctx->enc;
found:
ctx = *pctx;
return &ctx->enc;
}
int parse_ffconfig(const char *filename)
{
FILE *f;
char line[1024];
char cmd[64];
char arg[1024];
const char *p;
int val, errors, line_num;
FFStream **last_stream, *stream;
AVEncodeContext audio_enc, video_enc;
f = fopen(filename, "r");
if (!f) {
perror(filename);
return -1;
}
errors = 0;
line_num = 0;
first_stream = NULL;
first_codec = NULL;
last_stream = &first_stream;
stream = NULL;
for(;;) {
if (fgets(line, sizeof(line), f) == NULL)
break;
line_num++;
p = line;
while (isspace(*p))
p++;
if (*p == '\0' || *p == '#')
continue;
get_arg(cmd, sizeof(cmd), &p);
if (!strcasecmp(cmd, "Port")) {
get_arg(arg, sizeof(arg), &p);
my_addr.sin_port = htons (atoi(arg));
} else if (!strcasecmp(cmd, "BindAddress")) {
get_arg(arg, sizeof(arg), &p);
if (!inet_aton(arg, &my_addr.sin_addr)) {
fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
filename, line_num, arg);
errors++;
}
} else if (!strcasecmp(cmd, "MasterServer")) {
get_arg(master_url, sizeof(master_url), &p);
if (!strstart(master_url, "http://", NULL)) {
fprintf(stderr, "%s:%d: Invalid URL for master server: %s\n",
filename, line_num, master_url);
errors++;
}
} else if (!strcasecmp(cmd, "AudioDevice")) {
get_arg(arg, sizeof(arg), &p);
audio_device = strdup(arg);
} else if (!strcasecmp(cmd, "VideoDevice")) {
get_arg(arg, sizeof(arg), &p);
v4l_device = strdup(arg);
} else if (!strcasecmp(cmd, "MaxClients")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
filename, line_num, arg);
errors++;
} else {
nb_max_connections = val;
}
} else if (!strcasecmp(cmd, "CustomLog")) {
get_arg(logfilename, sizeof(logfilename), &p);
} else if (!strcasecmp(cmd, "<Stream")) {
char *q;
if (stream) {
fprintf(stderr, "%s:%d: Already in a stream tag\n",
filename, line_num);
} else {
stream = malloc(sizeof(FFStream));
memset(stream, 0, sizeof(FFStream));
*last_stream = stream;
last_stream = &stream->next;
get_arg(stream->filename, sizeof(stream->filename), &p);
q = strrchr(stream->filename, '>');
if (*q)
*q = '\0';
stream->fmt = guess_format(NULL, stream->filename, NULL);
memset(&audio_enc, 0, sizeof(AVEncodeContext));
memset(&video_enc, 0, sizeof(AVEncodeContext));
}
} else if (!strcasecmp(cmd, "Format")) {
get_arg(arg, sizeof(arg), &p);
if (!strcmp(arg, "master")) {
stream->stream_type = STREAM_TYPE_MASTER;
stream->fmt = NULL;
} else if (!strcmp(arg, "status")) {
stream->stream_type = STREAM_TYPE_STATUS;
stream->fmt = NULL;
} else {
stream->stream_type = STREAM_TYPE_LIVE;
stream->fmt = guess_format(arg, NULL, NULL);
if (!stream->fmt) {
fprintf(stderr, "%s:%d: Unknown Format: %s\n",
filename, line_num, arg);
errors++;
}
}
} else if (!strcasecmp(cmd, "AudioBitRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -