]> git.sesse.net Git - ffmpeg/blobdiff - ffmpeg.c
fixing 4MV
[ffmpeg] / ffmpeg.c
index afeef4158f2ed6efee38880935367db30b5b57f2..23145ce9304fb19ab8678e9f5dff7174839aafa7 100644 (file)
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -75,7 +75,13 @@ static int frame_width  = 160;
 static int frame_height = 128;
 static int frame_rate = 25 * FRAME_RATE_BASE;
 static int video_bit_rate = 200000;
+static int video_bit_rate_tolerance = 200000;
 static int video_qscale = 0;
+static int video_qmin = 3;
+static int video_qmax = 15;
+static int video_qdiff = 3;
+static float video_qblur = 0.5;
+static float video_qcomp = 0.5;
 static int video_disable = 0;
 static int video_codec_id = CODEC_ID_NONE;
 static int same_quality = 0;
@@ -96,6 +102,9 @@ static char *str_author = NULL;
 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;
+static int do_psnr = 0;
 
 typedef struct AVOutputStream {
     int file_index;          /* file index */
@@ -187,425 +196,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/2];
-    UINT8 audio_buf1[AUDIO_FIFO_SIZE/2];
-    UINT8 audio_out[AUDIO_FIFO_SIZE/2];
-    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) / 
-                          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);
-        }
-    }
+static void term_init(void)
+{
+}
 
-    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;
+static int read_key(void)
+{
+    return -1;
 }
 
-#endif /* CONFIG_WIN32 */
+#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 */
@@ -648,7 +262,7 @@ static void do_audio_out(AVFormatContext *s,
     }
 
     /* now encode as many frames as possible */
-    if (enc->codec_id != CODEC_ID_PCM) {
+    if (enc->frame_size > 1) {
         /* output resampled raw samples */
         fifo_write(&ost->fifo, buftmp, size_out, 
                    &ost->fifo.wptr);
@@ -657,13 +271,26 @@ static void do_audio_out(AVFormatContext *s,
         
         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);
+            ret = avcodec_encode_audio(enc, audio_out, sizeof(audio_out), 
+                                       (short *)audio_buf);
             s->format->write_packet(s, ost->index, audio_out, ret);
         }
     } else {
-        /* XXX: handle endianness */
-        s->format->write_packet(s, ost->index, buftmp, size_out);
+        /* output a pcm frame */
+        /* XXX: change encoding codec API to avoid this ? */
+        switch(enc->codec->id) {
+        case CODEC_ID_PCM_S16LE:
+        case CODEC_ID_PCM_S16BE:
+        case CODEC_ID_PCM_U16LE:
+        case CODEC_ID_PCM_U16BE:
+            break;
+        default:
+            size_out = size_out >> 1;
+            break;
+        }
+        ret = avcodec_encode_audio(enc, audio_out, size_out, 
+                                   (short *)buftmp);
+        s->format->write_packet(s, ost->index, audio_out, ret);
     }
 }
 
@@ -860,9 +487,6 @@ static void do_video_out(AVFormatContext *s,
         free(buf1);
 }
 
-//#define HEX_DUMP
-
-#ifdef HEX_DUMP
 static void hex_dump(UINT8 *buf, int size)
 {
     int len, i, j, c;
@@ -888,7 +512,6 @@ static void hex_dump(UINT8 *buf, int size)
         printf("\n");
     }
 }
-#endif
 
 /*
  * The following code is the main loop of the file converter
@@ -1146,8 +769,21 @@ 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;
@@ -1161,8 +797,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++) {
@@ -1179,7 +819,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;
@@ -1191,10 +830,10 @@ static int av_encode(AVFormatContext **output_files,
             continue;
         }
 
-#ifdef HEX_DUMP
-        printf("stream #%d, size=%d:\n", pkt.stream_index, pkt.size);
-        hex_dump(pkt.data, pkt.size);
-#endif
+        if (do_hex_dump) {
+            printf("stream #%d, size=%d:\n", pkt.stream_index, pkt.size);
+            hex_dump(pkt.data, pkt.size);
+        }
 
         //        printf("read #%d.%d size=%d\n", ist->file_index, ist->index, pkt.size);
 
@@ -1208,24 +847,19 @@ static int av_encode(AVFormatContext **output_files,
             if (ist->decoding_needed) {
                 switch(ist->st->codec.codec_type) {
                 case CODEC_TYPE_AUDIO:
-                    if (ist->st->codec.codec_id == CODEC_ID_PCM) {
-                        /* no need to call a codec */
-                        data_buf = ptr;
-                        data_size = len;
-                        ret = len;
-                    } else {
-                        ret = avcodec_decode_audio(&ist->st->codec, samples, &data_size,
-                                                   ptr, len);
-                        if (ret < 0)
-                            goto fail_decode;
-                        if (data_size == 0) {
-                            /* no audio frame */
-                            ptr += ret;
-                            len -= ret;
-                            continue;
-                        }
-                        data_buf = (UINT8 *)samples;
+                    /* XXX: could avoid copy if PCM 16 bits with same
+                       endianness as CPU */
+                    ret = avcodec_decode_audio(&ist->st->codec, samples, &data_size,
+                                               ptr, len);
+                    if (ret < 0)
+                        goto fail_decode;
+                    if (data_size == 0) {
+                        /* no audio frame */
+                        ptr += ret;
+                        len -= ret;
+                        continue;
                     }
+                    data_buf = (UINT8 *)samples;
                     break;
                 case CODEC_TYPE_VIDEO:
                     if (ist->st->codec.codec_id == CODEC_ID_RAWVIDEO) {
@@ -1331,6 +965,8 @@ static int av_encode(AVFormatContext **output_files,
                         frame_number = ist->frame_number;
                         sprintf(buf + strlen(buf), "frame=%5d q=%2d ",
                                 frame_number, enc->quality);
+                        if (do_psnr)
+                            sprintf(buf + strlen(buf), "PSNR=%6.2f ", enc->psnr_y);
                         vid = 1;
                     }
                     /* compute min pts value */
@@ -1353,6 +989,7 @@ static int av_encode(AVFormatContext **output_files,
             }
         }
     }
+    term_exit();
 
     /* dump report by using the first video and audio streams */
     {
@@ -1378,6 +1015,8 @@ static int av_encode(AVFormatContext **output_files,
                 frame_number = ist->frame_number;
                 sprintf(buf + strlen(buf), "frame=%5d q=%2d ",
                         frame_number, enc->quality);
+                if (do_psnr)
+                    sprintf(buf + strlen(buf), "PSNR=%6.2f ", enc->psnr_y);
                 vid = 1;
             }
             /* compute min pts value */
@@ -1516,6 +1155,11 @@ void opt_video_bitrate(const char *arg)
     video_bit_rate = atoi(arg) * 1000;
 }
 
+void opt_video_bitrate_tolerance(const char *arg)
+{
+    video_bit_rate_tolerance = atoi(arg) * 1000;
+}
+
 void opt_frame_rate(const char *arg)
 {
     frame_rate = (int)(strtod(arg, 0) * FRAME_RATE_BASE);
@@ -1549,6 +1193,45 @@ void opt_qscale(const char *arg)
     }
 }
 
+void opt_qmin(const char *arg)
+{
+    video_qmin = atoi(arg);
+    if (video_qmin < 0 ||
+        video_qmin > 31) {
+        fprintf(stderr, "qmin must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qmax(const char *arg)
+{
+    video_qmax = atoi(arg);
+    if (video_qmax < 0 ||
+        video_qmax > 31) {
+        fprintf(stderr, "qmax must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qdiff(const char *arg)
+{
+    video_qdiff = atoi(arg);
+    if (video_qdiff < 0 ||
+        video_qdiff > 31) {
+        fprintf(stderr, "qdiff must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qblur(const char *arg)
+{
+    video_qblur = atof(arg);
+}
+
+void opt_qcomp(const char *arg)
+{
+    video_qcomp = atof(arg);
+}
 
 void opt_audio_bitrate(const char *arg)
 {
@@ -1941,12 +1624,15 @@ void opt_output_file(const char *filename)
         use_video = file_format->video_codec != CODEC_ID_NONE;
         use_audio = file_format->audio_codec != CODEC_ID_NONE;
 
-        /* disable if no corresponding type found */
-        check_audio_video_inputs(&input_has_video, &input_has_audio);
-        if (!input_has_video)
-            use_video = 0;
-        if (!input_has_audio)
-            use_audio = 0;
+        /* disable if no corresponding type found and at least one
+           input file */
+        if (nb_input_files > 0) {
+            check_audio_video_inputs(&input_has_video, &input_has_audio);
+            if (!input_has_video)
+                use_video = 0;
+            if (!input_has_audio)
+                use_audio = 0;
+        }
 
         /* manual disable */
         if (audio_disable) {
@@ -1975,6 +1661,7 @@ void opt_output_file(const char *filename)
             video_enc->codec_type = CODEC_TYPE_VIDEO;
             
             video_enc->bit_rate = video_bit_rate;
+            video_enc->bit_rate_tolerance = video_bit_rate_tolerance;
             video_enc->frame_rate = frame_rate; 
             
             video_enc->width = frame_width;
@@ -1987,6 +1674,17 @@ void opt_output_file(const char *filename)
                 video_enc->flags |= CODEC_FLAG_QSCALE;
                 video_enc->quality = video_qscale;
             }
+            
+            video_enc->qmin= video_qmin;
+            video_enc->qmax= video_qmax;
+            video_enc->max_qdiff= video_qdiff;
+            video_enc->qblur= video_qblur;
+            video_enc->qcompress= video_qcomp;
+            
+            if (do_psnr)
+                video_enc->get_psnr = 1;
+            else
+                video_enc->get_psnr = 0;
             /* XXX: need to find a way to set codec parameters */
             if (oc->format == &ppm_format ||
                 oc->format == &ppmpipe_format) {
@@ -2082,6 +1780,97 @@ 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)
+{
+#ifndef __BEOS__
+    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);
+#endif
+}
+
+
 #ifndef CONFIG_WIN32
 INT64 getutime(void)
 {
@@ -2157,13 +1946,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)
@@ -2206,6 +2006,12 @@ const OptionDef options[] = {
     { "intra", OPT_BOOL | OPT_EXPERT, {(void*)&intra_only}, "use only intra frames"},
     { "vn", OPT_BOOL, {(void*)&video_disable}, "disable video" },
     { "qscale", HAS_ARG | OPT_EXPERT, {(void*)opt_qscale}, "use fixed video quantiser scale (VBR)", "q" },
+    { "qmin", HAS_ARG | OPT_EXPERT, {(void*)opt_qmin}, "min video quantiser scale (VBR)", "q" },
+    { "qmax", HAS_ARG | OPT_EXPERT, {(void*)opt_qmax}, "max video quantiser scale (VBR)", "q" },
+    { "qdiff", HAS_ARG | OPT_EXPERT, {(void*)opt_qdiff}, "max difference between the quantiser scale (VBR)", "q" },
+    { "qblur", HAS_ARG | OPT_EXPERT, {(void*)opt_qblur}, "video quantiser scale blur (VBR)", "blur" },
+    { "qcomp", HAS_ARG | OPT_EXPERT, {(void*)opt_qcomp}, "video quantiser scale compression (VBR)", "compression" },
+    { "bt", HAS_ARG, {(void*)opt_video_bitrate_tolerance}, "set video bitrate tolerance (in kbit/s)", "tolerance" },
 #ifdef CONFIG_GRAB
     { "vd", HAS_ARG | OPT_EXPERT, {(void*)opt_video_device}, "set video device", "device" },
 #endif
@@ -2227,6 +2033,9 @@ const OptionDef options[] = {
       "deinterlace pictures" },
     { "benchmark", OPT_BOOL | OPT_EXPERT, {(void*)&do_benchmark}, 
       "add timings for benchmarking" },
+    { "hex", OPT_BOOL | OPT_EXPERT, {(void*)&do_hex_dump}, 
+      "dump each input packet" },
+    { "psnr", OPT_BOOL | OPT_EXPERT, {(void*)&do_psnr}, "calculate PSNR of compressed frames" }, 
 
     { NULL, },
 };
@@ -2236,12 +2045,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++];
@@ -2270,35 +2086,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 */