diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 7b45e48423..d8f6c35a10 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -131,6 +131,9 @@ void avcodec_register_all(void) #ifdef CONFIG_LJPEG_ENCODER register_avcodec(&ljpeg_encoder); #endif //CONFIG_LJPEG_ENCODER +#ifdef CONFIG_JPEGLS_ENCODER + register_avcodec(&jpegls_encoder); +#endif //CONFIG_JPEGLS_ENCODER #ifdef CONFIG_ZLIB #ifdef CONFIG_PNG_ENCODER register_avcodec(&png_encoder); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b193c26181..6a797f925c 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -44,6 +44,7 @@ enum CodecID { CODEC_ID_MJPEGB, CODEC_ID_LJPEG, CODEC_ID_SP5X, + CODEC_ID_JPEGLS, CODEC_ID_MPEG4, CODEC_ID_RAWVIDEO, CODEC_ID_MSMPEG4V1, @@ -1532,6 +1533,14 @@ typedef struct AVCodecContext { * - decoding: unused */ int context_model; +#if 0 + /** + * + * - encoding: unused + * - decoding: set by user. + */ + uint8_t * (*realloc)(struct AVCodecContext *s, uint8_t *buf, int buf_size); +#endif /** * slice flags @@ -1944,6 +1953,7 @@ extern AVCodec rv20_encoder; extern AVCodec dvvideo_encoder; extern AVCodec mjpeg_encoder; extern AVCodec ljpeg_encoder; +extern AVCodec jpegls_encoder; extern AVCodec png_encoder; extern AVCodec ppm_encoder; extern AVCodec pgm_encoder; diff --git a/libavcodec/jpeg_ls.c b/libavcodec/jpeg_ls.c new file mode 100644 index 0000000000..4906453195 --- /dev/null +++ b/libavcodec/jpeg_ls.c @@ -0,0 +1,391 @@ +/* + * JPEG-LS encoder and decoder + * Copyright (c) 2003 Michael Niedermayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file jpeg_ls.c + * JPEG-LS encoder and decoder. + */ + +#undef printf +#undef fprintf + +static inline int quantize(MJpegDecodeContext *s, int v){ //FIXME optimize + if(v==0) return 0; + if(v < 0){ + if (v >-s->t1) return -1; + else if(v >-s->t2) return -2; + else if(v >-s->t3) return -3; + else return -4; + }else{ + if (v < s->t1) return 1; + else if(v < s->t2) return 2; + else if(v < s->t3) return 3; + else return 4; + } +} + +static inline int predict8(uint8_t *src, uint8_t *last){ //FIXME perhaps its better to suppress these 2 + const int LT= last[-1]; + const int T= last[ 0]; + const int L = src[-1]; + + return mid_pred(L, L + T - LT, T); +} + +static inline int predict16(uint16_t *src, uint16_t *last){ + const int LT= last[-1]; + const int T= last[ 0]; + const int L = src[-1]; + + return mid_pred(L, L + T - LT, T); +} + +static int encode_picture_ls(AVCodecContext *avctx, unsigned char *buf, int buf_size, void *data){ + return 0; +} + +static int iso_clip(int v, int vmin, int vmax){ + if(v > vmax || v < vmin) return vmin; + else return v; +} + +static void reset_ls_coding_parameters(MJpegDecodeContext *s, int reset_all){ + const int basic_t1= 3; + const int basic_t2= 7; + const int basic_t3= 21; + int factor; + + if(s->maxval==0 || reset_all) s->maxval= (1<bits) - 1; + + if(s->maxval >=128){ + factor= (FFMIN(s->maxval, 4096) + 128)>>8; + + if(s->t1==0 || reset_all) + s->t1= iso_clip(factor*(basic_t1-2) + 2 + 3*s->near, s->near+1, s->maxval); + if(s->t2==0 || reset_all) + s->t2= iso_clip(factor*(basic_t2-3) + 3 + 5*s->near, s->t1, s->maxval); + if(s->t3==0 || reset_all) + s->t3= iso_clip(factor*(basic_t3-4) + 4 + 7*s->near, s->t2, s->maxval); + }else{ + factor= 256 / (s->maxval + 1); + + if(s->t1==0 || reset_all) + s->t1= iso_clip(FFMAX(2, basic_t1/factor + 3*s->near), s->near+1, s->maxval); + if(s->t2==0 || reset_all) + s->t2= iso_clip(FFMAX(3, basic_t2/factor + 5*s->near), s->t1, s->maxval); + if(s->t3==0 || reset_all) + s->t3= iso_clip(FFMAX(4, basic_t3/factor + 6*s->near), s->t2, s->maxval); + } + + if(s->reset==0 || reset_all) s->reset= 64; +} + +static int decode_lse(MJpegDecodeContext *s) +{ + int len, id; + + /* XXX: verify len field validity */ + len = get_bits(&s->gb, 16); + id = get_bits(&s->gb, 8); + + switch(id){ + case 1: + s->maxval= get_bits(&s->gb, 16); + s->t1= get_bits(&s->gb, 16); + s->t2= get_bits(&s->gb, 16); + s->t3= get_bits(&s->gb, 16); + s->reset= get_bits(&s->gb, 16); + + reset_ls_coding_parameters(s, 0); + //FIXME quant table? + break; + case 2: + case 3: + printf("palette not supported\n"); + return -1; + case 4: + printf("oversize image not supported\n"); + return -1; + default: + printf("invalid id %d\n", id); + return -1; + } + + return 0; +} +#if 0 +static inline void update_vlc_state(VlcState * const state, const int v, int half_count){ + int drift= state->drift; + int count= state->count; + state->error_sum += ABS(v); + drift += v; + + if(count == half_count){ + count >>= 1; + drift >>= 1; + state->error_sum >>= 1; + } + count++; + + if(drift <= -count){ + if(state->bias > -128) state->bias--; + + drift += count; + if(drift <= -count) + drift= -count + 1; + }else if(drift > 0){ + if(state->bias < 127) state->bias++; + + drift -= count; + if(drift > 0) + drift= 0; + } + + state->drift= drift; + state->count= count; +} + +#define R(p, i) (is_uint8 ? (((uint8_t*)p)[i] : ((uint16_t*)p)[i]) + +static inline int ls_decode_line(MJpegDecodeContext *s, void *lastv, void *dstv, int last2, + int w, int point_transform, int is_uint8){ + int i, x, y; + + for(x=0; x < w; x++){ + int l, t, lt, rt; + + t= R(last, 0); + if(x){ + l = t; + lt= last2; + }else{ + l = R(dst, x-1); + lt= R(last, x-1); + } + + if(xbias, maxval); + }else{ + sign= 0; + pred= clip(0, pred + state->bias, maxval); + } + + i= state->count; + k=0; + while(i < state->error_sum){ //FIXME optimize + k++; + i += i; + } + + v= get_ur_golomb_jpegls(gb, k, LIMIT-qbpp, qbpp); +#if 1 + v++; + if(v&1) v= (v>>1); + else v= -(v>>1); + + if(k==0 && 2*state->drift <= - state->count) v ^= (-1); +#else + v ^= (k==0 && 2*state->drift <= - state->count); + v++; + if(v&1) v= (v>>1); + else v= -(v>>1); + +#endif + update_vlc_state(state, v, half_count); + + if(sign) v= -v; + + if(is_uint8) ((uint8_t *)dst)[x]= (pred + v) & maxval; + else ((uint16_t*)dst)[x]= (pred + v) & maxval; + }else{ + int run_count; + + while(get_bits1(&s->gb)){ + run_count = 1< w) run_count= w - x; + else run_index++; + + for(; run_count; run_count--){ + if(is_uint8) ((uint8_t *)dst)[x++]= l; + else ((uint16_t*)dst)[x++]= l; + } + + if(x >= w) return 0; + } + + run_count= get_bits(&s->gb, log2_run[run_index]); + + for(; run_count; run_count--){ + if(is_uint8) ((uint8_t *)dst)[x++]= l; + else ((uint16_t*)dst)[x++]= l; + } + + if(run_index) run_index--; + + if(x >= w) return 0; + + t= R(last, 0); + + RItype= (l==t); + if(l==t){ + state= 366; + temp= state->error_sum + (state->count>>1); + }else{ + state= 365; + temp= state->error_sum; + } + + pred= t; + sign= l > t; + + i= state->count; + k=0; + while(i < temp){ //FIXME optimize + k++; + i += i; + } + + assert(Errval != 0); + map = (k==0 && 2*Nn < state->count) == (Errval>0); + + + if(run_count==0 && run_mode==1){ + if(get_bits1(&s->gb)){ + run_count = 1<gb, log2_run[run_index]); + else run_count=0; + if(run_index) run_index--; + run_mode=2; + } + } + run_count--; + if(run_count < 0){ + run_mode=0; + run_count=0; + diff= get_vlc_symbol(&s->gb, &p->vlc_state[context]); + if(diff>=0) diff++; + }else + diff=0; + + } + } + +/* if (s->restart_interval && !s->restart_count) + s->restart_count = s->restart_interval;*/ + + if(mb_x==0 || mb_y==0 || s->interlaced){ + for(i=0;inb_blocks[i]; + c = s->comp_index[i]; + h = s->h_scount[i]; + v = s->v_scount[i]; + x = 0; + y = 0; + linesize= s->linesize[c]; + + for(j=0; jcurrent_picture[c] + (linesize * (v * mb_y + y)) + (h * mb_x + x); //FIXME optimize this crap + if(y==0 && mb_y==0){ + if(x==0 && mb_x==0){ + pred= 128 << point_transform; + }else{ + pred= ptr[-1]; + } + }else{ + if(x==0 && mb_x==0){ + pred= ptr[-linesize]; + }else{ + PREDICT(pred, ptr[-linesize-1], ptr[-linesize], ptr[-1], predictor); + } + } + + if (s->interlaced && s->bottom_field) + ptr += linesize >> 1; + *ptr= pred + (mjpeg_decode_dc(s, s->dc_index[i]) << point_transform); + + if (++x == h) { + x = 0; + y++; + } + } + } + }else{ + for(i=0;inb_blocks[i]; + c = s->comp_index[i]; + h = s->h_scount[i]; + v = s->v_scount[i]; + x = 0; + y = 0; + linesize= s->linesize[c]; + + for(j=0; jcurrent_picture[c] + (linesize * (v * mb_y + y)) + (h * mb_x + x); //FIXME optimize this crap + PREDICT(pred, ptr[-linesize-1], ptr[-linesize], ptr[-1], predictor); + *ptr= pred + (mjpeg_decode_dc(s, s->dc_index[i]) << point_transform); + if (++x == h) { + x = 0; + y++; + } + } + } + } + if (s->restart_interval && !--s->restart_count) { + align_get_bits(&s->gb); + skip_bits(&s->gb, 16); /* skip RSTn */ + } + return 0; +} +#endif + +#ifdef CONFIG_ENCODERS +AVCodec jpegls_encoder = { //FIXME avoid MPV_* lossless jpeg shouldnt need them + "jpegls", + CODEC_TYPE_VIDEO, + CODEC_ID_JPEGLS, + sizeof(MpegEncContext), + MPV_encode_init, + encode_picture_ls, + MPV_encode_end, +}; +#endif diff --git a/libavcodec/mjpeg.c b/libavcodec/mjpeg.c index 905b5fbea7..3322bcc838 100644 --- a/libavcodec/mjpeg.c +++ b/libavcodec/mjpeg.c @@ -118,8 +118,8 @@ typedef enum { JPG4 = 0xf4, JPG5 = 0xf5, JPG6 = 0xf6, - JPG7 = 0xf7, - JPG8 = 0xf8, + SOF48 = 0xf7, ///< JPEG-LS + LSE = 0xf8, ///< JPEG-LS extension parameters JPG9 = 0xf9, JPG10 = 0xfa, JPG11 = 0xfb, @@ -417,7 +417,10 @@ static void jpeg_put_comments(MpegEncContext *s) void mjpeg_picture_header(MpegEncContext *s) { - const int lossless= s->avctx->codec_id == CODEC_ID_LJPEG; + const int lossless= s->avctx->codec_id != CODEC_ID_MJPEG; + const int ls = s->avctx->codec_id == CODEC_ID_JPEGLS; + + assert(!(ls && s->mjpeg_write_tables)); put_marker(&s->pb, SOI); @@ -427,7 +430,12 @@ void mjpeg_picture_header(MpegEncContext *s) if (s->mjpeg_write_tables) jpeg_table_header(s); - put_marker(&s->pb, lossless ? SOF3 : SOF0); + switch(s->avctx->codec_id){ + case CODEC_ID_MJPEG: put_marker(&s->pb, SOF0 ); break; + case CODEC_ID_LJPEG: put_marker(&s->pb, SOF3 ); break; + case CODEC_ID_JPEGLS: put_marker(&s->pb, SOF48); break; + default: assert(0); + } put_bits(&s->pb, 16, 17); if(lossless && s->avctx->pix_fmt == PIX_FMT_RGBA32) @@ -485,9 +493,18 @@ void mjpeg_picture_header(MpegEncContext *s) put_bits(&s->pb, 4, 1); /* DC huffman table index */ put_bits(&s->pb, 4, lossless ? 0 : 1); /* AC huffman table index */ - put_bits(&s->pb, 8, lossless ? s->avctx->prediction_method+1 : 0); /* Ss (not used) */ - put_bits(&s->pb, 8, lossless ? 0 : 63); /* Se (not used) */ + put_bits(&s->pb, 8, (lossless && !ls) ? s->avctx->prediction_method+1 : 0); /* Ss (not used) */ + + switch(s->avctx->codec_id){ + case CODEC_ID_MJPEG: put_bits(&s->pb, 8, 63); break; /* Se (not used) */ + case CODEC_ID_LJPEG: put_bits(&s->pb, 8, 0); break; /* not used */ + case CODEC_ID_JPEGLS: put_bits(&s->pb, 8, 1); break; /* ILV = line interleaved */ + default: assert(0); + } + put_bits(&s->pb, 8, 0); /* Ah/Al (not used) */ + + //FIXME DC/AC entropy table selectors stuff in jpegls } static void escape_FF(MpegEncContext *s, int start) @@ -827,11 +844,17 @@ typedef struct MJpegDecodeContext { int interlaced; /* true if interlaced */ int bottom_field; /* true if bottom field */ int lossless; + int ls; int rgb; int rct; /* standard rct */ int pegasus_rct; /* pegasus reversible colorspace transform */ int bits; /* bits per component */ + int maxval; + int near; ///< near lossless bound (si 0 for lossless) + int t1,t2,t3; + int reset; ///< context halfing intervall ?rename + int width, height; int mb_width, mb_height; int nb_components; @@ -864,6 +887,8 @@ typedef struct MJpegDecodeContext { int mjpb_skiptosod; } MJpegDecodeContext; +#include "jpeg_ls.c" //FIXME make jpeg-ls more independant + static int mjpeg_decode_dht(MJpegDecodeContext *s); static int build_vlc(VLC *vlc, const uint8_t *bits_table, const uint8_t *val_table, @@ -1521,8 +1546,8 @@ static int mjpeg_decode_sos(MJpegDecodeContext *s) #endif } - predictor= get_bits(&s->gb, 8); /* lossless predictor or start of spectral (Ss) */ - skip_bits(&s->gb, 8); /* Se */ + predictor= get_bits(&s->gb, 8); /* JPEG Ss / lossless JPEG predictor /JPEG-LS NEAR */ + int ilv= get_bits(&s->gb, 8); /* JPEG Se / JPEG-LS ILV */ skip_bits(&s->gb, 4); /* Ah */ point_transform= get_bits(&s->gb, 4); /* Al */ @@ -1544,13 +1569,19 @@ static int mjpeg_decode_sos(MJpegDecodeContext *s) } if(s->avctx->debug & FF_DEBUG_PICT_INFO) - av_log(s->avctx, AV_LOG_DEBUG, "%s %s p:%d >>:%d\n", s->lossless ? "lossless" : "sequencial DCT", s->rgb ? "RGB" : "", predictor, point_transform); + av_log(s->avctx, AV_LOG_DEBUG, "%s %s p:%d >>:%d ilv:%d bits:%d %s\n", s->lossless ? "lossless" : "sequencial DCT", s->rgb ? "RGB" : "", + predictor, point_transform, ilv, s->bits, + s->pegasus_rct ? "PRCT" : (s->rct ? "RCT" : "")); + /* mjpeg-b can have padding bytes between sos and image data, skip them */ for (i = s->mjpb_skiptosod; i > 0; i--) skip_bits(&s->gb, 8); if(s->lossless){ + if(s->ls){ +// for(){ + }else{ if(s->rgb){ if(ljpeg_decode_rgb_scan(s, predictor, point_transform) < 0) return -1; @@ -1558,6 +1589,7 @@ static int mjpeg_decode_sos(MJpegDecodeContext *s) if(ljpeg_decode_yuv_scan(s, predictor, point_transform) < 0) return -1; } + } }else{ if(mjpeg_decode_scan(s) < 0) return -1; @@ -1894,6 +1926,8 @@ static int mjpeg_decode_frame(AVCodecContext *avctx, switch(start_code) { case SOI: s->restart_interval = 0; + reset_ls_coding_parameters(s, 1); + s->restart_count = 0; /* nothing to do on SOI */ break; @@ -1916,6 +1950,16 @@ static int mjpeg_decode_frame(AVCodecContext *avctx, if (mjpeg_decode_sof(s) < 0) return -1; break; + case SOF48: + s->lossless=1; + s->ls=1; + if (mjpeg_decode_sof(s) < 0) + return -1; + break; + case LSE: + if (decode_lse(s) < 0) + return -1; + break; case EOI: if ((s->buggy_avid && !s->interlaced) || s->restart_interval) break;