[PATCH] Add MxPEG video decoder
anatoly
anatoly.nenashev
Mon Feb 14 12:40:44 CET 2011
---
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/mjpegdec.c | 275 +++++++++++++++++++++++++++++++++++++++++++-----
libavcodec/mjpegdec.h | 13 +++
4 files changed, 264 insertions(+), 26 deletions(-)
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 682b626..719c2b1 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -269,6 +269,7 @@ OBJS-$(CONFIG_MSMPEG4V3_ENCODER) += msmpeg4.o msmpeg4data.o h263dec.o \
OBJS-$(CONFIG_MSRLE_DECODER) += msrle.o msrledec.o
OBJS-$(CONFIG_MSVIDEO1_DECODER) += msvideo1.o
OBJS-$(CONFIG_MSZH_DECODER) += lcldec.o
+OBJS-$(CONFIG_MXPEG_DECODER) += mjpegdec.o mjpeg.o
OBJS-$(CONFIG_NELLYMOSER_DECODER) += nellymoserdec.o nellymoser.o
OBJS-$(CONFIG_NELLYMOSER_ENCODER) += nellymoserenc.o nellymoser.o
OBJS-$(CONFIG_NUV_DECODER) += nuv.o rtjpeg.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 108a3ab..9d37bd9 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -151,6 +151,7 @@ void avcodec_register_all(void)
REGISTER_DECODER (MSRLE, msrle);
REGISTER_DECODER (MSVIDEO1, msvideo1);
REGISTER_DECODER (MSZH, mszh);
+ REGISTER_DECODER (MXPEG, mxpeg);
REGISTER_DECODER (NUV, nuv);
REGISTER_ENCDEC (PAM, pam);
REGISTER_ENCDEC (PBM, pbm);
diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index fbc637a..38c7aaf 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -205,9 +205,75 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s)
return 0;
}
+static int mjpeg_allocate_picture(MJpegDecodeContext *s, AVFrame* picture_ptr,
+ int key_frame, int reference_frame)
+{
+ int i;
+
+ if (picture_ptr->data[0])
+ s->avctx->release_buffer(s->avctx, picture_ptr);
+
+ picture_ptr->reference = reference_frame;
+ if (s->avctx->get_buffer(s->avctx, picture_ptr) < 0) {
+ av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+ return -1;
+ }
+ picture_ptr->pict_type = key_frame ? FF_I_TYPE : FF_P_TYPE;
+ picture_ptr->key_frame = key_frame;
+ s->got_picture = 1;
+
+ for (i = 0; i < MAX_COMPONENTS; i++) {
+ s->linesize[i] = picture_ptr->linesize[i] << s->interlaced;
+ }
+
+ return 0;
+}
+
+static int mxpeg_allocate_picture(MXpegDecodeContext *mxctx, MJpegDecodeContext* s)
+{
+ AVFrame *picture_ptr, *reference_ptr;
+ if (s->first_picture) {
+ av_log(s->avctx, AV_LOG_WARNING, "First picture has no SOF, skipping\n");
+ return -1;
+ }
+ if (!mxctx->got_sof_data) {
+ av_log(s->avctx, AV_LOG_WARNING, "Can not process SOS after broken SOF, skipping\n");
+ return -1;
+ }
+ if (!mxctx->mxm_bitmask) {
+ av_log(s->avctx, AV_LOG_WARNING, "Non-key frame has no MXM, skipping\n");
+ return -1;
+ }
+
+ reference_ptr = &mxctx->pictures[mxctx->picture_index ^ 1];
+ if (!reference_ptr->data[0]) {
+ av_log(s->avctx, AV_LOG_WARNING, "No reference picture data for non-key frame, skipping\n");
+ return -1;
+ }
+
+ picture_ptr = &mxctx->pictures[mxctx->picture_index];
+ if (mjpeg_allocate_picture(s, picture_ptr, 0, 1) < 0)
+ return -1;
+
+ return 0;
+}
+
int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
{
int len, nb_components, i, width, height, pix_fmt_id;
+ AVFrame *picture_ptr;
+ MXpegDecodeContext *mxctx = (s->avctx->codec_id == CODEC_ID_MXPEG) ? s->avctx->priv_data : 0;
+
+ if (mxctx) {
+ mxctx->got_sof_data = 0;
+ if (s->lossless) {
+ av_log(s->avctx, AV_LOG_ERROR, "Lossless mode doesn't available in MxPEG\n");
+ return -1;
+ }
+ picture_ptr = &mxctx->pictures[mxctx->picture_index];
+ } else {
+ picture_ptr = &s->picture;
+ }
/* XXX: verify len field validity */
len = get_bits(&s->gb, 16);
@@ -282,8 +348,8 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
s->height < ((s->org_height * 3) / 4)) {
s->interlaced = 1;
s->bottom_field = s->interlace_polarity;
- s->picture.interlaced_frame = 1;
- s->picture.top_field_first = !s->interlace_polarity;
+ picture_ptr->interlaced_frame = 1;
+ picture_ptr->top_field_first = !s->interlace_polarity;
height *= 2;
}
@@ -342,20 +408,13 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
s->avctx->pix_fmt = PIX_FMT_GRAY16;
}
- if(s->picture.data[0])
- s->avctx->release_buffer(s->avctx, &s->picture);
-
- s->picture.reference= 0;
- if(s->avctx->get_buffer(s->avctx, &s->picture) < 0){
- av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
- return -1;
- }
- s->picture.pict_type= FF_I_TYPE;
- s->picture.key_frame= 1;
- s->got_picture = 1;
-
- for(i=0; i<3; i++){
- s->linesize[i]= s->picture.linesize[i] << s->interlaced;
+ if (mxctx) {
+ mxctx->got_sof_data = 1;
+ if (mjpeg_allocate_picture(s, picture_ptr, 1, 1) < 0)
+ return -1;
+ } else {
+ if (mjpeg_allocate_picture(s, picture_ptr, 1, 0) < 0)
+ return -1;
}
// printf("%d %d %d %d %d %d\n", s->width, s->height, s->linesize[0], s->linesize[1], s->interlaced, s->avctx->height);
@@ -775,6 +834,17 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, i
int i, mb_x, mb_y;
uint8_t* data[MAX_COMPONENTS];
int linesize[MAX_COMPONENTS];
+ MXpegDecodeContext *mxctx = (s->avctx->codec_id == CODEC_ID_MXPEG) ? s->avctx->priv_data : 0;
+ AVFrame *picture_ptr = mxctx ? &mxctx->pictures[mxctx->picture_index] : &s->picture,
+ *reference_ptr = mxctx ? &mxctx->pictures[mxctx->picture_index ^ 1] : 0;
+ const int use_mxm_bitmask = mxctx && mxctx->mxm_bitmask;
+ const int use_reference = reference_ptr && reference_ptr->data[0];
+ GetBitContext mxm_bitmask_gb;
+
+ if (use_mxm_bitmask) {
+ init_get_bits(&mxm_bitmask_gb, mxctx->mxm_bitmask,
+ mxctx->bitmask_size << 3);
+ }
if(s->flipped && s->avctx->flags & CODEC_FLAG_EMU_EDGE) {
av_log(s->avctx, AV_LOG_ERROR, "Can not flip image with CODEC_FLAG_EMU_EDGE set!\n");
@@ -782,7 +852,7 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, i
}
for(i=0; i < nb_components; i++) {
int c = s->comp_index[i];
- data[c] = s->picture.data[c];
+ data[c] = picture_ptr->data[c];
linesize[c]=s->linesize[c];
s->coefs_finished[c] |= 1;
if(s->flipped) {
@@ -794,12 +864,15 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, i
for(mb_y = 0; mb_y < s->mb_height; mb_y++) {
for(mb_x = 0; mb_x < s->mb_width; mb_x++) {
+ const int copy_mb = use_mxm_bitmask && !get_bits1(&mxm_bitmask_gb);
+ if (copy_mb && !use_reference) continue;
+
if (s->restart_interval && !s->restart_count)
s->restart_count = s->restart_interval;
for(i=0;i<nb_components;i++) {
uint8_t *ptr;
- int n, h, v, x, y, c, j;
+ int n, h, v, x, y, c, j, blk_offset;
n = s->nb_blocks[i];
c = s->comp_index[i];
h = s->h_scount[i];
@@ -807,11 +880,25 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, i
x = 0;
y = 0;
for(j=0;j<n;j++) {
- ptr = data[c] +
- (((linesize[c] * (v * mb_y + y) * 8) +
- (h * mb_x + x) * 8) >> s->avctx->lowres);
+ blk_offset = (((linesize[c] * (v * mb_y + y) * 8) +
+ (h * mb_x + x) * 8) >> s->avctx->lowres);
if(s->interlaced && s->bottom_field)
- ptr += linesize[c] >> 1;
+ blk_offset += linesize[c] >> 1;
+ ptr = data[c] + blk_offset;
+ if (copy_mb) {
+ const uint8_t *src = reference_ptr->data[c] + blk_offset;
+ switch (s->avctx->lowres) {
+ case 0: copy_block8(ptr, src, linesize[c], linesize[c], 8);
+ break;
+ case 1: copy_block4(ptr, src, linesize[c], linesize[c], 4);
+ break;
+ case 2: copy_block2(ptr, src, linesize[c], linesize[c], 2);
+ break;
+ case 3: *ptr = *src;
+ break;
+ default:;
+ }
+ } else {
if(!s->progressive) {
s->dsp.clear_block(s->block);
if(decode_block(s, s->block, i,
@@ -831,6 +918,7 @@ static int mjpeg_decode_scan(MJpegDecodeContext *s, int nb_components, int Ah, i
return -1;
}
}
+ }
// av_log(s->avctx, AV_LOG_DEBUG, "mb: %d %d processed\n", mb_y, mb_x);
//av_log(NULL, AV_LOG_DEBUG, "%d %d %d %d %d %d %d %d \n", mb_x, mb_y, x, y, c, s->bottom_field, (v * mb_y + y) * 8, (h * mb_x + x) * 8);
if (++x == h) {
@@ -859,6 +947,14 @@ static int mjpeg_decode_scan_progressive_ac(MJpegDecodeContext *s, int ss, int s
int linesize = s->linesize[c];
int last_scan = 0;
int16_t *quant_matrix = s->quant_matrixes[ s->quant_index[c] ];
+ MXpegDecodeContext *mxctx = (s->avctx->codec_id == CODEC_ID_MXPEG) ? s->avctx->priv_data : 0;
+
+ if (mxctx) {
+ //FIXME implement progressive mode for MxPEG if needed (no samples available now)
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Progressive mode doesn't implemented in MxPEG decoder\n");
+ return -1;
+ }
if(!Al) {
s->coefs_finished[c] |= (1LL<<(se+1))-(1LL<<ss);
@@ -900,6 +996,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s)
int index, id;
const int block_size= s->lossless ? 1 : 8;
int ilv, prev_shift;
+ MXpegDecodeContext *mxctx = (s->avctx->codec_id == CODEC_ID_MXPEG) ? s->avctx->priv_data : 0;
/* XXX: verify len field validity */
len = get_bits(&s->gb, 16);
@@ -968,6 +1065,13 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s)
s->v_scount[0] = 1;
}
+ /* compare picture size in macroblocks from SOF and MXM (MxPEG decoder) */
+ if (mxctx && mxctx->mxm_bitmask &&
+ (s->mb_width != mxctx->mb_width || s->mb_height != mxctx->mb_height)) {
+ av_log(s->avctx, AV_LOG_ERROR, "Picture size in MB's from SOF and MXM doesn't equal\n");
+ return AVERROR(EINVAL);
+ }
+
if(s->avctx->debug & FF_DEBUG_PICT_INFO)
av_log(s->avctx, AV_LOG_DEBUG, "%s %s p:%d >>:%d ilv:%d bits:%d %s\n", s->lossless ? "lossless" : "sequential DCT", s->rgb ? "RGB" : "",
predictor, point_transform, ilv, s->bits,
@@ -1168,6 +1272,49 @@ out:
return 0;
}
+static int mxpeg_decode_mxm(AVCodecContext *avctx, MXpegDecodeContext *mxctx,
+ char *buf, int len)
+{
+ int32_t bitmask_size, i;
+ uint32_t mb_count;
+
+ av_freep(&mxctx->comment_buffer);
+ mxctx->comment_buffer = buf;
+ mxctx->mxm_bitmask = buf + 12;
+
+ mxctx->mb_width = AV_RL16(&buf[4]);
+ mxctx->mb_height = AV_RL16(&buf[6]);
+ mb_count = (uint32_t)mxctx->mb_width * mxctx->mb_height;
+ if (!mb_count || mb_count > 0x7FFFFFF8) {
+ av_log(avctx, AV_LOG_ERROR, "MXM wrong macroblocks count");
+ return AVERROR(EINVAL);
+ }
+
+ bitmask_size = (mb_count + 7) >> 3;
+ if (bitmask_size > (len - 12)) {
+ av_log(avctx, AV_LOG_ERROR, "MXM bitmask is not complete");
+ return AVERROR(EINVAL);
+ }
+
+ if (mxctx->bitmask_size != bitmask_size) {
+ av_freep(&mxctx->completion_bitmask);
+ mxctx->completion_bitmask = av_mallocz(bitmask_size);
+ if (!mxctx->completion_bitmask)
+ return AVERROR(ENOMEM);
+ mxctx->bitmask_size = bitmask_size;
+ }
+
+ if (!mxctx->has_complete_frame) {
+ mxctx->has_complete_frame = 1;
+ for (i = 0; i < bitmask_size; ++i) {
+ mxctx->completion_bitmask[i] |= mxctx->mxm_bitmask[i];
+ mxctx->has_complete_frame &= !(uint8_t)~mxctx->completion_bitmask[i];
+ }
+ }
+
+ return 0;
+}
+
static int mjpeg_decode_com(MJpegDecodeContext *s)
{
int len = get_bits(&s->gb, 16);
@@ -1198,8 +1345,14 @@ static int mjpeg_decode_com(MJpegDecodeContext *s)
else if((len > 20 && !strncmp(cbuf, "Intel(R) JPEG Library", 21)) ||
(len > 19 && !strncmp(cbuf, "Metasoft MJPEG Codec", 20))){
s->flipped = 1;
+ } else if (s->avctx->codec_id == CODEC_ID_MXPEG &&
+ !strncmp(cbuf, "MXM", 3)){
+ int ret = mxpeg_decode_mxm(s->avctx, s->avctx->priv_data,
+ cbuf, len - 2);
+ if (ret < 0)
+ return ret;
+ return 0;
}
-
av_free(cbuf);
}
}
@@ -1260,6 +1413,23 @@ found:
return val;
}
+static int mxpeg_check_dimensions(MXpegDecodeContext *mxctx)
+{
+ AVFrame *picture_ptr = &mxctx->pictures[mxctx->picture_index],
+ *reference_ptr = &mxctx->pictures[mxctx->picture_index ^ 1];
+ int i;
+
+ if (mxctx->mxm_bitmask && reference_ptr->data[0]) {
+ for (i = 0; i < MAX_COMPONENTS; ++i) {
+ if ( (!reference_ptr->data[i] ^ !picture_ptr->data[i]) ||
+ reference_ptr->linesize[i] != picture_ptr->linesize[i])
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
int ff_mjpeg_decode_frame(AVCodecContext *avctx,
void *data, int *data_size,
AVPacket *avpkt)
@@ -1267,11 +1437,14 @@ int ff_mjpeg_decode_frame(AVCodecContext *avctx,
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size;
MJpegDecodeContext *s = avctx->priv_data;
+ MXpegDecodeContext *mxctx = (avctx->codec_id == CODEC_ID_MXPEG) ? avctx->priv_data : 0;
const uint8_t *buf_end, *buf_ptr;
int start_code;
AVFrame *picture = data;
s->got_picture = 0; // picture from previous image can not be reused
+ if (mxctx) mxctx->mxm_bitmask = 0;
+
buf_ptr = buf;
buf_end = buf + buf_size;
while (buf_ptr < buf_end) {
@@ -1445,8 +1618,20 @@ eoi_parser:
if (s->bottom_field == !s->interlace_polarity)
goto not_the_end;
}
- *picture = s->picture;
*data_size = sizeof(AVFrame);
+ if (mxctx) {
+ *picture = mxctx->pictures[mxctx->picture_index];
+ mxctx->picture_index ^= 1;
+ if (!mxctx->has_complete_frame) {
+ if (!mxctx->mxm_bitmask) {
+ mxctx->has_complete_frame = 1;
+ } else {
+ *data_size = 0;
+ }
+ }
+ } else {
+ *picture = s->picture;
+ }
if(!s->lossless){
picture->quality= FFMAX3(s->qscale[0], s->qscale[1], s->qscale[2]);
@@ -1463,9 +1648,21 @@ eoi_parser:
break;
case SOS:
if (!s->got_picture) {
- av_log(avctx, AV_LOG_WARNING, "Can not process SOS before SOF, skipping\n");
- break;
+ if (mxctx) {
+ if (mxpeg_allocate_picture(mxctx, s) < 0)
+ return -1;
+ } else {
+ av_log(avctx, AV_LOG_WARNING,"Can not process SOS before SOF, skipping\n");
+ break;
+ }
}
+
+ if (mxctx && mxpeg_check_dimensions(mxctx) < 0) {
+ av_log(avctx, AV_LOG_WARNING,
+ "Dimensions of reference picture don't correspond to given SOF, skipping\n");
+ return -1;
+ }
+
ff_mjpeg_decode_sos(s);
/* buggy avid puts EOI every 10-20th frame */
/* if restart period is over process EOI */
@@ -1515,6 +1712,7 @@ the_end:
av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx)
{
MJpegDecodeContext *s = avctx->priv_data;
+ MXpegDecodeContext *mxctx = (avctx->codec_id == CODEC_ID_MXPEG) ? avctx->priv_data : 0;
int i, j;
if (s->picture.data[0])
@@ -1533,6 +1731,16 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx)
av_freep(&s->blocks[i]);
av_freep(&s->last_nnz[i]);
}
+
+ if (mxctx) {
+ for (i = 0; i < 2; i++) {
+ if (mxctx->pictures[i].data[0])
+ avctx->release_buffer(avctx, &mxctx->pictures[i]);
+ }
+ av_freep(&mxctx->comment_buffer);
+ av_freep(&mxctx->completion_bitmask);
+ }
+
return 0;
}
@@ -1565,3 +1773,18 @@ AVCodec ff_thp_decoder = {
.max_lowres = 3,
.long_name = NULL_IF_CONFIG_SMALL("Nintendo Gamecube THP video"),
};
+
+AVCodec ff_mxpeg_decoder = {
+ "mxpeg",
+ AVMEDIA_TYPE_VIDEO,
+ CODEC_ID_MXPEG,
+ sizeof(MXpegDecodeContext),
+ ff_mjpeg_decode_init,
+ NULL,
+ ff_mjpeg_decode_end,
+ ff_mjpeg_decode_frame,
+ CODEC_CAP_DR1,
+ NULL,
+ .max_lowres = 3,
+ .long_name = NULL_IF_CONFIG_SMALL("Mobotix MxPEG video"),
+};
diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h
index 7baa5dc..24597d5 100644
--- a/libavcodec/mjpegdec.h
+++ b/libavcodec/mjpegdec.h
@@ -107,6 +107,19 @@ typedef struct MJpegDecodeContext {
unsigned int ljpeg_buffer_size;
} MJpegDecodeContext;
+typedef struct MXpegDecodeContext {
+ MJpegDecodeContext jpgctx;
+ int got_sof_data; /* true if SOF data successfully parsed */
+ uint8_t *comment_buffer; /* temporary comment buffer */
+ uint8_t *mxm_bitmask; /* bitmask buffer */
+ int32_t bitmask_size; /* size of bitmask */
+ uint8_t has_complete_frame; /* true if has complete frame */
+ uint8_t *completion_bitmask; /* completion bitmask of macroblocks */
+ int mb_width, mb_height; /* size of picture in MB's from MXM header */
+ AVFrame pictures[2]; /* pictures array */
+ int picture_index; /* index of current picture, other picture is reference */
+} MXpegDecodeContext;
+
int ff_mjpeg_decode_init(AVCodecContext *avctx);
int ff_mjpeg_decode_end(AVCodecContext *avctx);
int ff_mjpeg_decode_frame(AVCodecContext *avctx,
--
1.7.1
--------------040109010701040101020801--
More information about the ffmpeg-devel
mailing list