On Tue, 14 Jun 2005, Steven M. Schultz wrote:
> On Mon, 13 Jun 2005, I wrote:
> > porting [some feature-patches] forward from 1.6.2 to RC? or CVS, and
was wondering if I
>
> Against CVS would be the best choice since cvs has diverged quite
> a bit from the RC1 tarball.
>
> > should offer them up for review and possible inclusion now, or wait until
> > after a release for the next cycle.
>
> Hmmm, don't hold your breath unless blue is one of your favorite
> colors? :-) Things move very slowly these days - seems there
> aren't many _active_ developers remaining (maybe everyone's just
> searching for new suppliers of "free time" :)).
>
> Go ahead and create the patches and post 'em - if they're not too
> likely to cause breakage they can probably be slipped in to cvs for
> when the next RC is created.
Thanks, Steve.
Here's one little patch that makes my live-to-mjpeg recordings look much
better.
When decoding a mjpeg .avi, lavy2uv can sometimes print this message:
"Corrupt JPEG data: 30 extraneous bytes before marker 0xd9"
When this happens part of a field contains some kind of garbage
that is often a visualy noticable as a "flash."
Mjpeg AVIs containing corruption like this can result from lavrec
using zoran card, perhaps if quality is set too high or somting else
causes an occaisional dma overrun.
This patch for lav2yuv and related library routines adds an option to
perform the simplest, crudest error-concealment to these damaged
frames: repeat the previous frame. The result looks way better on my
test recordings.
Under this patch, lav2yuv with no options behaves as before.
new option "lav2yuv -c" enables the corrupt-frame concealment.
No other mjpegutils/lavtools programs should be affected by this,
but percolating the error status up from the jpegutils.c code into
lav2yuv required a little API surgery in two places:
jpegutils.c/decode_jpeg_raw(): its return value which was ignored
everywhere _except_ by lav_common.c/readframe().
readframe() is only called from lav2yuv.c, which previously always
ignore its return value.
The patch adds additional return values to decode_jpeg_raw(). In
addition to returning success/fatal-error, the patch allows them to
also return a value tha means corrupt-frame-warning. I added
specification of the return values to the comments near each
definition.
--- mjpeg_play/lavtools/jpegutils.c 2004-04-04 08:07:29.000000000 -0400
+++ mjpeg_play_laverr/lavtools/jpegutils.c 2005-06-18 16:00:09.568229068
-0400
@@ -276,6 +276,10 @@
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
+
+ /* original emit_message method */
+ JMETHOD(void, original_emit_message, (j_common_ptr cinfo, int msg_level));
+ int warning_seen; /* was a corrupt-data warning seen */
};
static void my_error_exit (j_common_ptr cinfo)
@@ -291,6 +295,18 @@
longjmp (myerr->setjmp_buffer, 1);
}
+static void my_emit_message (j_common_ptr cinfo, int msg_level)
+{
+ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
+ struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err;
+
+ if(msg_level < 0)
+ myerr->warning_seen = 1;
+
+ /* call original emit_message() */
+ (myerr->original_emit_message)(cinfo, msg_level);
+}
+
#define MAX_LUMA_WIDTH 4096
#define MAX_CHROMA_WIDTH 2048
@@ -432,6 +448,12 @@
* 2: Interlaced, Bottom field first
* ctype Chroma format for decompression.
* Currently only CHROMA420 and CHROMA422 are available
+ * returns:
+ * -1 on fatal error
+ * 0 on success
+ * 1 if jpeg lib threw a "corrupt jpeg data" warning.
+ * in this case, "a damaged output image is likely."
+ *
*/
int decode_jpeg_raw (unsigned char *jpeg_data, int len,
@@ -459,6 +481,10 @@
/* We set up the normal JPEG error routines, then override error_exit. */
dinfo.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = my_error_exit;
+ /* also hook the emit_message routine to note corrupt-data warnings */
+ jerr.original_emit_message = jerr.pub.emit_message;
+ jerr.pub.emit_message = my_emit_message;
+ jerr.warning_seen = 0;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp (jerr.setjmp_buffer)) {
@@ -722,7 +748,10 @@
}
jpeg_destroy_decompress (&dinfo);
- return 0;
+ if(jerr.warning_seen)
+ return 1;
+ else
+ return 0;
ERR_EXIT:
jpeg_destroy_decompress (&dinfo);
--- mjpeg_play/lavtools/lav_common.c 2005-06-13 22:20:30.000000000 -0400
+++ mjpeg_play_laverr/lavtools/lav_common.c 2005-06-18 16:04:48.983536485
-0400
@@ -284,8 +284,15 @@
}
-
-
+/*
+ * readframe - read jpeg or dv frame into yuv buffer
+ *
+ * returns:
+ * 0 success
+ * 1 fatal error
+ * 2 corrupt data encountered;
+ * decoding can continue, but this frame may be damaged
+ */
int readframe(int numframe,
uint8_t *frame[],
LavParam *param,
@@ -293,6 +300,8 @@
{
int len, i, res, data_format;
uint8_t *frame_tmp;
+ int warn;
+ warn = 0;
if (MAX_JPEG_LEN < el.max_frame_size) {
mjpeg_error_exit1( "Max size of JPEG frame = %ld: too big",
@@ -386,10 +395,13 @@
frame[0], frame[1], frame[2]);
}
- if (res) {
- mjpeg_warn( "Decoding of Frame %d failed", numframe);
- /* TODO: Selective exit here... */
+ if (res < 0) {
+ mjpeg_warn( "Fatal Error Decoding Frame %d", numframe);
return 1;
+ } else if (res == 1) {
+ mjpeg_warn( "Decoding of Frame %d failed", numframe);
+ warn = 1;
+ res = 0;
}
@@ -401,8 +413,11 @@
frame[2][i] = 0x80;
}
}
- return 0;
-
+
+ if(warn)
+ return 2;
+ else
+ return 0;
}
--- mjpeg_play/lavtools/lav2yuv.c 2005-06-13 22:20:30.000000000 -0400
+++ mjpeg_play_laverr/lavtools/lav2yuv.c 2005-06-18 17:18:21.258925661
-0400
@@ -62,6 +62,7 @@
" -o num Frame offset - skip num frames in the beginning\n"
" if num is negative, all but the last num frames are
skipped\n"
" -f num Only num frames are written to stdout (0 means all frames)\n"
+ " -c Conceal corrupt jpeg frames by repeating previous frame\n"
" -x Exchange fields\n",
str);
exit(0);
@@ -74,12 +75,16 @@
static int scene_start;
LavParam param;
-uint8_t *frame_buf[3];
+uint8_t *frame_bufs[6];
+
+static int conceal_errframes;
+static int altbuf;
void streamout(void)
{
int framenum, movie_num=0, index[MAX_EDIT_LIST_FILES];
+ int concealnum = 0;
long int oldframe=N_EL_FRAME(el.frame_list[0])-1;
FILE *fd=NULL;
@@ -132,7 +137,30 @@
for (framenum = param.offset; framenum < (param.offset + param.frames);
++framenum)
{
- readframe(framenum, frame_buf, ¶m, el);
+ int rf;
+ uint8_t **read_buf;
+ uint8_t **frame_buf;
+ if(conceal_errframes)
+ read_buf = &frame_bufs[4 * (altbuf ^ 1)];
+ else
+ read_buf = &frame_bufs[0];
+
+ rf = readframe(framenum, read_buf, ¶m, el);
+ if(conceal_errframes) {
+ if(rf == 2) { // corrupt frame; repeat previous
+ mjpeg_debug("corrupt jpeg data in frame %d;
repeating previous frame.", framenum);
+ frame_buf = &frame_bufs[4 * altbuf];
+ concealnum++;
+ } else { // use new frame
+ frame_buf = read_buf;
+ altbuf ^= 1;
+ }
+ } else {
+ if(rf == 2)
+ mjpeg_debug("corrupt jpeg data in frame %d",
framenum);
+ frame_buf = &frame_bufs[0];
+ }
+
if (param.scenefile)
{
lum_mean =
@@ -173,6 +201,7 @@
mjpeg_error("Failed to write frame: %s", y4m_strerr(i));
}
}
+ mjpeg_info( "Repeated frames (for error concealment): %d", concealnum);
if (param.scenefile)
{
fprintf(fd, "%s %ld\n", temp, N_EL_FRAME(el.video_frames-1));
@@ -205,7 +234,7 @@
param.dar = y4m_dar_4_3;
param.chroma = Y4M_UNKNOWN;
- while ((n = getopt(argc, argv, "xmYv:S:T:D:o:f:P:A:C:")) != EOF) {
+ while ((n = getopt(argc, argv, "xmYv:S:T:D:o:f:P:A:C:c")) != EOF) {
switch (n) {
case 'v':
@@ -221,6 +250,9 @@
case 'x':
exchange_fields = 1;
break;
+ case 'c':
+ conceal_errframes = 1;
+ break;
case 'S':
param.scenefile = optarg;
break;
@@ -313,8 +345,9 @@
param.interlace = el.video_inter;
-
- init(¶m, frame_buf /*&buffer*/);
+ init(¶m, &frame_bufs[0] /*&buffer*/);
+ if(conceal_errframes)
+ init(¶m, &frame_bufs[4] /*&buffer*/);
#ifdef HAVE_LIBDV
lav_init_dv_decoder();