[cvs] / xvidcore / src / decoder.c Repository:
ViewVC logotype

Diff of /xvidcore/src/decoder.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.20, Fri Jun 14 13:21:13 2002 UTC revision 1.28, Fri Jul 12 00:49:59 2002 UTC
# Line 32  Line 32 
32   *   *
33   *  History:   *  History:
34   *   *
35     *  10.07.2002  added BFRAMES_DEC_DEBUG support
36     *              Fix a little bug for low_delay flage
37     *              MinChen <chenm001@163.com>
38     *  28.06.2002  added basic resync support to iframe/pframe_decode()
39     *  22.06.2002  added primative N_VOP support
40     *              #define BFRAMES_DEC now enables Minchen's bframe decoder
41   *  08.05.2002  add low_delay support for B_VOP decode   *  08.05.2002  add low_delay support for B_VOP decode
42   *              MinChen <chenm001@163.com>   *              MinChen <chenm001@163.com>
43   *  05.05.2002  fix some B-frame decode problem   *  05.05.2002  fix some B-frame decode problem
# Line 54  Line 60 
60  #include <stdlib.h>  #include <stdlib.h>
61  #include <string.h>  #include <string.h>
62    
63    #ifdef BFRAMES_DEC_DEBUG
64            #define BFRAMES_DEC
65    #endif
66    
67  #include "xvid.h"  #include "xvid.h"
68  #include "portab.h"  #include "portab.h"
69    
# Line 201  Line 211 
211                                  const uint32_t cbp,                                  const uint32_t cbp,
212                                  Bitstream * bs,                                  Bitstream * bs,
213                                  const uint32_t quant,                                  const uint32_t quant,
214                                  const uint32_t intra_dc_threshold)                                  const uint32_t intra_dc_threshold,
215                                    const unsigned int bound)
216  {  {
217    
218          DECLARE_ALIGNED_MATRIX(block, 6, 64, int16_t, CACHE_LINE);          DECLARE_ALIGNED_MATRIX(block, 6, 64, int16_t, CACHE_LINE);
# Line 227  Line 238 
238    
239                  start_timer();                  start_timer();
240                  predict_acdc(dec->mbs, x_pos, y_pos, dec->mb_width, i, &block[i * 64],                  predict_acdc(dec->mbs, x_pos, y_pos, dec->mb_width, i, &block[i * 64],
241                                           iQuant, iDcScaler, predictors);                                           iQuant, iDcScaler, predictors, bound);
242                  if (!acpred_flag) {                  if (!acpred_flag) {
243                          pMB->acpred_directions[i] = 0;                          pMB->acpred_directions[i] = 0;
244                  }                  }
# Line 246  Line 257 
257    
258                          block[i * 64 + 0] = dc_dif;                          block[i * 64 + 0] = dc_dif;
259                          start_coeff = 1;                          start_coeff = 1;
260    
261                            DPRINTF(DPRINTF_COEFF,"block[0] %i", dc_dif);
262                  } else {                  } else {
263                          start_coeff = 0;                          start_coeff = 0;
264                  }                  }
# Line 420  Line 433 
433                             int quant,                             int quant,
434                             int intra_dc_threshold)                             int intra_dc_threshold)
435  {  {
436            uint32_t bound;
437          uint32_t x, y;          uint32_t x, y;
438    
439            bound = 0;
440    
441          for (y = 0; y < dec->mb_height; y++) {          for (y = 0; y < dec->mb_height; y++) {
442                  for (x = 0; x < dec->mb_width; x++) {                  for (x = 0; x < dec->mb_width; x++) {
443                          MACROBLOCK *mb = &dec->mbs[y * dec->mb_width + x];                          MACROBLOCK *mb;
   
444                          uint32_t mcbpc;                          uint32_t mcbpc;
445                          uint32_t cbpc;                          uint32_t cbpc;
446                          uint32_t acpred_flag;                          uint32_t acpred_flag;
447                          uint32_t cbpy;                          uint32_t cbpy;
448                          uint32_t cbp;                          uint32_t cbp;
449    
450                            while (BitstreamShowBits(bs, 9) == 1)
451                                    BitstreamSkip(bs, 9);
452    
453                            if (check_resync_marker(bs, 0))
454                            {
455                                    bound = read_video_packet_header(bs, 0, &quant);
456                                    x = bound % dec->mb_width;
457                                    y = bound / dec->mb_width;
458                            }
459                            mb = &dec->mbs[y * dec->mb_width + x];
460    
461                            DPRINTF(DPRINTF_MB, "macroblock (%i,%i) %08x", x, y, BitstreamShowBits(bs, 32));
462    
463                          mcbpc = get_mcbpc_intra(bs);                          mcbpc = get_mcbpc_intra(bs);
464                          mb->mode = mcbpc & 7;                          mb->mode = mcbpc & 7;
465                          cbpc = (mcbpc >> 4);                          cbpc = (mcbpc >> 4);
466    
467                          acpred_flag = BitstreamGetBit(bs);                          acpred_flag = BitstreamGetBit(bs);
468    
                         if (mb->mode == MODE_STUFFING) {  
                                 DEBUG("-- STUFFING ?");  
                                 continue;  
                         }  
   
469                          cbpy = get_cbpy(bs, 1);                          cbpy = get_cbpy(bs, 1);
470                          cbp = (cbpy << 2) | cbpc;                          cbp = (cbpy << 2) | cbpc;
471    
# Line 463  Line 485 
485                          }                          }
486    
487                          decoder_mbintra(dec, mb, x, y, acpred_flag, cbp, bs, quant,                          decoder_mbintra(dec, mb, x, y, acpred_flag, cbp, bs, quant,
488                                                          intra_dc_threshold);                                                          intra_dc_threshold, bound);
489                  }                  }
490          }          }
491    
# Line 477  Line 499 
499                                    int y,                                    int y,
500                                    int k,                                    int k,
501                                    VECTOR * mv,                                    VECTOR * mv,
502                                    int fcode)                                    int fcode,
503                                      const int bound)
504  {  {
505    
506          int scale_fac = 1 << (fcode - 1);          int scale_fac = 1 << (fcode - 1);
# Line 485  Line 508 
508          int low = ((-32) * scale_fac);          int low = ((-32) * scale_fac);
509          int range = (64 * scale_fac);          int range = (64 * scale_fac);
510    
511          VECTOR pmv[4];          VECTOR pmv;
         int32_t psad[4];  
   
512          int mv_x, mv_y;          int mv_x, mv_y;
         int pmv_x, pmv_y;  
   
   
         get_pmvdata(dec->mbs, x, y, dec->mb_width, k, pmv, psad);  
513    
514          pmv_x = pmv[0].x;          pmv = get_pmv2(dec->mbs, dec->mb_width, bound, x, y, k);
         pmv_y = pmv[0].y;  
515    
516          mv_x = get_mv(bs, fcode);          mv_x = get_mv(bs, fcode);
517          mv_y = get_mv(bs, fcode);          mv_y = get_mv(bs, fcode);
518    
519          mv_x += pmv_x;          DPRINTF(DPRINTF_MV,"mv_diff (%i,%i) pred (%i,%i)", mv_x, mv_y, pmv.x, pmv.y);
520          mv_y += pmv_y;  
521            mv_x += pmv.x;
522            mv_y += pmv.y;
523    
524          if (mv_x < low) {          if (mv_x < low) {
525                  mv_x += range;                  mv_x += range;
# Line 531  Line 549 
549  {  {
550    
551          uint32_t x, y;          uint32_t x, y;
552            uint32_t bound;
553    
554          start_timer();          start_timer();
555          image_setedges(&dec->refn[0], dec->edged_width, dec->edged_height,          image_setedges(&dec->refn[0], dec->edged_width, dec->edged_height,
556                                     dec->width, dec->height, dec->interlacing);                                     dec->width, dec->height, dec->interlacing);
557          stop_edges_timer();          stop_edges_timer();
558    
559            bound = 0;
560    
561          for (y = 0; y < dec->mb_height; y++) {          for (y = 0; y < dec->mb_height; y++) {
562                  for (x = 0; x < dec->mb_width; x++) {                  for (x = 0; x < dec->mb_width; x++) {
563                          MACROBLOCK *mb = &dec->mbs[y * dec->mb_width + x];                          MACROBLOCK *mb;
564    
565                            // skip stuffing
566                            while (BitstreamShowBits(bs, 10) == 1)
567                                    BitstreamSkip(bs, 10);
568    
569                            if (check_resync_marker(bs, fcode - 1))
570                            {
571                                    bound = read_video_packet_header(bs, fcode - 1, &quant);
572                                    x = bound % dec->mb_width;
573                                    y = bound / dec->mb_width;
574                            }
575                            mb = &dec->mbs[y * dec->mb_width + x];
576    
577                            DPRINTF(DPRINTF_MB, "macroblock (%i,%i) %08x", x, y, BitstreamShowBits(bs, 32));
578    
579                          //if (!(dec->mb_skip[y*dec->mb_width + x]=BitstreamGetBit(bs)))         // not_coded                          //if (!(dec->mb_skip[y*dec->mb_width + x]=BitstreamGetBit(bs)))         // not_coded
580                          if (!(BitstreamGetBit(bs)))     // not_coded                          if (!(BitstreamGetBit(bs)))     // not_coded
# Line 554  Line 589 
589                                  mcbpc = get_mcbpc_inter(bs);                                  mcbpc = get_mcbpc_inter(bs);
590                                  mb->mode = mcbpc & 7;                                  mb->mode = mcbpc & 7;
591                                  cbpc = (mcbpc >> 4);                                  cbpc = (mcbpc >> 4);
592    
593                                    DPRINTF(DPRINTF_MB, "mode %i", mb->mode);
594                                    DPRINTF(DPRINTF_MB, "cbpc %i", cbpc);
595                                  acpred_flag = 0;                                  acpred_flag = 0;
596    
597                                  intra = (mb->mode == MODE_INTRA || mb->mode == MODE_INTRA_Q);                                  intra = (mb->mode == MODE_INTRA || mb->mode == MODE_INTRA_Q);
# Line 562  Line 600 
600                                          acpred_flag = BitstreamGetBit(bs);                                          acpred_flag = BitstreamGetBit(bs);
601                                  }                                  }
602    
                                 if (mb->mode == MODE_STUFFING) {  
                                         DEBUG("-- STUFFING ?");  
                                         continue;  
                                 }  
   
603                                  cbpy = get_cbpy(bs, intra);                                  cbpy = get_cbpy(bs, intra);
604                                    DPRINTF(DPRINTF_MB, "cbpy %i", cbpy);
605    
606                                  cbp = (cbpy << 2) | cbpc;                                  cbp = (cbpy << 2) | cbpc;
607    
608                                  if (mb->mode == MODE_INTER_Q || mb->mode == MODE_INTRA_Q) {                                  if (mb->mode == MODE_INTER_Q || mb->mode == MODE_INTRA_Q) {
609                                          quant += dquant_table[BitstreamGetBits(bs, 2)];                                          int dquant = dquant_table[BitstreamGetBits(bs, 2)];
610                                            DPRINTF(DPRINTF_MB, "dquant %i", dquant);
611                                            quant += dquant;
612                                          if (quant > 31) {                                          if (quant > 31) {
613                                                  quant = 31;                                                  quant = 31;
614                                          } else if (mb->quant < 1) {                                          } else if (quant < 1) {
615                                                  quant = 1;                                                  quant = 1;
616                                          }                                          }
617                                            DPRINTF(DPRINTF_MB, "quant %i", quant);
618                                  }                                  }
619                                  mb->quant = quant;                                  mb->quant = quant;
620    
# Line 600  Line 638 
638                                  if (mb->mode == MODE_INTER || mb->mode == MODE_INTER_Q) {                                  if (mb->mode == MODE_INTER || mb->mode == MODE_INTER_Q) {
639                                          if (dec->interlacing && mb->field_pred) {                                          if (dec->interlacing && mb->field_pred) {
640                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0],                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0],
641                                                                                    fcode);                                                                                    fcode, bound);
642                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[1],                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[1],
643                                                                                    fcode);                                                                                    fcode, bound);
644                                          } else {                                          } else {
645                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0],                                                  get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0],
646                                                                                    fcode);                                                                                    fcode, bound);
647                                                  mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x =                                                  mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x =
648                                                          mb->mvs[0].x;                                                          mb->mvs[0].x;
649                                                  mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y =                                                  mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y =
# Line 613  Line 651 
651                                          }                                          }
652                                  } else if (mb->mode ==                                  } else if (mb->mode ==
653                                                     MODE_INTER4V /* || mb->mode == MODE_INTER4V_Q */ ) {                                                     MODE_INTER4V /* || mb->mode == MODE_INTER4V_Q */ ) {
654                                          get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0], fcode);                                          get_motion_vector(dec, bs, x, y, 0, &mb->mvs[0], fcode, bound);
655                                          get_motion_vector(dec, bs, x, y, 1, &mb->mvs[1], fcode);                                          get_motion_vector(dec, bs, x, y, 1, &mb->mvs[1], fcode, bound);
656                                          get_motion_vector(dec, bs, x, y, 2, &mb->mvs[2], fcode);                                          get_motion_vector(dec, bs, x, y, 2, &mb->mvs[2], fcode, bound);
657                                          get_motion_vector(dec, bs, x, y, 3, &mb->mvs[3], fcode);                                          get_motion_vector(dec, bs, x, y, 3, &mb->mvs[3], fcode, bound);
658                                  } else                  // MODE_INTRA, MODE_INTRA_Q                                  } else                  // MODE_INTRA, MODE_INTRA_Q
659                                  {                                  {
660                                          mb->mvs[0].x = mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x =                                          mb->mvs[0].x = mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x =
# Line 624  Line 662 
662                                          mb->mvs[0].y = mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y =                                          mb->mvs[0].y = mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y =
663                                                  0;                                                  0;
664                                          decoder_mbintra(dec, mb, x, y, acpred_flag, cbp, bs, quant,                                          decoder_mbintra(dec, mb, x, y, acpred_flag, cbp, bs, quant,
665                                                                          intra_dc_threshold);                                                                          intra_dc_threshold, bound);
666                                          continue;                                          continue;
667                                  }                                  }
668    
# Line 1051  Line 1089 
1089    
1090          uint32_t x, y;          uint32_t x, y;
1091          VECTOR mv, zeromv;          VECTOR mv, zeromv;
1092    #ifdef BFRAMES_DEC_DEBUG
1093            FILE *fp;
1094            static char first=0;
1095    #define BFRAME_DEBUG    if (!first && fp){ \
1096                    fprintf(fp,"Y=%3d   X=%3d   MB=%2d   CBP=%02X\n",y,x,mb->mb_type,mb->cbp); \
1097            }
1098    #endif
1099    
1100          start_timer();          start_timer();
1101          image_setedges(&dec->refn[0], dec->edged_width, dec->edged_height,          image_setedges(&dec->refn[0], dec->edged_width, dec->edged_height,
# Line 1058  Line 1103 
1103          //image_setedges(&dec->refn[1], dec->edged_width, dec->edged_height, dec->width, dec->height, dec->interlacing);          //image_setedges(&dec->refn[1], dec->edged_width, dec->edged_height, dec->width, dec->height, dec->interlacing);
1104          stop_edges_timer();          stop_edges_timer();
1105    
1106    #ifdef BFRAMES_DEC_DEBUG
1107            if (!first){
1108                    fp=fopen("C:\\XVIDDBG.TXT","w");
1109            }
1110    #endif
1111    
1112          for (y = 0; y < dec->mb_height; y++) {          for (y = 0; y < dec->mb_height; y++) {
1113                  // Initialize Pred Motion Vector                  // Initialize Pred Motion Vector
# Line 1066  Line 1116 
1116                          MACROBLOCK *mb = &dec->mbs[y * dec->mb_width + x];                          MACROBLOCK *mb = &dec->mbs[y * dec->mb_width + x];
1117                          MACROBLOCK *last_mb = &dec->last_mbs[y * dec->mb_width + x];                          MACROBLOCK *last_mb = &dec->last_mbs[y * dec->mb_width + x];
1118    
1119                          mb->mvs[0].x = mb->mvs[0].y = zeromv.x = zeromv.y = mv.x = mv.y =                          mb->mvs[0].x = mb->mvs[0].y = zeromv.x = zeromv.y = mv.x = mv.y = 0;
                                 0;  
1120    
1121                          // the last P_VOP is skip macroblock ?                          // the last P_VOP is skip macroblock ?
1122                          if (last_mb->mode == MODE_NOT_CODED) {                          if (last_mb->mode == MODE_NOT_CODED) {
1123                                  //DEBUG2("Skip MB in B-frame at (X,Y)=!",x,y);                                  //DEBUG2("Skip MB in B-frame at (X,Y)=!",x,y);
1124                                  mb->mb_type = MODE_FORWARD;                                  mb->mb_type = MODE_NOT_CODED;
1125                                  mb->cbp = 0;                                  mb->cbp = 0;
1126    #ifdef BFRAMES_DEC_DEBUG
1127            BFRAME_DEBUG
1128    #endif
1129                                    mb->mb_type = MODE_FORWARD;
1130                                  mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x = mb->mvs[0].x;                                  mb->mvs[1].x = mb->mvs[2].x = mb->mvs[3].x = mb->mvs[0].x;
1131                                  mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y = mb->mvs[0].y;                                  mb->mvs[1].y = mb->mvs[2].y = mb->mvs[3].y = mb->mvs[0].y;
1132                                  mb->quant = 8;                                  mb->quant = 8;
# Line 1098  Line 1151 
1151    
1152                                          if (quant > 31) {                                          if (quant > 31) {
1153                                                  quant = 31;                                                  quant = 31;
1154                                          } else if (mb->quant < 1) {                                          } else if (quant < 1) {
1155                                                  quant = 1;                                                  quant = 1;
1156                                          }                                          }
1157                                  } else {                                  } else {
# Line 1113  Line 1166 
1166                          mb->mode = MODE_INTER;                          mb->mode = MODE_INTER;
1167                          //DEBUG1("Switch bm_type=",mb->mb_type);                          //DEBUG1("Switch bm_type=",mb->mb_type);
1168    
1169    #ifdef BFRAMES_DEC_DEBUG
1170            BFRAME_DEBUG
1171    #endif
1172                          switch (mb->mb_type) {                          switch (mb->mb_type) {
1173                          case MODE_DIRECT:                          case MODE_DIRECT:
1174                                  get_b_motion_vector(dec, bs, x, y, &mb->mvs[0], 1, zeromv);                                  get_b_motion_vector(dec, bs, x, y, &mb->mvs[0], 1, zeromv);
# Line 1193  Line 1249 
1249                                  break;                                  break;
1250    
1251                          default:                          default:
1252                                  DEBUG1("Not support B-frame mb_type =", mb->mb_type);                                  //DEBUG1("Not support B-frame mb_type =", mb->mb_type);
1253                                    ;
1254                          }                          }
1255    
1256                  }                                               // end of FOR                  }                                               // end of FOR
1257          }          }
1258    #ifdef BFRAMES_DEC_DEBUG
1259            if (!first){
1260                    first=1;
1261                    if (fp)
1262                            fclose(fp);
1263            }
1264    #endif
1265  }  }
1266    
1267  // swap two MACROBLOCK array  // swap two MACROBLOCK array
# Line 1241  Line 1305 
1305          case P_VOP:          case P_VOP:
1306                  decoder_pframe(dec, &bs, rounding, quant, fcode_forward,                  decoder_pframe(dec, &bs, rounding, quant, fcode_forward,
1307                                             intra_dc_threshold);                                             intra_dc_threshold);
1308    #ifdef BFRAMES_DEC
1309                  DEBUG1("P_VOP  Time=", dec->time);                  DEBUG1("P_VOP  Time=", dec->time);
1310    #endif
1311                  break;                  break;
1312    
1313          case I_VOP:          case I_VOP:
1314                  decoder_iframe(dec, &bs, quant, intra_dc_threshold);                  decoder_iframe(dec, &bs, quant, intra_dc_threshold);
1315    #ifdef BFRAMES_DEC
1316                  DEBUG1("I_VOP  Time=", dec->time);                  DEBUG1("I_VOP  Time=", dec->time);
1317    #endif
1318                  break;                  break;
1319    
1320          case B_VOP:          case B_VOP:
1321  #ifdef BFRAMES  #ifdef BFRAMES_DEC
1322                  if (dec->time_pp > dec->time_bp) {                  if (dec->time_pp > dec->time_bp) {
1323                          DEBUG1("B_VOP  Time=", dec->time);                          DEBUG1("B_VOP  Time=", dec->time);
1324                          decoder_bframe(dec, &bs, quant, fcode_forward, fcode_backward);                          decoder_bframe(dec, &bs, quant, fcode_forward, fcode_backward);
1325                  } else {                  } else {
1326                          DEBUG("broken B-frame!");                          DEBUG("broken B-frame!");
1327                  }                  }
1328    #else
1329                    image_copy(&dec->cur, &dec->refn[0], dec->edged_width, dec->height);
1330  #endif  #endif
1331                  break;                  break;
1332    
1333          case N_VOP:                             // vop not coded          case N_VOP:                             // vop not coded
1334                    // when low_delay==0, N_VOP's should interpolate between the past and future frames
1335                    image_copy(&dec->cur, &dec->refn[0], dec->edged_width, dec->height);
1336                  break;                  break;
1337    
1338          default:          default:
1339                  return XVID_ERR_FAIL;                  return XVID_ERR_FAIL;
1340          }          }
1341    
1342    #ifdef BFRAMES_DEC_DEBUG
1343            if (frame->length != BitstreamPos(&bs) / 8){
1344                    DEBUG2("InLen/UseLen",frame->length, BitstreamPos(&bs) / 8);
1345            }
1346    #endif
1347          frame->length = BitstreamPos(&bs) / 8;          frame->length = BitstreamPos(&bs) / 8;
1348    
1349  #ifdef BFRAMES  
1350    #ifdef BFRAMES_DEC
1351          // test if no B_VOP          // test if no B_VOP
1352          if (dec->low_delay) {          if (dec->low_delay) {
1353  #endif  #endif
1354                  image_output(&dec->cur, dec->width, dec->height, dec->edged_width,                  image_output(&dec->cur, dec->width, dec->height, dec->edged_width,
1355                                           frame->image, frame->stride, frame->colorspace);                                           frame->image, frame->stride, frame->colorspace);
1356  #ifdef BFRAMES  
1357    #ifdef BFRAMES_DEC
1358          } else {          } else {
1359                  if (dec->frames >= 1) {                  if (dec->frames >= 0) {
1360                          start_timer();                          start_timer();
1361                          if ((vop_type == I_VOP || vop_type == P_VOP)) {                          if ((vop_type == I_VOP || vop_type == P_VOP)) {
1362                                  image_output(&dec->refn[0], dec->width, dec->height,                                  image_output(&dec->refn[0], dec->width, dec->height,
# Line 1297  Line 1376 
1376                  image_swap(&dec->refn[0], &dec->refn[1]);                  image_swap(&dec->refn[0], &dec->refn[1]);
1377                  image_swap(&dec->cur, &dec->refn[0]);                  image_swap(&dec->cur, &dec->refn[0]);
1378                  // swap MACROBLOCK                  // swap MACROBLOCK
1379                  if (dec->low_delay && vop_type == P_VOP)                  if (!dec->low_delay && vop_type == P_VOP)
1380                          mb_swap(&dec->mbs, &dec->last_mbs);                          mb_swap(&dec->mbs, &dec->last_mbs);
1381          }          }
1382    

Legend:
Removed from v.1.20  
changed lines
  Added in v.1.28

No admin address has been configured
ViewVC Help
Powered by ViewVC 1.0.4