]> git.sesse.net Git - ffmpeg/blobdiff - ffmpeg.c
mpeg1&2 aspect decoding
[ffmpeg] / ffmpeg.c
index f6c7f7017dc34d09565b87f1cea3b4427603741c..4c0b01a6a2ce65777d2ce3ba5fe1e1df6a9a2006 100644 (file)
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -18,6 +18,7 @@
  */
 #define HAVE_AV_CONFIG_H
 #include "avformat.h"
+#include "framehook.h"
 
 #ifndef CONFIG_WIN32
 #include <unistd.h>
@@ -30,6 +31,9 @@
 #include <time.h>
 #include <ctype.h>
 
+#if !defined(INFINITY) && defined(HUGE_VAL)
+#define INFINITY HUGE_VAL
+#endif
 
 #define MAXINT64 INT64_C(0x7fffffffffffffff)
 
@@ -162,11 +166,16 @@ typedef struct AVOutputStream {
     /* input pts and corresponding output pts
        for A/V sync */
     double sync_ipts;
+    double sync_ipts_offset;
     INT64 sync_opts;
     /* video only */
-    AVPicture pict_tmp;         /* temporary image for resizing */
-    int video_resample;
+    int video_resample;      /* video_resample and video_crop are mutually exclusive */
+    AVPicture pict_tmp;      /* temporary image for resampling */
     ImgReSampleContext *img_resample_ctx; /* for image resampling */
+
+    int video_crop;          /* video_resample and video_crop are mutually exclusive */
+    int topBand;             /* cropping area sizes */
+    int leftBand;
     
     /* audio only */
     int audio_resample;
@@ -283,6 +292,7 @@ int read_ffserver_streams(AVFormatContext *s, const char *filename)
     s->nb_streams = ic->nb_streams;
     for(i=0;i<ic->nb_streams;i++) {
         AVStream *st;
+
         st = av_mallocz(sizeof(AVFormatContext));
         memcpy(st, ic->streams[i], sizeof(AVStream));
         s->streams[i] = st;
@@ -447,18 +457,58 @@ static void write_picture(AVFormatContext *s, int index, AVPicture *picture,
     av_free(buf);
 }
 
+static void pre_process_video_frame(AVInputStream *ist, AVPicture *picture, void **bufp)
+{
+    AVCodecContext *dec;
+    AVPicture *picture2;
+    AVPicture picture_tmp;
+    UINT8 *buf = 0;
+
+    dec = &ist->st->codec;
+
+    /* deinterlace : must be done before any resize */
+    if (do_deinterlace) {
+        int size;
+
+        /* create temporary picture */
+        size = avpicture_get_size(dec->pix_fmt, dec->width, dec->height);
+        buf = av_malloc(size);
+        if (!buf)
+            return;
+        
+        picture2 = &picture_tmp;
+        avpicture_fill(picture2, buf, dec->pix_fmt, dec->width, dec->height);
+
+        if (avpicture_deinterlace(picture2, picture, 
+                                  dec->pix_fmt, dec->width, dec->height) < 0) {
+            /* if error, do not deinterlace */
+            av_free(buf);
+            buf = NULL;
+            picture2 = picture;
+        }
+    } else {
+        picture2 = picture;
+    }
+
+    frame_hook_process(picture2, dec->pix_fmt, dec->width, dec->height);
+
+    if (picture != picture2)
+        *picture = *picture2;
+    *bufp = buf;
+}
+
 /* we begin to correct av delay at this threshold */
 #define AV_DELAY_MAX 0.100
 
 static void do_video_out(AVFormatContext *s, 
                          AVOutputStream *ost, 
                          AVInputStream *ist,
-                         AVPicture *picture1,
+                         AVPicture *in_picture,
                          int *frame_size, AVOutputStream *audio_sync)
 {
     int nb_frames, i, ret;
-    AVPicture *picture, *picture2, *pict;
-    AVPicture picture_tmp1, picture_tmp2;
+    AVPicture *final_picture, *formatted_picture;
+    AVPicture picture_format_temp, picture_crop_temp;
     static UINT8 *video_buffer;
     UINT8 *buf = NULL, *buf1 = NULL;
     AVCodecContext *enc, *dec;
@@ -496,8 +546,30 @@ static void do_video_out(AVFormatContext *s,
             else if (av_delay > AV_DELAY_MAX)
                 nb_frames = 0;
         }
+    } else {
+        double vdelta;
+
+        if (ost->sync_ipts != AV_NOPTS_VALUE) {
+            vdelta = (double)(ost->st->pts.val) * s->pts_num / s->pts_den - (ost->sync_ipts - ost->sync_ipts_offset);
+            if (vdelta < 100 && vdelta > -100) {
+                if (vdelta < -AV_DELAY_MAX)
+                    nb_frames = 2;
+                else if (vdelta > AV_DELAY_MAX)
+                    nb_frames = 0;
+            } else {
+                ost->sync_ipts_offset -= vdelta;
+            }
+
+#if 0
+            {
+                static char *action[] = { "drop frame", "copy frame", "dup frame" };
+                printf("Input PTS %12.6f, output PTS %12.6f: %s\n",
+                    (double) ost->sync_ipts, (double) ost->st->pts.val * s->pts_num / s->pts_den,
+                    action[nb_frames]);
+            }
+#endif
+        }
     }
-    /* XXX: also handle frame rate conversion */
     if (nb_frames <= 0) 
         return;
 
@@ -506,30 +578,6 @@ static void do_video_out(AVFormatContext *s,
     if (!video_buffer)
         return;
 
-    /* deinterlace : must be done before any resize */
-    if (do_deinterlace) {
-        int size;
-
-        /* create temporary picture */
-        size = avpicture_get_size(dec->pix_fmt, dec->width, dec->height);
-        buf1 = av_malloc(size);
-        if (!buf1)
-            return;
-        
-        picture2 = &picture_tmp2;
-        avpicture_fill(picture2, buf1, dec->pix_fmt, dec->width, dec->height);
-
-        if (avpicture_deinterlace(picture2, picture1, 
-                                  dec->pix_fmt, dec->width, dec->height) < 0) {
-            /* if error, do not deinterlace */
-            av_free(buf1);
-            buf1 = NULL;
-            picture2 = picture1;
-        }
-    } else {
-        picture2 = picture1;
-    }
-
     /* convert pixel format if needed */
     if (enc->pix_fmt != dec->pix_fmt) {
         int size;
@@ -539,41 +587,63 @@ static void do_video_out(AVFormatContext *s,
         buf = av_malloc(size);
         if (!buf)
             return;
-        pict = &picture_tmp1;
-        avpicture_fill(pict, buf, enc->pix_fmt, dec->width, dec->height);
+        formatted_picture = &picture_format_temp;
+        avpicture_fill(formatted_picture, buf, enc->pix_fmt, dec->width, dec->height);
         
-        if (img_convert(pict, enc->pix_fmt, 
-                        picture2, dec->pix_fmt, 
+        if (img_convert(formatted_picture, enc->pix_fmt, 
+                        in_picture, dec->pix_fmt, 
                         dec->width, dec->height) < 0) {
             fprintf(stderr, "pixel format conversion not handled\n");
             goto the_end;
         }
     } else {
-        pict = picture2;
+        formatted_picture = in_picture;
     }
 
     /* XXX: resampling could be done before raw format convertion in
        some cases to go faster */
     /* XXX: only works for YUV420P */
     if (ost->video_resample) {
-        picture = &ost->pict_tmp;
-        img_resample(ost->img_resample_ctx, picture, pict);
+        final_picture = &ost->pict_tmp;
+        img_resample(ost->img_resample_ctx, final_picture, formatted_picture);
+    } else if (ost->video_crop) {
+        picture_crop_temp.data[0] = formatted_picture->data[0] +
+                (ost->topBand * formatted_picture->linesize[0]) + ost->leftBand;
+
+        picture_crop_temp.data[1] = formatted_picture->data[1] +
+                ((ost->topBand >> 1) * formatted_picture->linesize[1]) +
+                (ost->leftBand >> 1);
+
+        picture_crop_temp.data[2] = formatted_picture->data[2] +
+                ((ost->topBand >> 1) * formatted_picture->linesize[2]) +
+                (ost->leftBand >> 1);
+
+        picture_crop_temp.linesize[0] = formatted_picture->linesize[0];
+        picture_crop_temp.linesize[1] = formatted_picture->linesize[1];
+        picture_crop_temp.linesize[2] = formatted_picture->linesize[2];
+        final_picture = &picture_crop_temp;
     } else {
-        picture = pict;
+        final_picture = formatted_picture;
     }
     /* duplicates frame if needed */
     /* XXX: pb because no interleaving */
     for(i=0;i<nb_frames;i++) {
         if (enc->codec_id != CODEC_ID_RAWVIDEO) {
+            AVVideoFrame big_picture;
+            
+            memset(&big_picture, 0, sizeof(AVVideoFrame));
+            *(AVPicture*)&big_picture= *final_picture;
+                        
             /* handles sameq here. This is not correct because it may
                not be a global option */
             if (same_quality) {
-                enc->quality = dec->quality;
-            }
+                big_picture.quality = ist->st->quality;
+            }else
+                big_picture.quality = ost->st->quality;
             
             ret = avcodec_encode_video(enc, 
                                        video_buffer, VIDEO_BUFFER_SIZE,
-                                       picture);
+                                       &big_picture);
             //enc->frame_number = enc->real_pict_num;
             av_write_frame(s, ost->index, video_buffer, ret);
             *frame_size = ret;
@@ -590,9 +660,9 @@ static void do_video_out(AVFormatContext *s,
                    avoid any copies. We support temorarily the older
                    method. */
                 av_write_frame(s, ost->index, 
-                               (UINT8 *)picture, sizeof(AVPicture));
+                               (UINT8 *)final_picture, sizeof(AVPicture));
             } else {
-                write_picture(s, ost->index, picture, enc->pix_fmt, 
+                write_picture(s, ost->index, final_picture, enc->pix_fmt, 
                               enc->width, enc->height);
             }
         }
@@ -603,6 +673,11 @@ static void do_video_out(AVFormatContext *s,
     av_free(buf1);
 }
 
+static double psnr(double d){
+    if(d==0) return INFINITY;
+    return -10.0*log(d)/log(10);
+}
+
 static void do_video_stats(AVFormatContext *os, AVOutputStream *ost, 
                            int frame_size)
 {
@@ -634,9 +709,9 @@ static void do_video_stats(AVFormatContext *os, AVOutputStream *ost,
     total_size += frame_size;
     if (enc->codec_type == CODEC_TYPE_VIDEO) {
         frame_number = ost->frame_number;
-        fprintf(fvstats, "frame= %5d q= %2d ", frame_number, enc->quality);
-        if (do_psnr)
-            fprintf(fvstats, "PSNR= %6.2f ", enc->psnr_y);
+        fprintf(fvstats, "frame= %5d q= %2.1f ", frame_number, enc->coded_picture->quality);
+        if (enc->flags&CODEC_FLAG_PSNR)
+            fprintf(fvstats, "PSNR= %6.2f ", psnr(enc->coded_picture->error[0]/(enc->width*enc->height*255.0*255.0)));
         
         fprintf(fvstats,"f_size= %6d ", frame_size);
         /* compute pts value */
@@ -648,7 +723,7 @@ static void do_video_stats(AVFormatContext *os, AVOutputStream *ost,
         avg_bitrate = (double)(total_size * 8) / ti1 / 1000.0;
         fprintf(fvstats, "s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ",
             (double)total_size / 1024, ti1, bitrate, avg_bitrate);
-        fprintf(fvstats,"type= %s\n", enc->key_frame == 1 ? "I" : "P");        
+        fprintf(fvstats,"type= %s\n", enc->coded_picture->key_frame == 1 ? "I" : "P");        
     }
 }
 
@@ -690,12 +765,16 @@ void print_report(AVFormatContext **output_files,
         ost = ost_table[i];
         os = output_files[ost->file_index];
         enc = &ost->st->codec;
+        if (vid && enc->codec_type == CODEC_TYPE_VIDEO) {
+            sprintf(buf + strlen(buf), "q=%2.1f ",
+                    enc->coded_picture->quality);
+        }
         if (!vid && enc->codec_type == CODEC_TYPE_VIDEO) {
             frame_number = ost->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);
+            sprintf(buf + strlen(buf), "frame=%5d q=%2.1f ",
+                    frame_number, enc->coded_picture ? enc->coded_picture->quality : 0);
+            if (enc->flags&CODEC_FLAG_PSNR)
+                sprintf(buf + strlen(buf), "PSNR= %6.2f ", psnr(enc->coded_picture->error[0]/(enc->width*enc->height*255.0*255.0)));
             vid = 1;
         }
         /* compute min output value */
@@ -907,11 +986,27 @@ static int av_encode(AVFormatContext **output_files,
                 break;
             case CODEC_TYPE_VIDEO:
                 if (codec->width == icodec->width &&
-                    codec->height == icodec->height) {
+                    codec->height == icodec->height &&
+                    frame_topBand == 0 &&
+                    frame_bottomBand == 0 &&
+                    frame_leftBand == 0 &&
+                    frame_rightBand == 0)
+                {
+                    ost->video_resample = 0;
+                    ost->video_crop = 0;
+                } else if ((codec->width == icodec->width -
+                                (frame_leftBand + frame_rightBand)) &&
+                        (codec->height == icodec->height -
+                                (frame_topBand  + frame_bottomBand)))
+                {
                     ost->video_resample = 0;
+                    ost->video_crop = 1;
+                    ost->topBand = frame_topBand;
+                    ost->leftBand = frame_leftBand;
                 } else {
                     UINT8 *buf;
                     ost->video_resample = 1;
+                    ost->video_crop = 0; // cropping is handled as part of resample
                     buf = av_malloc((codec->width * codec->height * 3) / 2);
                     if (!buf)
                         goto fail;
@@ -1019,8 +1114,8 @@ static int av_encode(AVFormatContext **output_files,
             AVCodec *codec;
             codec = avcodec_find_decoder(ist->st->codec.codec_id);
             if (!codec) {
-                fprintf(stderr, "Unsupported codec for input stream #%d.%d\n", 
-                        ist->file_index, ist->index);
+                fprintf(stderr, "Unsupported codec (id=%d) for input stream #%d.%d\n", 
+                        ist->st->codec.codec_id, ist->file_index, ist->index);
                 exit(1);
             }
             if (avcodec_open(&ist->st->codec, codec) < 0) {
@@ -1075,6 +1170,7 @@ static int av_encode(AVFormatContext **output_files,
         int data_size, got_picture;
         AVPicture picture;
         short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2];
+        void *buffer_to_free;
         double pts_min;
         
     redo:
@@ -1136,7 +1232,7 @@ static int av_encode(AVFormatContext **output_files,
             av_hex_dump(pkt.data, pkt.size);
         }
 
-        //        printf("read #%d.%d size=%d\n", ist->file_index, ist->index, pkt.size);
+        // printf("read #%d.%d size=%d\n", ist->file_index, ist->index, pkt.size);
 
         len = pkt.size;
         ptr = pkt.data;
@@ -1191,9 +1287,13 @@ static int av_encode(AVFormatContext **output_files,
                                      ist->st->codec.height);
                         ret = len;
                     } else {
+                        AVVideoFrame big_picture;
+
                         data_size = (ist->st->codec.width * ist->st->codec.height * 3) / 2;
                         ret = avcodec_decode_video(&ist->st->codec, 
-                                                   &picture, &got_picture, ptr, len);
+                                                   &big_picture, &got_picture, ptr, len);
+                        picture= *(AVPicture*)&big_picture;
+                        ist->st->quality= big_picture.quality;
                         if (ret < 0) {
                         fail_decode:
                             fprintf(stderr, "Error while decoding stream #%d.%d\n",
@@ -1221,6 +1321,11 @@ static int av_encode(AVFormatContext **output_files,
             ptr += ret;
             len -= ret;
 
+            buffer_to_free = 0;
+            if (ist->st->codec.codec_type == CODEC_TYPE_VIDEO) {
+                pre_process_video_frame(ist, &picture, &buffer_to_free);
+            }
+
             ist->frame_decoded = 1;
 
 #if 0
@@ -1257,6 +1362,9 @@ static int av_encode(AVFormatContext **output_files,
                         /* XXX: take into account the various fifos,
                            in particular for audio */
                         ost->sync_opts = ost->st->pts.val;
+                        //printf("ipts=%lld sync_ipts=%f sync_opts=%lld pts.val=%lld pkt.pts=%lld\n", ipts, ost->sync_ipts, ost->sync_opts, ost->st->pts.val, pkt.pts); 
+                    } else {
+                        //printf("pts.val=%lld\n", ost->st->pts.val); 
                     }
 
                     if (ost->encoding_needed) {
@@ -1296,6 +1404,7 @@ static int av_encode(AVFormatContext **output_files,
                     }
                 }
             }
+            av_free(buffer_to_free);
             ipts = AV_NOPTS_VALUE;
         }
     discard_packet:
@@ -1719,6 +1828,25 @@ void opt_audio_codec(const char *arg)
     }
 }
 
+void add_frame_hooker(const char *arg)
+{
+    int argc = 0;
+    char *argv[64];
+    int i;
+    char *args = strdup(arg);
+
+    argv[0] = strtok(args, " ");
+    while (argc < 62 && (argv[++argc] = strtok(NULL, " "))) {
+    }
+
+    i = frame_hook_add(argc, argv);
+
+    if (i != 0) {
+        fprintf(stderr, "Failed to add video hook function: %s\n", arg);
+        exit(1);
+    }
+}
+
 const char *motion_str[] = {
     "zero",
     "full",
@@ -1973,6 +2101,7 @@ void opt_output_file(const char *filename)
                 fprintf(stderr, "Could not alloc stream\n");
                 exit(1);
             }
+            avcodec_get_context_defaults(&st->codec);
 
             video_enc = &st->codec;
             if (video_stream_copy) {
@@ -2001,7 +2130,7 @@ void opt_output_file(const char *filename)
                     video_enc->gop_size = 0;
                 if (video_qscale || same_quality) {
                     video_enc->flags |= CODEC_FLAG_QSCALE;
-                    video_enc->quality = video_qscale;
+                    st->quality = video_qscale;
                 }
             
                 if (use_hq) {
@@ -2076,9 +2205,7 @@ void opt_output_file(const char *filename)
                 }
             
                 if (do_psnr)
-                    video_enc->get_psnr = 1;
-                else
-                    video_enc->get_psnr = 0;
+                    video_enc->flags|= CODEC_FLAG_PSNR;
             
                 video_enc->me_method = me_method;
 
@@ -2108,6 +2235,7 @@ void opt_output_file(const char *filename)
                 fprintf(stderr, "Could not alloc stream\n");
                 exit(1);
             }
+            avcodec_get_context_defaults(&st->codec);
 
             audio_enc = &st->codec;
             audio_enc->codec_type = CODEC_TYPE_AUDIO;
@@ -2489,6 +2617,7 @@ const OptionDef options[] = {
     { "psnr", OPT_BOOL | OPT_EXPERT, {(void*)&do_psnr}, "calculate PSNR of compressed frames" },
     { "vstats", OPT_BOOL | OPT_EXPERT, {(void*)&do_vstats}, "dump video coding statistics to file" }, 
     { "bitexact", OPT_EXPERT, {(void*)opt_bitexact}, "only use bit exact algorithms (for codec testing)" }, 
+    { "vhook", HAS_ARG | OPT_EXPERT, {(void*)add_frame_hooker}, "insert video processing module", "module name and parameters" },
     { NULL, },
 };
 
@@ -2526,8 +2655,13 @@ int main(int argc, char **argv)
                 exit(1);
             }
             arg = NULL;
-            if (po->flags & HAS_ARG)
+            if (po->flags & HAS_ARG) {
                 arg = argv[optindex++];
+                if (!arg) {
+                    fprintf(stderr, "%s: missing argument for option '%s'\n", argv[0], opt);
+                    exit(1);
+                }
+            }
             if (po->flags & OPT_STRING) {
                 char *str;
                 str = strdup(arg);
@@ -2588,5 +2722,6 @@ int main(int argc, char **argv)
     for(i=0;i<nb_input_files;i++)
         av_close_input_file(input_files[i]);
 
+    av_free_static();
     return 0;
 }