*/
#define HAVE_AV_CONFIG_H
#include "avformat.h"
+#include "framehook.h"
#ifndef CONFIG_WIN32
#include <unistd.h>
#include <time.h>
#include <ctype.h>
+#if !defined(INFINITY) && defined(HUGE_VAL)
+#define INFINITY HUGE_VAL
+#endif
#define MAXINT64 INT64_C(0x7fffffffffffffff)
/* 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;
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;
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;
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;
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;
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;
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);
}
}
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)
{
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 */
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");
}
}
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 */
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;
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) {
int data_size, got_picture;
AVPicture picture;
short samples[AVCODEC_MAX_AUDIO_FRAME_SIZE / 2];
+ void *buffer_to_free;
double pts_min;
redo:
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;
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",
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
/* 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) {
}
}
}
+ av_free(buffer_to_free);
ipts = AV_NOPTS_VALUE;
}
discard_packet:
}
}
+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",
fprintf(stderr, "Could not alloc stream\n");
exit(1);
}
+ avcodec_get_context_defaults(&st->codec);
video_enc = &st->codec;
if (video_stream_copy) {
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) {
}
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;
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;
{ "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, },
};
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);
for(i=0;i<nb_input_files;i++)
av_close_input_file(input_files[i]);
+ av_free_static();
return 0;
}