aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libavcodec/h264_slice.c260
-rw-r--r--libavcodec/h264dec.c67
-rw-r--r--libavcodec/h264dec.h27
-rw-r--r--tests/fate/h264.mak4
-rw-r--r--tests/ref/fate/h264-twofields-packet35
5 files changed, 210 insertions, 183 deletions
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index ad7a75fa2e..6b555ab4c5 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1849,119 +1849,12 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
return 0;
}
-/**
- * Decode a slice header.
- * This will (re)initialize the decoder and call h264_frame_start() as needed.
- *
- * @param h h264context
- *
- * @return 0 if okay, <0 if an error occurred
- */
-int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
- const H2645NAL *nal)
+/* do all the per-slice initialization needed before we can start decoding the
+ * actual MBs */
+static int h264_slice_init(H264Context *h, H264SliceContext *sl,
+ const H2645NAL *nal)
{
int i, j, ret = 0;
- int first_slice = sl == h->slice_ctx && !h->current_slice;
-
- ret = h264_slice_header_parse(h, sl, nal);
- if (ret < 0)
- return ret;
-
- // discard redundant pictures
- if (sl->redundant_pic_count > 0)
- return 0;
-
- if (sl->first_mb_addr == 0 || !h->current_slice) {
- if (h->setup_finished) {
- av_log(h->avctx, AV_LOG_ERROR, "Too many fields\n");
- return AVERROR_INVALIDDATA;
- }
- }
-
- if (sl->first_mb_addr == 0) { // FIXME better field boundary detection
- if (h->current_slice) {
- if (h->max_contexts > 1) {
- if (!h->single_decode_warning) {
- av_log(h->avctx, AV_LOG_WARNING, "Cannot decode multiple access units as slice threads\n");
- h->single_decode_warning = 1;
- }
- h->max_contexts = 1;
- return SLICE_SINGLETHREAD;
- }
-
- if (h->cur_pic_ptr && FIELD_PICTURE(h) && h->first_field) {
- ret = ff_h264_field_end(h, h->slice_ctx, 1);
- if (ret < 0)
- return ret;
- } else if (h->cur_pic_ptr && !FIELD_PICTURE(h) && !h->first_field && h->nal_unit_type == H264_NAL_IDR_SLICE) {
- av_log(h, AV_LOG_WARNING, "Broken frame packetizing\n");
- ret = ff_h264_field_end(h, h->slice_ctx, 1);
- ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 0);
- ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 1);
- h->cur_pic_ptr = NULL;
- if (ret < 0)
- return ret;
- } else
- return AVERROR_INVALIDDATA;
- }
-
- if (!h->first_field) {
- if (h->cur_pic_ptr && !h->droppable) {
- ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX,
- h->picture_structure == PICT_BOTTOM_FIELD);
- }
- h->cur_pic_ptr = NULL;
- }
- }
-
- if (!h->current_slice)
- av_assert0(sl == h->slice_ctx);
-
- if (h->current_slice == 0 && !h->first_field) {
- if (
- (h->avctx->skip_frame >= AVDISCARD_NONREF && !h->nal_ref_idc) ||
- (h->avctx->skip_frame >= AVDISCARD_BIDIR && sl->slice_type_nos == AV_PICTURE_TYPE_B) ||
- (h->avctx->skip_frame >= AVDISCARD_NONINTRA && sl->slice_type_nos != AV_PICTURE_TYPE_I) ||
- (h->avctx->skip_frame >= AVDISCARD_NONKEY && h->nal_unit_type != H264_NAL_IDR_SLICE && h->sei.recovery_point.recovery_frame_cnt < 0) ||
- h->avctx->skip_frame >= AVDISCARD_ALL) {
- return SLICE_SKIPED;
- }
- }
-
- if (!first_slice) {
- const PPS *pps = (const PPS*)h->ps.pps_list[sl->pps_id]->data;
-
- if (h->ps.pps->sps_id != pps->sps_id ||
- h->ps.pps->transform_8x8_mode != pps->transform_8x8_mode /*||
- (h->setup_finished && h->ps.pps != pps)*/) {
- av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n");
- return AVERROR_INVALIDDATA;
- }
- if (h->ps.sps != (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data) {
- av_log(h->avctx, AV_LOG_ERROR,
- "SPS changed in the middle of the frame\n");
- return AVERROR_INVALIDDATA;
- }
- }
-
- if (h->current_slice == 0) {
- ret = h264_field_start(h, sl, nal, first_slice);
- if (ret < 0)
- return ret;
- } else {
- if (h->picture_structure != sl->picture_structure ||
- h->droppable != (nal->ref_idc == 0)) {
- av_log(h->avctx, AV_LOG_ERROR,
- "Changing field mode (%d -> %d) between slices is not allowed\n",
- h->picture_structure, sl->picture_structure);
- return AVERROR_INVALIDDATA;
- } else if (!h->cur_pic_ptr) {
- av_log(h->avctx, AV_LOG_ERROR,
- "unset cur_pic_ptr on slice %d\n",
- h->current_slice + 1);
- return AVERROR_INVALIDDATA;
- }
- }
if (h->picture_idr && nal->type != H264_NAL_IDR_SLICE) {
av_log(h->avctx, AV_LOG_ERROR, "Invalid mix of IDR and non-IDR slices\n");
@@ -2009,7 +1902,7 @@ int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
nal->ref_idc == 0))
sl->deblocking_filter = 0;
- if (sl->deblocking_filter == 1 && h->max_contexts > 1) {
+ if (sl->deblocking_filter == 1 && h->nb_slice_ctx > 1) {
if (h->avctx->flags2 & AV_CODEC_FLAG2_FAST) {
/* Cheat slightly for speed:
* Do not bother to deblock across slices. */
@@ -2093,6 +1986,129 @@ int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
return 0;
}
+int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
+{
+ H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
+ int first_slice = sl == h->slice_ctx && !h->current_slice;
+ int ret;
+
+ sl->gb = nal->gb;
+
+ ret = h264_slice_header_parse(h, sl, nal);
+ if (ret < 0)
+ return ret;
+
+ // discard redundant pictures
+ if (sl->redundant_pic_count > 0)
+ return 0;
+
+ if (sl->first_mb_addr == 0 || !h->current_slice) {
+ if (h->setup_finished) {
+ av_log(h->avctx, AV_LOG_ERROR, "Too many fields\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ if (sl->first_mb_addr == 0) { // FIXME better field boundary detection
+ if (h->current_slice) {
+ // this slice starts a new field
+ // first decode any pending queued slices
+ if (h->nb_slice_ctx_queued) {
+ H264SliceContext tmp_ctx;
+
+ ret = ff_h264_execute_decode_slices(h);
+ if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+ return ret;
+
+ memcpy(&tmp_ctx, h->slice_ctx, sizeof(tmp_ctx));
+ memcpy(h->slice_ctx, sl, sizeof(tmp_ctx));
+ memcpy(sl, &tmp_ctx, sizeof(tmp_ctx));
+ sl = h->slice_ctx;
+ }
+
+ if (h->cur_pic_ptr && FIELD_PICTURE(h) && h->first_field) {
+ ret = ff_h264_field_end(h, h->slice_ctx, 1);
+ if (ret < 0)
+ return ret;
+ } else if (h->cur_pic_ptr && !FIELD_PICTURE(h) && !h->first_field && h->nal_unit_type == H264_NAL_IDR_SLICE) {
+ av_log(h, AV_LOG_WARNING, "Broken frame packetizing\n");
+ ret = ff_h264_field_end(h, h->slice_ctx, 1);
+ ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 0);
+ ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 1);
+ h->cur_pic_ptr = NULL;
+ if (ret < 0)
+ return ret;
+ } else
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!h->first_field) {
+ if (h->cur_pic_ptr && !h->droppable) {
+ ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX,
+ h->picture_structure == PICT_BOTTOM_FIELD);
+ }
+ h->cur_pic_ptr = NULL;
+ }
+ }
+
+ if (!h->current_slice)
+ av_assert0(sl == h->slice_ctx);
+
+ if (h->current_slice == 0 && !h->first_field) {
+ if (
+ (h->avctx->skip_frame >= AVDISCARD_NONREF && !h->nal_ref_idc) ||
+ (h->avctx->skip_frame >= AVDISCARD_BIDIR && sl->slice_type_nos == AV_PICTURE_TYPE_B) ||
+ (h->avctx->skip_frame >= AVDISCARD_NONINTRA && sl->slice_type_nos != AV_PICTURE_TYPE_I) ||
+ (h->avctx->skip_frame >= AVDISCARD_NONKEY && h->nal_unit_type != H264_NAL_IDR_SLICE && h->sei.recovery_point.recovery_frame_cnt < 0) ||
+ h->avctx->skip_frame >= AVDISCARD_ALL) {
+ return 0;
+ }
+ }
+
+ if (!first_slice) {
+ const PPS *pps = (const PPS*)h->ps.pps_list[sl->pps_id]->data;
+
+ if (h->ps.pps->sps_id != pps->sps_id ||
+ h->ps.pps->transform_8x8_mode != pps->transform_8x8_mode /*||
+ (h->setup_finished && h->ps.pps != pps)*/) {
+ av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (h->ps.sps != (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data) {
+ av_log(h->avctx, AV_LOG_ERROR,
+ "SPS changed in the middle of the frame\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ if (h->current_slice == 0) {
+ ret = h264_field_start(h, sl, nal, first_slice);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (h->picture_structure != sl->picture_structure ||
+ h->droppable != (nal->ref_idc == 0)) {
+ av_log(h->avctx, AV_LOG_ERROR,
+ "Changing field mode (%d -> %d) between slices is not allowed\n",
+ h->picture_structure, sl->picture_structure);
+ return AVERROR_INVALIDDATA;
+ } else if (!h->cur_pic_ptr) {
+ av_log(h->avctx, AV_LOG_ERROR,
+ "unset cur_pic_ptr on slice %d\n",
+ h->current_slice + 1);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ ret = h264_slice_init(h, sl, nal);
+ if (ret < 0)
+ return ret;
+
+ h->nb_slice_ctx_queued++;
+
+ return 0;
+}
+
int ff_h264_get_slice_type(const H264SliceContext *sl)
{
switch (sl->slice_type) {
@@ -2678,33 +2694,35 @@ finish:
* Call decode_slice() for each context.
*
* @param h h264 master context
- * @param context_count number of contexts to execute
*/
-int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count)
+int ff_h264_execute_decode_slices(H264Context *h)
{
AVCodecContext *const avctx = h->avctx;
H264SliceContext *sl;
+ int context_count = h->nb_slice_ctx_queued;
+ int ret = 0;
int i, j;
- av_assert0(context_count && h->slice_ctx[context_count - 1].mb_y < h->mb_height);
-
h->slice_ctx[0].next_slice_idx = INT_MAX;
- if (h->avctx->hwaccel
+ if (h->avctx->hwaccel || context_count < 1
#if FF_API_CAP_VDPAU
|| h->avctx->codec->capabilities & AV_CODEC_CAP_HWACCEL_VDPAU
#endif
)
return 0;
+
+ av_assert0(context_count && h->slice_ctx[context_count - 1].mb_y < h->mb_height);
+
if (context_count == 1) {
- int ret;
h->slice_ctx[0].next_slice_idx = h->mb_width * h->mb_height;
h->postpone_filter = 0;
ret = decode_slice(avctx, &h->slice_ctx[0]);
h->mb_y = h->slice_ctx[0].mb_y;
- return ret;
+ if (ret < 0)
+ goto finish;
} else {
av_assert0(context_count > 0);
for (i = 0; i < context_count; i++) {
@@ -2759,5 +2777,7 @@ int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count)
}
}
- return 0;
+finish:
+ h->nb_slice_ctx_queued = 0;
+ return ret;
}
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 41e6ce458c..31db1db076 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -602,7 +602,6 @@ static void debug_green_metadata(const H264SEIGreenMetaData *gm, void *logctx)
static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
{
AVCodecContext *const avctx = h->avctx;
- unsigned context_count = 0;
int nals_needed = 0; ///< number of NALs that need decoding before the next frame thread starts
int idr_cleared=0;
int i, ret = 0;
@@ -610,7 +609,6 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
h->has_slice = 0;
h->nal_unit_type= 0;
- h->max_contexts = h->nb_slice_ctx;
if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS)) {
h->current_slice = 0;
if (!h->first_field)
@@ -640,14 +638,12 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
for (i = 0; i < h->pkt.nb_nals; i++) {
H2645NAL *nal = &h->pkt.nals[i];
- H264SliceContext *sl = &h->slice_ctx[context_count];
- int err;
+ int max_slice_ctx, err;
if (avctx->skip_frame >= AVDISCARD_NONREF &&
nal->ref_idc == 0 && nal->type != H264_NAL_SEI)
continue;
-again:
// FIXME these should stop being context-global variables
h->nal_ref_idc = nal->ref_idc;
h->nal_unit_type = nal->type;
@@ -672,13 +668,9 @@ again:
idr_cleared = 1;
h->has_recovery_point = 1;
case H264_NAL_SLICE:
- sl->gb = nal->gb;
h->has_slice = 1;
- if ((err = ff_h264_decode_slice_header(h, sl, nal)))
- break;
-
- if (sl->redundant_pic_count > 0)
+ if ((err = ff_h264_queue_decode_slice(h, nal)))
break;
if (h->current_slice == 1) {
@@ -698,14 +690,14 @@ again:
#endif
}
- if (avctx->hwaccel) {
- ret = avctx->hwaccel->decode_slice(avctx,
- nal->raw_data,
- nal->raw_size);
- if (ret < 0)
- goto end;
+ max_slice_ctx = avctx->hwaccel ? 1 : h->nb_slice_ctx;
+ if (h->nb_slice_ctx_queued == max_slice_ctx) {
+ if (h->avctx->hwaccel) {
+ ret = avctx->hwaccel->decode_slice(avctx, nal->raw_data, nal->raw_size);
+ h->nb_slice_ctx_queued = 0;
+ } else
#if FF_API_CAP_VDPAU
- } else if (CONFIG_H264_VDPAU_DECODER &&
+ if (CONFIG_H264_VDPAU_DECODER &&
h->avctx->codec->capabilities & AV_CODEC_CAP_HWACCEL_VDPAU) {
ff_vdpau_add_data_chunk(h->cur_pic_ptr->f->data[0],
start_code,
@@ -713,9 +705,13 @@ again:
ff_vdpau_add_data_chunk(h->cur_pic_ptr->f->data[0],
nal->raw_data,
nal->raw_size);
-#endif
+ ret = 0;
} else
- context_count++;
+#endif
+ ret = ff_h264_execute_decode_slices(h);
+ if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+ goto end;
+ }
break;
case H264_NAL_DPA:
case H264_NAL_DPB:
@@ -765,34 +761,14 @@ FF_ENABLE_DEPRECATION_WARNINGS
nal->type, nal->size_bits);
}
- if (context_count == h->max_contexts) {
- ret = ff_h264_execute_decode_slices(h, context_count);
- if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
- goto end;
- context_count = 0;
- }
-
- if (err < 0 || err == SLICE_SKIPED) {
- if (err < 0)
- av_log(h->avctx, AV_LOG_ERROR, "decode_slice_header error\n");
- sl->ref_count[0] = sl->ref_count[1] = sl->list_count = 0;
- } else if (err == SLICE_SINGLETHREAD) {
- if (context_count > 0) {
- ret = ff_h264_execute_decode_slices(h, context_count);
- if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
- goto end;
- context_count = 0;
- }
- /* Slice could not be decoded in parallel mode, restart. */
- sl = &h->slice_ctx[0];
- goto again;
+ if (err < 0) {
+ av_log(h->avctx, AV_LOG_ERROR, "decode_slice_header error\n");
}
}
- if (context_count) {
- ret = ff_h264_execute_decode_slices(h, context_count);
- if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
- goto end;
- }
+
+ ret = ff_h264_execute_decode_slices(h);
+ if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+ goto end;
ret = 0;
end:
@@ -1013,6 +989,7 @@ static int h264_decode_frame(AVCodecContext *avctx, void *data,
h->flags = avctx->flags;
h->setup_finished = 0;
+ h->nb_slice_ctx_queued = 0;
if (h->backup_width != -1) {
avctx->width = h->backup_width;
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index fa5c98ee90..5f868b76cb 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -353,6 +353,7 @@ typedef struct H264Context {
H264SliceContext *slice_ctx;
int nb_slice_ctx;
+ int nb_slice_ctx_queued;
H2645Packet pkt;
@@ -491,20 +492,6 @@ typedef struct H264Context {
*/
int current_slice;
- /**
- * Max number of threads / contexts.
- * This is equal to AVCodecContext.thread_count unless
- * multithreaded decoding is impossible, in which case it is
- * reduced to 1.
- */
- int max_contexts;
-
- /**
- * 1 if the single thread fallback warning has already been
- * displayed, 0 otherwise.
- */
- int single_decode_warning;
-
/** @} */
/**
@@ -848,10 +835,14 @@ void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, int y,
int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
const H2645NAL *nal);
-#define SLICE_SINGLETHREAD 1
-#define SLICE_SKIPED 2
-
-int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count);
+/**
+ * Submit a slice for decoding.
+ *
+ * Parse the slice header, starting a new field/frame if necessary. If any
+ * slices are queued for the previous field, they are decoded.
+ */
+int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal);
+int ff_h264_execute_decode_slices(H264Context *h);
int ff_h264_update_thread_context(AVCodecContext *dst,
const AVCodecContext *src);
diff --git a/tests/fate/h264.mak b/tests/fate/h264.mak
index 1f6e5f3947..52b6a98785 100644
--- a/tests/fate/h264.mak
+++ b/tests/fate/h264.mak
@@ -215,6 +215,9 @@ FATE_H264-$(call DEMDEC, MOV, H264) += fate-h264-mixed-nal-coding
# this sample has invalid extradata that is not escaped
FATE_H264-$(call DEMDEC, MOV, H264) += fate-h264-unescaped-extradata
+# this sample contains field-coded frames, with both fields in a single packet
+FATE_H264-$(call DEMDEC, MOV, H264) += fate-h264-twofields-packet
+
FATE_H264-$(call ALLYES, MOV_DEMUXER H264_MP4TOANNEXB_BSF H264_MUXER) += fate-h264-bsf-mp4toannexb
FATE_H264-$(call DEMDEC, MATROSKA, H264) += fate-h264-direct-bff
FATE_H264-$(call DEMDEC, FLV, H264) += fate-h264-brokensps-2580
@@ -431,6 +434,7 @@ fate-h264-intra-refresh-recovery: CMD = framecrc -i $(TARGET_SAM
fate-h264-invalid-ref-mod: CMD = framecrc -i $(TARGET_SAMPLES)/h264/h264refframeregression.mp4 -an -frames 10 -pix_fmt yuv420p10le
fate-h264-lossless: CMD = framecrc -i $(TARGET_SAMPLES)/h264/lossless.h264
fate-h264-mixed-nal-coding: CMD = framecrc -i $(TARGET_SAMPLES)/h264/mixed-nal-coding.mp4
+fate-h264-twofields-packet: CMD = framecrc -i $(TARGET_SAMPLES)/h264/twofields_packet.mp4 -an -frames 30
fate-h264-unescaped-extradata: CMD = framecrc -i $(TARGET_SAMPLES)/h264/unescaped_extradata.mp4 -an -frames 10
fate-h264-3386: CMD = framecrc -i $(TARGET_SAMPLES)/h264/bbc2.sample.h264
fate-h264-missing-frame: CMD = framecrc -i $(TARGET_SAMPLES)/h264/nondeterministic_cut.h264
diff --git a/tests/ref/fate/h264-twofields-packet b/tests/ref/fate/h264-twofields-packet
new file mode 100644
index 0000000000..4cff0a15bd
--- /dev/null
+++ b/tests/ref/fate/h264-twofields-packet
@@ -0,0 +1,35 @@
+#tb 0: 1001/30000
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 1920x1080
+#sar 0: 1/1
+0, 0, 0, 1, 3110400, 0x40d65f69
+0, 1, 1, 1, 3110400, 0xdcbc50bf
+0, 2, 2, 1, 3110400, 0x73a2276a
+0, 3, 3, 1, 3110400, 0x84a2b3c6
+0, 4, 4, 1, 3110400, 0x7cf3b570
+0, 5, 5, 1, 3110400, 0xa2d1e03a
+0, 6, 6, 1, 3110400, 0x03220fb1
+0, 7, 7, 1, 3110400, 0x89cd526a
+0, 8, 8, 1, 3110400, 0xbb4b7531
+0, 9, 9, 1, 3110400, 0x0a69f053
+0, 10, 10, 1, 3110400, 0x0187994b
+0, 11, 11, 1, 3110400, 0x26ed49fa
+0, 12, 12, 1, 3110400, 0xbe8966d4
+0, 13, 13, 1, 3110400, 0x248d203c
+0, 14, 14, 1, 3110400, 0x3139c754
+0, 15, 15, 1, 3110400, 0xf22380c4
+0, 16, 16, 1, 3110400, 0x3e00dcc1
+0, 17, 17, 1, 3110400, 0x8cbe2483
+0, 18, 18, 1, 3110400, 0x6951cd63
+0, 19, 19, 1, 3110400, 0x36aca4c5
+0, 20, 20, 1, 3110400, 0x4d4f6fbe
+0, 21, 21, 1, 3110400, 0x997247aa
+0, 22, 22, 1, 3110400, 0x0fd40e06
+0, 23, 23, 1, 3110400, 0xa10d2d67
+0, 24, 24, 1, 3110400, 0x87c481da
+0, 25, 25, 1, 3110400, 0xe3dca3cd
+0, 26, 26, 1, 3110400, 0x5f77b078
+0, 27, 27, 1, 3110400, 0xf1ddd098
+0, 28, 28, 1, 3110400, 0xedcd1754
+0, 29, 29, 1, 3110400, 0x14ac7153