unified grab & convertion loops (should be easier to achieve correct synchronization now - but still work to do!) - added ffplay support

Originally committed as revision 144 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Fabrice Bellard 2001-09-24 23:22:25 +00:00
parent 2744a37fac
commit a38469e1da

609
ffmpeg.c
View File

@ -97,6 +97,7 @@ static char *str_copyright = NULL;
static char *str_comment = NULL;
static int do_benchmark = 0;
static int do_hex_dump = 0;
static int do_play = 0;
typedef struct AVOutputStream {
int file_index; /* file index */
@ -188,425 +189,30 @@ static int read_key(void)
return -1;
}
#define AUDIO_FIFO_SIZE 8192
#else
/* main loop for grabbing */
int av_grab(AVFormatContext *s)
/* no interactive support */
static void term_exit(void)
{
UINT8 audio_buf[AUDIO_FIFO_SIZE];
UINT8 audio_buf1[AUDIO_FIFO_SIZE];
UINT8 audio_out[AUDIO_FIFO_SIZE];
UINT8 video_buffer[1024*1024];
char buf[256];
short *samples;
URLContext *audio_handle = NULL, *video_handle = NULL;
int ret;
AVCodecContext *enc, *first_video_enc = NULL;
int frame_size, frame_bytes;
int use_audio, use_video;
int frame_rate, sample_rate, channels;
int width, height, frame_number, i, pix_fmt = 0;
AVOutputStream *ost_table[s->nb_streams], *ost;
UINT8 *picture_in_buf = NULL, *picture_420p = NULL;
int audio_fifo_size = 0, picture_size = 0;
INT64 time_start;
/* init output stream info */
for(i=0;i<s->nb_streams;i++)
ost_table[i] = NULL;
/* output stream init */
for(i=0;i<s->nb_streams;i++) {
ost = av_mallocz(sizeof(AVOutputStream));
if (!ost)
goto fail;
ost->index = i;
ost->st = s->streams[i];
ost_table[i] = ost;
}
use_audio = 0;
use_video = 0;
frame_rate = 0;
sample_rate = 0;
frame_size = 0;
channels = 1;
width = 0;
height = 0;
frame_number = 0;
for(i=0;i<s->nb_streams;i++) {
AVCodec *codec;
ost = ost_table[i];
enc = &ost->st->codec;
codec = avcodec_find_encoder(enc->codec_id);
if (!codec) {
fprintf(stderr, "Unknown codec\n");
return -1;
}
if (avcodec_open(enc, codec) < 0) {
fprintf(stderr, "Incorrect encode parameters\n");
return -1;
}
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
use_audio = 1;
if (enc->sample_rate > sample_rate)
sample_rate = enc->sample_rate;
if (enc->frame_size > frame_size)
frame_size = enc->frame_size;
if (enc->channels > channels)
channels = enc->channels;
break;
case CODEC_TYPE_VIDEO:
if (!first_video_enc)
first_video_enc = enc;
use_video = 1;
if (enc->frame_rate > frame_rate)
frame_rate = enc->frame_rate;
if (enc->width > width)
width = enc->width;
if (enc->height > height)
height = enc->height;
break;
}
}
/* audio */
samples = NULL;
if (use_audio) {
snprintf(buf, sizeof(buf), "audio:%d,%d", sample_rate, channels);
ret = url_open(&audio_handle, buf, URL_RDONLY);
if (ret < 0) {
fprintf(stderr, "Could not open audio device: disabling audio capture\n");
use_audio = 0;
} else {
URLFormat f;
/* read back exact grab parameters */
if (url_getformat(audio_handle, &f) < 0) {
fprintf(stderr, "could not read back video grab parameters\n");
goto fail;
}
sample_rate = f.sample_rate;
channels = f.channels;
audio_fifo_size = ((AUDIO_FIFO_SIZE / 2) / audio_handle->packet_size) *
audio_handle->packet_size;
fprintf(stderr, "Audio sampling: %d Hz, %s\n",
sample_rate, channels == 2 ? "stereo" : "mono");
}
}
/* video */
if (use_video) {
snprintf(buf, sizeof(buf), "video:%d,%d,%f",
width, height, (float)frame_rate / FRAME_RATE_BASE);
ret = url_open(&video_handle, buf, URL_RDONLY);
if (ret < 0) {
fprintf(stderr,"Could not init video 4 linux capture: disabling video capture\n");
use_video = 0;
} else {
URLFormat f;
const char *pix_fmt_str;
/* read back exact grab parameters */
if (url_getformat(video_handle, &f) < 0) {
fprintf(stderr, "could not read back video grab parameters\n");
goto fail;
}
width = f.width;
height = f.height;
pix_fmt = f.pix_fmt;
switch(pix_fmt) {
case PIX_FMT_YUV420P:
pix_fmt_str = "420P";
break;
case PIX_FMT_YUV422:
pix_fmt_str = "422";
break;
case PIX_FMT_RGB24:
pix_fmt_str = "RGB24";
break;
case PIX_FMT_BGR24:
pix_fmt_str = "BGR24";
break;
default:
pix_fmt_str = "???";
break;
}
picture_size = video_handle->packet_size;
picture_in_buf = malloc(picture_size);
if (!picture_in_buf)
goto fail;
/* allocate a temporary picture if not grabbing in 420P format */
if (pix_fmt != PIX_FMT_YUV420P) {
picture_420p = malloc((width * height * 3) / 2);
}
fprintf(stderr, "Video sampling: %dx%d, %s format, %0.2f fps\n",
width, height, pix_fmt_str, (float)frame_rate / FRAME_RATE_BASE);
}
}
if (!use_video && !use_audio) {
fprintf(stderr,"Could not open grab devices : exiting\n");
exit(1);
}
/* init built in conversion functions */
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
enc = &ost->st->codec;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
ost->audio_resample = 0;
if ((enc->channels != channels ||
enc->sample_rate != sample_rate)) {
ost->audio_resample = 1;
ost->resample = audio_resample_init(enc->channels, channels,
enc->sample_rate, sample_rate);
}
if (fifo_init(&ost->fifo, (2 * audio_fifo_size * enc->sample_rate *
enc->channels) / sample_rate))
goto fail;
break;
case CODEC_TYPE_VIDEO:
ost->video_resample = 0;
if (enc->width != width ||
enc->height != height) {
UINT8 *buf;
ost->video_resample = 1;
buf = malloc((enc->width * enc->height * 3) / 2);
if (!buf)
goto fail;
ost->pict_tmp.data[0] = buf;
ost->pict_tmp.data[1] = buf + enc->width * height;
ost->pict_tmp.data[2] = ost->pict_tmp.data[1] + (enc->width * height) / 4;
ost->pict_tmp.linesize[0] = enc->width;
ost->pict_tmp.linesize[1] = enc->width / 2;
ost->pict_tmp.linesize[2] = enc->width / 2;
ost->img_resample_ctx = img_resample_init(
ost->st->codec.width, ost->st->codec.height,
width, height);
}
}
}
fprintf(stderr, "Press [q] to stop encoding\n");
s->format->write_header(s);
time_start = gettime();
term_init();
for(;;) {
/* if 'q' pressed, exits */
if (read_key() == 'q')
break;
/* read & compress audio frames */
if (use_audio) {
int ret, nb_samples, nb_samples_out;
UINT8 *buftmp;
for(;;) {
ret = url_read(audio_handle, audio_buf, audio_fifo_size);
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);
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
enc = &ost->st->codec;
if (enc->codec_type == CODEC_TYPE_AUDIO) {
/* rate & stereo convertion */
if (!ost->audio_resample) {
buftmp = audio_buf;
nb_samples_out = nb_samples;
} else {
buftmp = audio_buf1;
nb_samples_out = audio_resample(ost->resample,
(short *)buftmp, (short *)audio_buf,
nb_samples);
}
fifo_write(&ost->fifo, buftmp, nb_samples_out * enc->channels * 2,
&ost->fifo.wptr);
}
}
/* compress as many frame as possible with each audio codec */
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
enc = &ost->st->codec;
if (enc->codec_type == CODEC_TYPE_AUDIO) {
frame_bytes = enc->frame_size * 2 * enc->channels;
while (fifo_read(&ost->fifo, audio_buf,
frame_bytes, &ost->fifo.rptr) == 0) {
ret = avcodec_encode_audio(enc,
audio_out, sizeof(audio_out),
(short *)audio_buf);
s->format->write_packet(s, ost->index, audio_out, ret);
}
}
}
}
}
if (use_video) {
AVPicture *picture1, *picture2, *picture;
AVPicture picture_tmp0, picture_tmp1;
ret = url_read(video_handle, picture_in_buf, picture_size);
if (ret < 0)
break;
picture2 = &picture_tmp0;
avpicture_fill(picture2, picture_in_buf, pix_fmt, width, height);
if (pix_fmt != PIX_FMT_YUV420P) {
picture = &picture_tmp1;
avpicture_fill(picture, picture_420p,
PIX_FMT_YUV420P, width, height);
img_convert(picture, PIX_FMT_YUV420P,
picture2, pix_fmt,
width, height);
} else {
picture = picture2;
}
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
enc = &ost->st->codec;
if (enc->codec_type == CODEC_TYPE_VIDEO) {
int n1, n2, nb;
/* feed each codec with its requested frame rate */
n1 = ((INT64)frame_number * enc->frame_rate) / frame_rate;
n2 = (((INT64)frame_number + 1) * enc->frame_rate) / frame_rate;
nb = n2 - n1;
if (nb > 0) {
/* resize the picture if needed */
if (ost->video_resample) {
picture1 = &ost->pict_tmp;
img_resample(ost->img_resample_ctx,
picture1, picture);
} else {
picture1 = picture;
}
ret = avcodec_encode_video(enc, video_buffer,
sizeof(video_buffer),
picture1);
s->format->write_packet(s, ost->index, video_buffer, ret);
}
}
}
frame_number++;
}
/* write report */
{
char buf[1024];
INT64 total_size;
float ti, bitrate;
static float last_ti;
INT64 ti1;
total_size = url_ftell(&s->pb);
ti1 = gettime() - time_start;
/* check elapsed time */
if (recording_time && ti1 >= recording_time)
break;
ti = ti1 / 1000000.0;
if (ti < 0.1)
ti = 0.1;
/* dispaly twice per second */
if ((ti - last_ti) >= 0.5) {
last_ti = ti;
bitrate = (int)((total_size * 8) / ti / 1000.0);
buf[0] = '\0';
if (use_video) {
sprintf(buf + strlen(buf), "frame=%5d fps=%4.1f q=%2d ",
frame_number, (float)frame_number / ti, first_video_enc->quality);
}
sprintf(buf + strlen(buf), "size=%8LdkB time=%0.1f bitrate=%6.1fkbits/s",
total_size / 1024, ti, bitrate);
fprintf(stderr, "%s \r", buf);
fflush(stderr);
}
}
}
term_exit();
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
enc = &ost->st->codec;
avcodec_close(enc);
}
s->format->write_trailer(s);
if (audio_handle)
url_close(audio_handle);
if (video_handle)
url_close(video_handle);
/* write report */
{
float ti, bitrate;
INT64 total_size;
total_size = url_ftell(&s->pb);
ti = (gettime() - time_start) / 1000000.0;
if (ti < 0.1)
ti = 0.1;
bitrate = (int)((total_size * 8) / ti / 1000.0);
fprintf(stderr, "\033[K\nTotal time = %0.1f s, %Ld KBytes, %0.1f kbits/s\n",
ti, total_size / 1024, bitrate);
if (use_video) {
fprintf(stderr, "Total frames = %d\n", frame_number);
}
}
ret = 0;
fail1:
if (picture_in_buf)
free(picture_in_buf);
if (picture_420p)
free(picture_420p);
for(i=0;i<s->nb_streams;i++) {
ost = ost_table[i];
if (ost) {
if (ost->fifo.buffer)
fifo_free(&ost->fifo);
if (ost->pict_tmp.data[0])
free(ost->pict_tmp.data[0]);
if (ost->video_resample)
img_resample_close(ost->img_resample_ctx);
if (ost->audio_resample)
audio_resample_close(ost->resample);
free(ost);
}
}
return ret;
fail:
ret = -ENOMEM;
goto fail1;
}
#endif /* CONFIG_WIN32 */
static void term_init(void)
{
}
static int read_key(void)
{
return -1;
}
#endif
int read_ffserver_streams(AVFormatContext *s, const char *filename)
{
int i;
AVFormatContext *ic;
ic = av_open_input_file(filename, FFM_PACKET_SIZE);
ic = av_open_input_file(filename, NULL, FFM_PACKET_SIZE, NULL);
if (!ic)
return -EIO;
/* copy stream format */
@ -1156,9 +762,22 @@ static int av_encode(AVFormatContext **output_files,
/* open files and write file headers */
for(i=0;i<nb_output_files;i++) {
os = output_files[i];
os->format->write_header(os);
if (os->format->write_header(os) < 0) {
fprintf(stderr, "Could not write header for output file #%d (incorrect codec paramters ?)\n", i);
ret = -EINVAL;
goto fail;
}
}
#ifndef CONFIG_WIN32
if (!do_play) {
fprintf(stderr, "Press [q] to stop encoding\n");
} else {
fprintf(stderr, "Press [q] to stop playing\n");
}
#endif
term_init();
start_time = gettime();
min_pts = 0;
for(;;) {
@ -1171,8 +790,12 @@ static int av_encode(AVFormatContext **output_files,
AVPicture picture;
short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2];
/* select the input file with the smallest pts */
redo:
/* if 'q' pressed, exits */
if (read_key() == 'q')
break;
/* select the input file with the smallest pts */
file_index = -1;
min_pts = MAXINT64;
for(i=0;i<nb_istreams;i++) {
@ -1189,7 +812,6 @@ static int av_encode(AVFormatContext **output_files,
if (recording_time > 0 && min_pts >= recording_time)
break;
/* read a packet from it and output it in the fifo */
is = input_files[file_index];
if (av_read_packet(is, &pkt) < 0) {
file_table[file_index].eof_reached = 1;
@ -1358,6 +980,7 @@ static int av_encode(AVFormatContext **output_files,
}
}
}
term_exit();
/* dump report by using the first video and audio streams */
{
@ -2090,6 +1713,95 @@ void opt_output_file(const char *filename)
video_codec_id = CODEC_ID_NONE;
}
#ifdef CONFIG_GRAB
/* prepare dummy protocols for grab */
void prepare_grab(void)
{
int has_video, has_audio, i, j;
AVFormatContext *oc;
AVFormatContext *ic;
AVFormatParameters ap1, *ap = &ap1;
/* see if audio/video inputs are needed */
has_video = 0;
has_audio = 0;
memset(ap, 0, sizeof(*ap));
for(j=0;j<nb_output_files;j++) {
oc = output_files[j];
for(i=0;i<oc->nb_streams;i++) {
AVCodecContext *enc = &oc->streams[i]->codec;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
if (enc->sample_rate > ap->sample_rate)
ap->sample_rate = enc->sample_rate;
if (enc->channels > ap->channels)
ap->channels = enc->channels;
has_audio = 1;
break;
case CODEC_TYPE_VIDEO:
if (enc->width > ap->width)
ap->width = enc->width;
if (enc->height > ap->height)
ap->height = enc->height;
if (enc->frame_rate > ap->frame_rate)
ap->frame_rate = enc->frame_rate;
has_video = 1;
break;
}
}
}
if (has_video == 0 && has_audio == 0) {
fprintf(stderr, "Output file must have at least one audio or video stream\n");
exit(1);
}
if (has_video) {
ic = av_open_input_file("", "video_grab_device", 0, ap);
if (!ic) {
fprintf(stderr, "Could not open video grab device\n");
exit(1);
}
input_files[nb_input_files] = ic;
dump_format(ic, nb_input_files, v4l_device, 0);
nb_input_files++;
}
if (has_audio) {
ic = av_open_input_file("", "audio_device", 0, ap);
if (!ic) {
fprintf(stderr, "Could not open audio grab device\n");
exit(1);
}
input_files[nb_input_files] = ic;
dump_format(ic, nb_input_files, audio_device, 0);
nb_input_files++;
}
}
#else
void prepare_grab(void)
{
fprintf(stderr, "Must supply at least one input file\n");
exit(1);
}
#endif
/* open the necessary output devices for playing */
void prepare_play(void)
{
file_format = guess_format("audio_device", NULL, NULL);
if (!file_format) {
fprintf(stderr, "Could not find audio device\n");
exit(1);
}
opt_output_file(audio_device);
}
#ifndef CONFIG_WIN32
INT64 getutime(void)
{
@ -2165,13 +1877,24 @@ void show_formats(void)
void show_help(void)
{
const char *prog;
const OptionDef *po;
int i, expert;
prog = do_play ? "ffplay" : "ffmpeg";
printf("ffmpeg version " FFMPEG_VERSION ", Copyright (c) 2000,2001 Gerard Lantau\n"
"usage: ffmpeg [[options] -i input_file]... {[options] outfile}...\n"
"Hyper fast MPEG1/MPEG4/H263/RV and AC3/MPEG audio encoder\n"
"\n"
printf("%s version " FFMPEG_VERSION ", Copyright (c) 2000, 2001 Gerard Lantau\n",
prog);
if (!do_play) {
printf("usage: ffmpeg [[options] -i input_file]... {[options] outfile}...\n"
"Hyper fast MPEG1/MPEG4/H263/RV and AC3/MPEG audio encoder\n");
} else {
printf("usage: ffplay [options] input_file...\n"
"Simple audio player\n");
}
printf("\n"
"Main options are:\n");
for(i=0;i<2;i++) {
if (i == 1)
@ -2246,12 +1969,19 @@ int main(int argc, char **argv)
int optindex, i;
const char *opt, *arg;
const OptionDef *po;
INT64 ti;
register_all();
/* detect if invoked as player */
i = strlen(argv[0]);
if (i >= 6 && !strcmp(argv[0] + i - 6, "ffplay"))
do_play = 1;
if (argc <= 1)
show_help();
/* parse options */
optindex = 1;
while (optindex < argc) {
opt = argv[optindex++];
@ -2280,35 +2010,40 @@ int main(int argc, char **argv)
po->u.func_arg(arg);
}
} else {
opt_output_file(opt);
if (!do_play) {
opt_output_file(opt);
} else {
opt_input_file(opt);
}
}
}
if (nb_input_files == 0) {
#ifdef CONFIG_GRAB
if (nb_output_files != 1) {
fprintf(stderr, "Only one output file supported when grabbing\n");
exit(1);
}
av_grab(output_files[0]);
#else
fprintf(stderr, "Must supply at least one input file\n");
#endif
} else {
INT64 ti;
if (!do_play) {
/* file converter / grab */
if (nb_output_files <= 0) {
fprintf(stderr, "Must supply at least one output file\n");
exit(1);
}
ti = getutime();
av_encode(output_files, nb_output_files, input_files, nb_input_files,
stream_maps, nb_stream_maps);
ti = getutime() - ti;
if (do_benchmark) {
printf("bench: utime=%0.3fs\n", ti / 1000000.0);
if (nb_input_files == 0) {
prepare_grab();
}
} else {
/* player */
if (nb_input_files <= 0) {
fprintf(stderr, "Must supply at least one input file\n");
exit(1);
}
prepare_play();
}
ti = getutime();
av_encode(output_files, nb_output_files, input_files, nb_input_files,
stream_maps, nb_stream_maps);
ti = getutime() - ti;
if (do_benchmark) {
printf("bench: utime=%0.3fs\n", ti / 1000000.0);
}
/* close files */