mediacodech264enc.c 17.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
mediastreamer2 mediacodech264enc.c
Copyright (C) 2015 Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19 20 21 22 23
*/

#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/rfc3984.h"
#include "mediastreamer2/msvideo.h"
#include "mediastreamer2/msticker.h"
24
#include "mediastreamer2/mscodecutils.h"
25
#include "mediastreamer2/msjava.h"
26
#include "android_mediacodec.h"
27
#include "h264utils.h"
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

#include <jni.h>
#include <media/NdkMediaCodec.h>

#include "ortp/b64.h"

#define TIMEOUT_US 0

#define MS_MEDIACODECH264_CONF(required_bitrate, bitrate_limit, resolution, fps, ncpus) \
	{ required_bitrate, bitrate_limit, { MS_VIDEO_SIZE_ ## resolution ## _W, MS_VIDEO_SIZE_ ## resolution ## _H }, fps, ncpus, NULL }

static const MSVideoConfiguration mediaCodecH264_conf_list[] = {
	MS_MEDIACODECH264_CONF(2048000, 	1000000,            UXGA, 25,  2),
	MS_MEDIACODECH264_CONF(1024000, 	5000000, 	  SXGA_MINUS, 25,  2),
	MS_MEDIACODECH264_CONF(1024000,  	5000000,   			720P, 30,  2),
43 44
	MS_MEDIACODECH264_CONF(750000, 	2048000,             XGA, 25,  2),
	MS_MEDIACODECH264_CONF(500000,  	1024000,            SVGA, 15,  2),
Mickaël Turnel's avatar
Mickaël Turnel committed
45
	MS_MEDIACODECH264_CONF(600000,      3000000,             VGA, 30,  2),
46
	MS_MEDIACODECH264_CONF(400000,  	 800000,             VGA, 15,  2),
47 48 49
	MS_MEDIACODECH264_CONF(128000,  	 512000,             CIF, 15,  1),
	MS_MEDIACODECH264_CONF(100000,  	 380000,            QVGA, 15,  1),
	MS_MEDIACODECH264_CONF(0,      170000,            QCIF, 10,  1),
50 51
};

52
typedef struct _EncData {
53 54 55 56 57 58 59
	AMediaCodec *codec;
	const MSVideoConfiguration *vconf_list;
	MSVideoConfiguration vconf;
	Rfc3984Context *packer;
	uint64_t framenum;
	int mode;
	MSVideoStarter starter;
60
	MSIFrameRequestsLimiterCtx iframe_limiter;
61
	mblk_t *sps, *pps; /*lastly generated SPS, PPS, in case we need to repeat them*/
62
	bool_t avpf_enabled;
63 64 65 66 67
	bool_t isPlanar;
	bool_t use_media_image;
	bool_t first_buffer_queued;
	bool_t codec_started;
	bool_t codec_lost;
68
} EncData;
69

70 71
static void set_mblk(mblk_t **packet, mblk_t *newone) {
	if (newone) {
72 73
		newone = copyb(newone);
	}
74 75

	if (*packet) {
76 77
		freemsg(*packet);
	}
78

79 80 81
	*packet = newone;
}

82 83 84 85 86 87 88 89 90 91 92
static int alloc_encoder(EncData *d){
	if (!d->codec){
		d->codec = AMediaCodec_createEncoderByType("video/avc");
		if (!d->codec) {
			ms_error("MSMediaCodecH264Enc: could not create MediaCodec");
			return AMEDIA_ERROR_UNKNOWN;
		}
	}
	return 0;
}

93
static void enc_init(MSFilter *f) {
94
	MSVideoSize vsize;
95 96 97
	EncData *d = ms_new0(EncData, 1);

	d->packer = NULL;
98
	d->isPlanar = TRUE;
99 100 101 102 103
	d->mode = 1;
	d->avpf_enabled = FALSE;

	d->framenum = 0;
	d->vconf_list = mediaCodecH264_conf_list;
104
	MS_VIDEO_SIZE_ASSIGN(vsize, CIF);
105
	d->vconf = ms_video_find_best_configuration_for_size(d->vconf_list, vsize, ms_factory_get_cpu_count(f->factory));
106
	d->codec_started = FALSE;
107 108 109 110
	/*we shall allocate the MediaCodec encoder the sooner as possible and before the decoder, because
	 * on some phones hardware encoder and decoders can't be allocated at the same time.
	 * */
	alloc_encoder(d);
111
	f->data = d;
112 113
}

114 115 116 117 118 119 120 121 122 123
static media_status_t try_color_format(EncData *d, AMediaFormat *format, unsigned value){
	media_status_t status;
	AMediaFormat_setInt32(format, "color-format", value);
	status = AMediaCodec_configure(d->codec, format, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
	if (status != 0){
		ms_message("AMediaCodec_configure() failed with error %i for format %u", (int)status, value);
	}
	return status;
}

124
static int enc_configure(EncData *d){
125
	media_status_t status = AMEDIA_ERROR_UNSUPPORTED;
126 127
	AMediaFormat *format;
	
128 129 130
	status = alloc_encoder(d);
	if (status != 0) return status;
	
131 132
	d->codec_lost = FALSE;
	d->codec_started = FALSE;
133
	format = AMediaFormat_new();
134 135 136 137
	AMediaFormat_setString(format, "mime", "video/avc");
	AMediaFormat_setInt32(format, "width", d->vconf.vsize.width);
	AMediaFormat_setInt32(format, "height", d->vconf.vsize.height);
	AMediaFormat_setInt32(format, "i-frame-interval", 20);
138
	AMediaFormat_setInt32(format, "bitrate", (d->vconf.required_bitrate * 9)/10); /*take a margin*/
139
	AMediaFormat_setInt32(format, "frame-rate", d->vconf.fps);
140
	AMediaFormat_setInt32(format, "bitrate-mode", 1);
Erwan Croze's avatar
Erwan Croze committed
141
	AMediaFormat_setInt32(format, "profile", 1); // AVCProfileBaseline
142
	AMediaFormat_setInt32(format, "level", 1024); // AVCLevel32
143

144 145 146
	if ((d->use_media_image = AMediaImage_isAvailable())) {
		ms_message("MSMediaCodecH264Enc: AMediaImage is available.");
		status = try_color_format(d, format, 0x7f420888);/*the new "flexible YUV", appeared in API23*/
147
	} else {
148 149 150 151
		status = try_color_format(d, format, 21);/*the semi-planar YUV*/
		if (status != 0){
			status = try_color_format(d, format, 19); /*basic YUV420P*/
			if (status == 0) d->isPlanar = TRUE;
152
		}
153
	}
154 155

	if (status != 0) {
156
		ms_error("MSMediaCodecH264Enc: Could not configure encoder.");
157 158
	} else {
		int32_t color_format;
159 160

		if (!AMediaFormat_getInt32(format, "color-format", &color_format)) {
161 162
			color_format = -1;
		}
Erwan Croze's avatar
Erwan Croze committed
163
		ms_message("MSMediaCodecH264Enc: encoder successfully configured. size=%ix%i, color-format=%d",
164
				    d->vconf.vsize.width,  d->vconf.vsize.height, color_format);
165
		if ((status = AMediaCodec_start(d->codec)) != AMEDIA_OK) {
166
			ms_error("MSMediaCodecH264Enc: Could not start encoder.");
167 168
		} else {
			ms_message("MSMediaCodecH264Enc: encoder successfully started");
169
			d->codec_started = TRUE;
170
		}
171
	}
172

Simon Morlat's avatar
Simon Morlat committed
173
	AMediaFormat_delete(format);
174
	d->first_buffer_queued = FALSE;
175 176 177 178 179 180 181 182
	return status;
}

static void enc_preprocess(MSFilter *f) {
	EncData *d = (EncData *)f->data;

	enc_configure(d);
	
183
	d->packer = rfc3984_new_with_factory(f->factory);
184 185 186
	rfc3984_set_mode(d->packer, d->mode);
	rfc3984_enable_stap_a(d->packer, FALSE);
	ms_video_starter_init(&d->starter);
187
	ms_iframe_requests_limiter_init(&d->iframe_limiter, 1000);	
188 189 190
}

static void enc_postprocess(MSFilter *f) {
191
	EncData *d = (EncData *)f->data;
Erwan Croze's avatar
Erwan Croze committed
192

193 194 195 196
	if (d->packer){
		rfc3984_destroy(d->packer);
		d->packer = NULL;
	}
197 198

	if (d->codec) {
199
		if (d->codec_started){
200
			AMediaCodec_flush(d->codec);
201
			AMediaCodec_stop(d->codec);
202 203 204
			//It is preferable to reset the encoder, otherwise it may not accept a new configuration while returning in preprocess().
			//This was observed at least on Moto G2, with qualcomm encoder.
			AMediaCodec_reset(d->codec); 
205 206
			d->codec_started = FALSE;
		}
207
	}
208

209 210
	set_mblk(&d->sps, NULL);
	set_mblk(&d->pps, NULL);
211 212
}

213 214
static void enc_uninit(MSFilter *f) {
	EncData *d = (EncData *)f->data;
215 216 217 218
	if (d->codec){
		AMediaCodec_delete(d->codec);
		d->codec = NULL;
	}
219 220 221
	ms_free(d);
}

222 223
static void enc_process(MSFilter *f) {
	EncData *d = (EncData *)f->data;
224 225 226
	MSPicture pic = {0};
	mblk_t *im;
	long long int ts = f->ticker->time * 90LL;
227 228 229 230
	ssize_t ibufidx, obufidx;
	AMediaCodecBufferInfo info;
	size_t bufsize;
	bool_t have_seen_sps_pps = FALSE;
231 232 233 234 235 236 237 238 239
	
	if (d->codec_lost && (f->ticker->time % 5000 == 0)){
		if (enc_configure(d) != 0){
			ms_error("MSMediaCodecH264Enc: AMediaCodec_reset() was not sufficient, will recreate the encoder in a moment...");
			AMediaCodec_delete(d->codec);
			d->codec = NULL;
			d->codec_lost = TRUE;
		}
	}
240

241
	if (!d->codec_started || d->codec_lost) {
242 243 244 245
		ms_queue_flush(f->inputs[0]);
		return;
	}

246 247
	/*First queue input image*/
	if ((im = ms_queue_peek_last(f->inputs[0])) != NULL) {
248
		if (ms_yuv_buf_init_from_mblk(&pic, im) == 0) {
249
			uint8_t *buf;
Erwan Croze's avatar
Erwan Croze committed
250

251 252
			if (ms_iframe_requests_limiter_iframe_requested(&d->iframe_limiter, f->ticker->time) ||
			        (d->avpf_enabled == FALSE && ms_video_starter_need_i_frame(&d->starter, f->ticker->time))) {
253
				AMediaFormat *afmt = AMediaFormat_new();
254
				/*Force a key-frame*/
255 256 257
				AMediaFormat_setInt32(afmt, "request-sync", 0);
				AMediaCodec_setParams(d->codec, afmt);
				AMediaFormat_delete(afmt);
258
				ms_error("MSMediaCodecH264Enc: I-frame requested to MediaCodec");
259
				ms_iframe_requests_limiter_notify_iframe_sent(&d->iframe_limiter, f->ticker->time);
260
			}
261 262

			ibufidx = AMediaCodec_dequeueInputBuffer(d->codec, TIMEOUT_US);
263

264
			if (ibufidx >= 0) {
265 266 267 268 269 270 271 272 273 274 275 276 277 278
				buf = AMediaCodec_getInputBuffer(d->codec, ibufidx, &bufsize);
				if (buf){
					if (d->use_media_image) {
						AMediaImage image;

						if (AMediaCodec_getInputImage(d->codec, ibufidx, &image)) {
							if (image.format == 35 /* YUV_420_888 */) {
								MSRect src_roi = {0, 0, pic.w, pic.h};
								int src_pix_strides[4] = {1, 1, 1, 1};
								ms_yuv_buf_copy_with_pix_strides(pic.planes, pic.strides, src_pix_strides, src_roi, image.buffers, image.row_strides, image.pixel_strides, image.crop_rect);
								bufsize = image.row_strides[0] * image.height * 3 / 2;
							} else {
								ms_error("%s: encoder requires non YUV420 format", f->desc->name);
							}
279
							AMediaImage_close(&image);
280
						}
281 282
					} else {
						if (d->isPlanar) {
283 284 285 286
							int ysize = pic.w * pic.h;
							int usize = ysize / 4;
							memcpy(buf, pic.planes[0], ysize);
							memcpy(buf + ysize, pic.planes[1], usize);
287
							memcpy(buf + ysize + usize, pic.planes[2], usize);
288 289
						} else {
							int i;
290
							size_t size = (size_t) pic.w * pic.h;
291
							uint8_t *dst = pic.planes[0];
292
							memcpy(buf, dst, size);
293

294 295 296
							for (i = 0; i < pic.w / 2 * pic.h / 2; i++) {
								buf[size + 2 * i] = pic.planes[1][i];
								buf[size + 2 * i + 1] = pic.planes[2][i];
297
							}
298 299
						}
					}
300 301 302 303 304 305 306
					AMediaCodec_queueInputBuffer(d->codec, ibufidx, 0, bufsize, f->ticker->time * 1000, 0);
					if (!d->first_buffer_queued){
						d->first_buffer_queued = TRUE;
						ms_message("MSMediaCodecH264Enc: first frame to encode queued (size: %ix%i)", pic.w, pic.h);
					}
				}else{
					ms_error("MSMediaCodecH264Enc: obtained InputBuffer, but no address.");
307
				}
308
			} else if (ibufidx == AMEDIA_ERROR_UNKNOWN) {
309
				ms_error("MSMediaCodecH264Enc: AMediaCodec_dequeueInputBuffer() had an exception");
310
			}
311 312 313
		}
	}
	ms_queue_flush(f->inputs[0]);
314

315 316
	if (!d->first_buffer_queued)
		return;
Erwan Croze's avatar
Erwan Croze committed
317

318 319 320
	/*Second, dequeue possibly pending encoded frames*/
	while ((obufidx = AMediaCodec_dequeueOutputBuffer(d->codec, &info, TIMEOUT_US)) >= 0) {
		uint8_t *buf = AMediaCodec_getOutputBuffer(d->codec, obufidx, &bufsize);
Erwan Croze's avatar
Erwan Croze committed
321

322 323 324
		if (buf) {
			mblk_t *m;
			MSQueue nalus;
Erwan Croze's avatar
Erwan Croze committed
325

326
			ms_queue_init(&nalus);
327
			ms_h264_bitstream_to_nalus(buf + info.offset, info.size, &nalus);
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

			if (!ms_queue_empty(&nalus)) {
				m = ms_queue_peek_first(&nalus);

				switch (ms_h264_nalu_get_type(m)) {
					case MSH264NaluTypeIDR:
						if (!have_seen_sps_pps) {
							ms_message("MSMediaCodecH264Enc: seeing IDR without prior SPS/PPS, so manually adding them.");

							if (d->sps && d->pps) {
								ms_queue_insert(&nalus, m, copyb(d->sps));
								ms_queue_insert(&nalus, m, copyb(d->pps));
							} else {
								ms_error("MSMediaCodecH264Enc: SPS or PPS are not known !");
							}
343
						}
344
						break;
345

346 347 348 349 350
					case MSH264NaluTypeSPS:
						ms_message("MSMediaCodecH264Enc: seeing SPS");
						have_seen_sps_pps = TRUE;
						set_mblk(&d->sps, m);
						m = ms_queue_next(&nalus, m);
351

352 353 354
						if (!ms_queue_end(&nalus, m) && ms_h264_nalu_get_type(m) == MSH264NaluTypePPS) {
							ms_message("MSMediaCodecH264Enc: seeing PPS");
							set_mblk(&d->pps, m);
355
						}
356
						break;
357

358 359 360
					case MSH264NaluTypePPS:
						ms_warning("MSMediaCodecH264Enc: unexpecting starting PPS");
						break;
361
					default:
Erwan Croze's avatar
Erwan Croze committed
362
						break;
363
				}
364

365
				rfc3984_pack(d->packer, &nalus, f->outputs[0], ts);
366

367 368 369 370 371 372
				if (d->framenum == 0) {
					ms_video_starter_first_frame(&d->starter, f->ticker->time);
				}
				d->framenum++;
			}else{
				ms_error("MSMediaCodecH264Enc: no NALUs in buffer obtained from MediaCodec");
373
			}
374 375
		}else{
			ms_error("MSMediaCodecH264Enc: AMediaCodec_getOutputBuffer() returned NULL");
376
		}
377
		AMediaCodec_releaseOutputBuffer(d->codec, obufidx, FALSE);
378 379
	}

380 381 382 383
	if (obufidx == AMEDIA_ERROR_UNKNOWN) {
		ms_error("MSMediaCodecH264Enc: AMediaCodec_dequeueOutputBuffer() had an exception, MediaCodec is lost");
		/* TODO: the MediaCodec is irrecoverabely crazy at this point. We should probably use AMediacCodec_reset() but this method is not wrapped yet.*/
		d->codec_lost = TRUE;
384
		AMediaCodec_reset(d->codec);
385
	}
386 387
}

388 389 390
static int enc_get_br(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	*(int *)arg = d->vconf.required_bitrate;
391 392 393 394 395 396
	return 0;
}

static int enc_set_configuration(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	const MSVideoConfiguration *vconf = (const MSVideoConfiguration *)arg;
397

398 399 400 401 402
	if (vconf != &d->vconf) memcpy(&d->vconf, vconf, sizeof(MSVideoConfiguration));

	if (d->vconf.required_bitrate > d->vconf.bitrate_limit)
		d->vconf.required_bitrate = d->vconf.bitrate_limit;

403 404 405 406
	ms_message("Video configuration set: bitrate=%d bits/s, fps=%f, vsize=%dx%d", d->vconf.required_bitrate, d->vconf.fps, d->vconf.vsize.width, d->vconf.vsize.height);
	
	if (d->codec_started){
		AMediaFormat *afmt = AMediaFormat_new();
407
		/*Update the output bitrate*/
408 409 410 411 412 413
		ms_filter_lock(f);
		AMediaFormat_setInt32(afmt, "video-bitrate", d->vconf.required_bitrate);
		AMediaCodec_setParams(d->codec, afmt);
		AMediaFormat_delete(afmt);
		ms_filter_unlock(f);
	}
414 415 416 417 418 419
	return 0;
}

static int enc_set_br(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	int br = *(int *)arg;
420

421
	if (d->codec_started) {
422 423
		/* Encoding is already ongoing, do not change video size, only bitrate. */
		d->vconf.required_bitrate = br;
424
		/* apply the new bitrate request to the running MediaCodec*/
425
		enc_set_configuration(f, &d->vconf);
426
	} else {
427
		MSVideoConfiguration best_vconf = ms_video_find_best_configuration_for_size_and_bitrate(d->vconf_list, d->vconf.vsize, ms_factory_get_cpu_count(f->factory),  br);
428 429
		enc_set_configuration(f, &best_vconf);
	}
430

431 432 433
	return 0;
}

434 435 436
static int enc_set_fps(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	d->vconf.fps = *(float *)arg;
437 438 439 440
	enc_set_configuration(f, &d->vconf);
	return 0;
}

441 442 443
static int enc_get_fps(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	*(float *)arg = d->vconf.fps;
444 445 446
	return 0;
}

447 448 449
static int enc_get_vsize(MSFilter *f, void *arg) {
	EncData *d = (EncData *)f->data;
	*(MSVideoSize *)arg = d->vconf.vsize;
450 451 452 453 454 455 456 457 458
	return 0;
}

static int enc_enable_avpf(MSFilter *f, void *data) {
	EncData *s = (EncData *)f->data;
	s->avpf_enabled = *((bool_t *)data) ? TRUE : FALSE;
	return 0;
}

459
static int enc_set_vsize(MSFilter *f, void *arg) {
460 461 462 463
	MSVideoConfiguration best_vconf;
	EncData *d = (EncData *)f->data;
	MSVideoSize *vs = (MSVideoSize *)arg;

464
	best_vconf = ms_video_find_best_configuration_for_size(d->vconf_list, *vs, ms_factory_get_cpu_count(f->factory));
465 466 467 468 469 470 471 472 473
	d->vconf.vsize = *vs;
	d->vconf.fps = best_vconf.fps;
	d->vconf.bitrate_limit = best_vconf.bitrate_limit;
	enc_set_configuration(f, &d->vconf);
	return 0;
}

static int enc_notify_pli(MSFilter *f, void *data) {
	EncData *d = (EncData *)f->data;
474 475
	ms_message("MSMediaCodecH264Enc: PLI requested");
	ms_iframe_requests_limiter_request_iframe(&d->iframe_limiter);
476 477 478
	return 0;
}

479 480
static int enc_notify_fir(MSFilter *f, void *data) {
	EncData *d = (EncData *)f->data;
481 482
	ms_message("MSMediaCodecH264Enc: FIR requested");
	ms_iframe_requests_limiter_request_iframe(&d->iframe_limiter);
483 484 485
	return 0;
}

486 487 488 489 490 491 492 493 494 495 496 497 498
static int enc_get_conf_list(MSFilter *f, void *data){
	EncData *d = (EncData *)f->data;
	*(MSVideoConfiguration **)data = (MSVideoConfiguration*)d->vconf_list;
	return 0;
}

static int enc_set_conf_list(MSFilter *f, void *data){
	EncData *d = (EncData *)f->data;
	MSVideoConfiguration *conf_list = *(MSVideoConfiguration **)data;
	d->vconf_list = conf_list != NULL ? conf_list : mediaCodecH264_conf_list;
	return 0;
}

499
static MSFilterMethod  mediacodec_h264_enc_methods[] = {
500 501 502 503 504 505
	{ MS_FILTER_SET_FPS,                       enc_set_fps                },
	{ MS_FILTER_SET_BITRATE,                   enc_set_br                 },
	{ MS_FILTER_GET_BITRATE,                   enc_get_br                 },
	{ MS_FILTER_GET_FPS,                       enc_get_fps                },
	{ MS_FILTER_GET_VIDEO_SIZE,                enc_get_vsize              },
	{ MS_VIDEO_ENCODER_NOTIFY_PLI,             enc_notify_pli             },
506
	{ MS_VIDEO_ENCODER_NOTIFY_FIR,             enc_notify_fir             },
507 508
	{ MS_FILTER_SET_VIDEO_SIZE,                enc_set_vsize              },
	{ MS_VIDEO_ENCODER_ENABLE_AVPF,            enc_enable_avpf            },
509 510 511
	{ MS_VIDEO_ENCODER_GET_CONFIGURATION_LIST, enc_get_conf_list	      },
	{ MS_VIDEO_ENCODER_SET_CONFIGURATION_LIST, enc_set_conf_list	      },
	{ MS_VIDEO_ENCODER_SET_CONFIGURATION,	   enc_set_configuration      },
512 513 514 515
	{ 0,                                       NULL                       }
};


516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
MSFilterDesc ms_mediacodec_h264_enc_desc = {
	.id = MS_MEDIACODEC_H264_ENC_ID,
	.name = "MSMediaCodecH264Enc",
	.text = "A H264 encoder based on MediaCodec API.",
	.category = MS_FILTER_ENCODER,
	.enc_fmt = "H264",
	.ninputs = 1,
	.noutputs = 1,
	.init = enc_init,
	.preprocess = enc_preprocess,
	.process = enc_process,
	.postprocess = enc_postprocess,
	.uninit = enc_uninit,
	.methods = mediacodec_h264_enc_methods,
	.flags = MS_FILTER_IS_PUMP
531
};