📄 ffserver.c
字号:
audio_enc.bit_rate = atoi(arg) * 1000;
}
} else if (!strcasecmp(cmd, "AudioChannels")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
audio_enc.channels = atoi(arg);
}
} else if (!strcasecmp(cmd, "AudioSampleRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
audio_enc.rate = atoi(arg);
}
} else if (!strcasecmp(cmd, "VideoBitRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.bit_rate = atoi(arg) * 1000;
}
} else if (!strcasecmp(cmd, "VideoFrameRate")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.rate = atoi(arg);
}
} else if (!strcasecmp(cmd, "VideoGopSize")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
video_enc.gop_size = atoi(arg);
}
} else if (!strcasecmp(cmd, "VideoIntraOnly")) {
if (stream) {
video_enc.gop_size = 1;
}
} else if (!strcasecmp(cmd, "</Stream>")) {
if (!stream) {
fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
filename, line_num);
errors++;
}
if (stream->fmt) {
if (stream->fmt->audio_codec != CODEC_ID_NONE) {
stream->audio_enc = add_codec(stream->fmt->audio_codec,
&audio_enc);
}
if (stream->fmt->video_codec != CODEC_ID_NONE)
stream->video_enc = add_codec(stream->fmt->video_codec,
&video_enc);
}
stream = NULL;
} else {
fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
filename, line_num, cmd);
errors++;
}
}
fclose(f);
if (errors)
return -1;
else
return 0;
}
void *http_server_thread(void *arg)
{
http_server(my_addr);
return NULL;
}
static void write_packet(FFCodec *ffenc,
UINT8 *buf, int size)
{
PacketHeader hdr;
AVEncodeContext *enc = &ffenc->enc;
UINT8 *wptr;
mk_header(&hdr, enc, size);
wptr = http_fifo.wptr;
fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
fifo_write(&http_fifo, buf, size, &wptr);
/* atomic modification of wptr */
http_fifo.wptr = wptr;
ffenc->data_count += size;
ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
}
#define AUDIO_FIFO_SIZE 8192
int av_grab(void)
{
UINT8 audio_buf[AUDIO_FIFO_SIZE/2];
UINT8 audio_buf1[AUDIO_FIFO_SIZE/2];
UINT8 audio_out[AUDIO_FIFO_SIZE/2];
UINT8 video_buffer[128*1024];
char buf[256];
short *samples;
int ret;
int audio_fd;
FFCodec *ffenc;
AVEncodeContext *enc;
int frame_size, frame_bytes;
int use_audio, use_video;
int frame_rate, sample_rate, channels;
int width, height, frame_number;
UINT8 *picture[3];
use_audio = 0;
use_video = 0;
frame_rate = 0;
sample_rate = 0;
frame_size = 0;
channels = 1;
width = 0;
height = 0;
frame_number = 0;
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
avencoder_string(buf, sizeof(buf), enc);
fprintf(stderr, " %s\n", buf);
if (avencoder_open(enc, enc->codec) < 0) {
fprintf(stderr, "Incorrect encode parameters\n");
return -1;
}
switch(enc->codec->type) {
case CODEC_TYPE_AUDIO:
use_audio = 1;
if (enc->rate > sample_rate)
sample_rate = enc->rate;
if (enc->frame_size > frame_size)
frame_size = enc->frame_size;
if (enc->channels > channels)
channels = enc->channels;
fifo_init(&ffenc->fifo, AUDIO_FIFO_SIZE);
break;
case CODEC_TYPE_VIDEO:
use_video = 1;
if (enc->rate > frame_rate)
frame_rate = enc->rate;
if (enc->width > width)
width = enc->width;
if (enc->height > height)
height = enc->height;
break;
}
ffenc = ffenc->next;
}
/* audio */
samples = NULL;
audio_fd = -1;
if (use_audio) {
printf("Audio sampling: %d Hz, %s\n",
sample_rate, channels == 2 ? "stereo" : "mono");
audio_fd = audio_open(sample_rate, channels);
if (audio_fd < 0) {
fprintf(stderr, "Could not open audio device\n");
exit(1);
}
}
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
if (enc->codec->type == CODEC_TYPE_AUDIO &&
(enc->channels != channels ||
enc->rate != sample_rate)) {
audio_resample_init(&ffenc->resample, enc->channels, channels,
enc->rate, sample_rate);
}
ffenc = ffenc->next;
}
/* video */
if (use_video) {
printf("Video sampling: %dx%d, %d fps\n",
width, height, frame_rate);
ret = v4l_init(frame_rate, width, height);
if (ret < 0) {
fprintf(stderr,"Could not init video 4 linux capture\n");
exit(1);
}
}
for(;;) {
/* read & compress audio frames */
if (use_audio) {
int ret, nb_samples, nb_samples_out;
UINT8 *buftmp;
for(;;) {
ret = read(audio_fd, audio_buf, AUDIO_FIFO_SIZE/2);
if (ret <= 0)
break;
/* fill each codec fifo by doing the right sample
rate conversion. This is not optimal because we
do too much work, but it is easy to do */
nb_samples = ret / (channels * 2);
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
if (enc->codec->type == CODEC_TYPE_AUDIO) {
/* rate & stereo convertion */
if (enc->channels == channels &&
enc->rate == sample_rate) {
buftmp = audio_buf;
nb_samples_out = nb_samples;
} else {
buftmp = audio_buf1;
nb_samples_out = audio_resample(&ffenc->resample,
(short *)buftmp, (short *)audio_buf,
nb_samples);
}
fifo_write(&ffenc->fifo, buftmp, nb_samples_out * enc->channels * 2,
&ffenc->fifo.wptr);
}
ffenc = ffenc->next;
}
/* compress as many frame as possible with each audio codec */
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
if (enc->codec->type == CODEC_TYPE_AUDIO) {
frame_bytes = enc->frame_size * 2 * enc->channels;
while (fifo_read(&ffenc->fifo, audio_buf, frame_bytes, &ffenc->fifo.rptr) == 0) {
ret = avencoder_encode(enc,
audio_out, sizeof(audio_out), audio_buf);
write_packet(ffenc, audio_out, ret);
}
}
ffenc = ffenc->next;
}
}
}
if (use_video) {
ret = v4l_read_picture (picture, width, height,
frame_number);
if (ret < 0)
break;
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
if (enc->codec->type == CODEC_TYPE_VIDEO) {
int n1, n2;
/* feed each codec with its requested frame rate */
n1 = (frame_number * enc->rate) / frame_rate;
n2 = ((frame_number + 1) * enc->rate) / frame_rate;
if (n2 > n1) {
ret = avencoder_encode(enc, video_buffer, sizeof(video_buffer), picture);
write_packet(ffenc, video_buffer, ret);
}
}
ffenc = ffenc->next;
}
frame_number++;
}
}
ffenc = first_codec;
while (ffenc != NULL) {
enc = &ffenc->enc;
avencoder_close(enc);
ffenc = ffenc->next;
}
close(audio_fd);
return 0;
}
void help(void)
{
printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000,2001 Gerard Lantau\n"
"usage: ffserver [-L] [-h] [-f configfile]\n"
"Hyper fast multi format Audio/Video streaming server\n"
"\n"
"-L : print the LICENCE\n"
"-h : this help\n"
"-f configfile : use configfile instead of /etc/ffserver.conf\n"
);
}
void licence(void)
{
printf(
"ffserver version " FFMPEG_VERSION "\n"
"Copyright (c) 2000,2001 Gerard Lantau\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
);
}
int main(int argc, char **argv)
{
pthread_t http_server_tid;
const char *config_filename;
int c;
/* codecs */
register_avencoder(&ac3_encoder);
register_avencoder(&mp2_encoder);
register_avencoder(&mpeg1video_encoder);
register_avencoder(&h263_encoder);
register_avencoder(&rv10_encoder);
register_avencoder(&mjpeg_encoder);
register_avencoder(&divx_encoder);
/* audio video formats */
register_avformat(&mp2_format);
register_avformat(&ac3_format);
register_avformat(&mpeg_mux_format);
register_avformat(&mpeg1video_format);
register_avformat(&h263_format);
register_avformat(&rm_format);
register_avformat(&ra_format);
register_avformat(&asf_format);
register_avformat(&avi_format);
register_avformat(&mpjpeg_format);
register_avformat(&jpeg_format);
register_avformat(&swf_format);
config_filename = "/etc/ffserver.conf";
for(;;) {
c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
if (c == -1)
break;
switch(c) {
case 'L':
licence();
exit(1);
case '?':
case 'h':
help();
exit(1);
case 'f':
config_filename = optarg;
break;
default:
exit(2);
}
}
/* address on which the server will handle connections */
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons (8080);
my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
nb_max_connections = 5;
first_stream = NULL;
logfilename[0] = '\0';
if (parse_ffconfig(config_filename) < 0) {
fprintf(stderr, "Incorrect config file - exiting.\n");
exit(1);
}
/* signal init */
signal(SIGPIPE, SIG_IGN);
/* open log file if needed */
if (logfilename[0] != '\0') {
if (!strcmp(logfilename, "-"))
logfile = stdout;
else
logfile = fopen(logfilename, "w");
}
/* init fifo */
http_fifo_write_count = 0;
if (fifo_init(&http_fifo, FIFO_MAX_SIZE) < 0) {
fprintf(stderr, "Could not allow receive fifo\n");
exit(1);
}
if (master_url[0] == '\0') {
/* no master server: we grab ourself */
/* launch server thread */
if (pthread_create(&http_server_tid, NULL,
http_server_thread, NULL) != 0) {
fprintf(stderr, "Could not create http server thread\n");
exit(1);
}
/* launch the audio / video grab */
if (av_grab() < 0) {
fprintf(stderr, "Could not start audio/video grab\n");
exit(1);
}
} else {
/* master server : no thread are needed */
if (http_server(my_addr) < 0) {
fprintf(stderr, "Could start http server\n");
exit(1);
}
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -