static INLINE int libyuv_scale(aom_image_t *src, aom_image_t *dst, FilterModeEnum mode) { #if CONFIG_AOM_HIGHBITDEPTH if (src->fmt == AOM_IMG_FMT_I42016) { assert(dst->fmt == AOM_IMG_FMT_I42016); return I420Scale_16( (uint16_t *)src->planes[AOM_PLANE_Y], src->stride[AOM_PLANE_Y] / 2, (uint16_t *)src->planes[AOM_PLANE_U], src->stride[AOM_PLANE_U] / 2, (uint16_t *)src->planes[AOM_PLANE_V], src->stride[AOM_PLANE_V] / 2, src->d_w, src->d_h, (uint16_t *)dst->planes[AOM_PLANE_Y], dst->stride[AOM_PLANE_Y] / 2, (uint16_t *)dst->planes[AOM_PLANE_U], dst->stride[AOM_PLANE_U] / 2, (uint16_t *)dst->planes[AOM_PLANE_V], dst->stride[AOM_PLANE_V] / 2, dst->d_w, dst->d_h, mode); } #endif assert(src->fmt == AOM_IMG_FMT_I420); assert(dst->fmt == AOM_IMG_FMT_I420); return I420Scale(src->planes[AOM_PLANE_Y], src->stride[AOM_PLANE_Y], src->planes[AOM_PLANE_U], src->stride[AOM_PLANE_U], src->planes[AOM_PLANE_V], src->stride[AOM_PLANE_V], src->d_w, src->d_h, dst->planes[AOM_PLANE_Y], dst->stride[AOM_PLANE_Y], dst->planes[AOM_PLANE_U], dst->stride[AOM_PLANE_U], dst->planes[AOM_PLANE_V], dst->stride[AOM_PLANE_V], dst->d_w, dst->d_h, mode); }
static INLINE int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst, FilterModeEnum mode) { #if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH if (src->fmt == VPX_IMG_FMT_I42016) { assert(dst->fmt == VPX_IMG_FMT_I42016); return I420Scale_16((uint16_t*)src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y]/2, (uint16_t*)src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U]/2, (uint16_t*)src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V]/2, src->d_w, src->d_h, (uint16_t*)dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y]/2, (uint16_t*)dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U]/2, (uint16_t*)dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V]/2, dst->d_w, dst->d_h, mode); } #endif assert(src->fmt == VPX_IMG_FMT_I420); assert(dst->fmt == VPX_IMG_FMT_I420); return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y], src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U], src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V], src->d_w, src->d_h, dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y], dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U], dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V], dst->d_w, dst->d_h, mode); }
static int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst, FilterMode mode) { assert(src->fmt == VPX_IMG_FMT_I420); assert(dst->fmt == VPX_IMG_FMT_I420); return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y], src->planes[VPX_PLANE_U], src->stride[VPX_PLANE_U], src->planes[VPX_PLANE_V], src->stride[VPX_PLANE_V], src->d_w, src->d_h, dst->planes[VPX_PLANE_Y], dst->stride[VPX_PLANE_Y], dst->planes[VPX_PLANE_U], dst->stride[VPX_PLANE_U], dst->planes[VPX_PLANE_V], dst->stride[VPX_PLANE_V], dst->d_w, dst->d_h, mode); }
int main(int argc, char **argv) { FILE *infile, *outfile[NUM_ENCODERS]; vpx_codec_ctx_t codec[NUM_ENCODERS]; vpx_codec_enc_cfg_t cfg[NUM_ENCODERS]; vpx_codec_pts_t frame_cnt = 0; vpx_image_t raw[NUM_ENCODERS]; vpx_codec_err_t res[NUM_ENCODERS]; int i; long width; long height; int frame_avail; int got_data; int flags = 0; /*Currently, only realtime mode is supported in multi-resolution encoding.*/ int arg_deadline = VPX_DL_REALTIME; /* Set show_psnr to 1/0 to show/not show PSNR. Choose show_psnr=0 if you don't need to know PSNR, which will skip PSNR calculation and save encoding time. */ int show_psnr = 0; uint64_t psnr_sse_total[NUM_ENCODERS] = {0}; uint64_t psnr_samples_total[NUM_ENCODERS] = {0}; double psnr_totals[NUM_ENCODERS][4] = {{0,0}}; int psnr_count[NUM_ENCODERS] = {0}; /* Set the required target bitrates for each resolution level. */ unsigned int target_bitrate[NUM_ENCODERS]={1400, 500, 100}; /* Enter the frame rate of the input video */ int framerate = 30; /* Set down-sampling factor for each resolution level. dsf[0] controls down sampling from level 0 to level 1; dsf[1] controls down sampling from level 1 to level 2; dsf[2] is not used. */ vpx_rational_t dsf[NUM_ENCODERS] = {{2, 1}, {2, 1}, {1, 1}}; if(argc!= (5+NUM_ENCODERS)) die("Usage: %s <width> <height> <infile> <outfile(s)> <output psnr?>\n", argv[0]); printf("Using %s\n",vpx_codec_iface_name(interface)); width = strtol(argv[1], NULL, 0); height = strtol(argv[2], NULL, 0); if(width < 16 || width%2 || height <16 || height%2) die("Invalid resolution: %ldx%ld", width, height); /* Open input video file for encoding */ if(!(infile = fopen(argv[3], "rb"))) die("Failed to open %s for reading", argv[3]); /* Open output file for each encoder to output bitstreams */ for (i=0; i< NUM_ENCODERS; i++) { if(!(outfile[i] = fopen(argv[i+4], "wb"))) die("Failed to open %s for writing", argv[i+4]); } show_psnr = strtol(argv[NUM_ENCODERS + 4], NULL, 0); /* Populate default encoder configuration */ for (i=0; i< NUM_ENCODERS; i++) { res[i] = vpx_codec_enc_config_default(interface, &cfg[i], 0); if(res[i]) { printf("Failed to get config: %s\n", vpx_codec_err_to_string(res[i])); return EXIT_FAILURE; } } /* * Update the default configuration according to needs of the application. */ /* Highest-resolution encoder settings */ cfg[0].g_w = width; cfg[0].g_h = height; cfg[0].g_threads = 1; /* number of threads used */ cfg[0].rc_dropframe_thresh = 0; cfg[0].rc_end_usage = VPX_CBR; cfg[0].rc_resize_allowed = 0; cfg[0].rc_min_quantizer = 4; cfg[0].rc_max_quantizer = 56; cfg[0].rc_undershoot_pct = 98; cfg[0].rc_overshoot_pct = 100; cfg[0].rc_buf_initial_sz = 500; cfg[0].rc_buf_optimal_sz = 600; cfg[0].rc_buf_sz = 1000; //cfg[0].rc_dropframe_thresh = 10; cfg[0].g_error_resilient = 1; /* Enable error resilient mode */ cfg[0].g_lag_in_frames = 0; /* Disable automatic keyframe placement */ //cfg[0].kf_mode = VPX_KF_DISABLED; cfg[0].kf_min_dist = cfg[0].kf_max_dist = 1000; cfg[0].rc_target_bitrate = target_bitrate[0]; /* Set target bitrate */ cfg[0].g_timebase.num = 1; /* Set fps */ cfg[0].g_timebase.den = framerate; /* Other-resolution encoder settings */ for (i=1; i< NUM_ENCODERS; i++) { memcpy(&cfg[i], &cfg[0], sizeof(vpx_codec_enc_cfg_t)); cfg[i].g_threads = 1; /* number of threads used */ cfg[i].rc_target_bitrate = target_bitrate[i]; /* Note: Width & height of other-resolution encoders are calculated * from the highest-resolution encoder's size and the corresponding * down_sampling_factor. */ { unsigned int iw = cfg[i-1].g_w*dsf[i-1].den + dsf[i-1].num - 1; unsigned int ih = cfg[i-1].g_h*dsf[i-1].den + dsf[i-1].num - 1; cfg[i].g_w = iw/dsf[i-1].num; cfg[i].g_h = ih/dsf[i-1].num; } /* Make width & height to be multiplier of 2. */ // Should support odd size ??? if((cfg[i].g_w)%2)cfg[i].g_w++; if((cfg[i].g_h)%2)cfg[i].g_h++; } /* Allocate image for each encoder */ for (i=0; i< NUM_ENCODERS; i++) if(!vpx_img_alloc(&raw[i], VPX_IMG_FMT_I420, cfg[i].g_w, cfg[i].g_h, 32)) die("Failed to allocate image", cfg[i].g_w, cfg[i].g_h); if (raw[0].stride[VPX_PLANE_Y] == raw[0].d_w) read_frame_p = read_frame; else read_frame_p = read_frame_by_row; for (i=0; i< NUM_ENCODERS; i++) write_ivf_file_header(outfile[i], &cfg[i], 0); /* Initialize multi-encoder */ if(vpx_codec_enc_init_multi(&codec[0], interface, &cfg[0], NUM_ENCODERS, (show_psnr ? VPX_CODEC_USE_PSNR : 0), &dsf[0])) die_codec(&codec[0], "Failed to initialize encoder"); /* The extra encoding configuration parameters can be set as follows. */ /* Set encoding speed */ for ( i=0; i<NUM_ENCODERS; i++) { int speed = -6; if(vpx_codec_control(&codec[i], VP8E_SET_CPUUSED, speed)) die_codec(&codec[i], "Failed to set cpu_used"); } /* Set static thresh for highest-resolution encoder. Set it to 1000 for * better performance. */ { unsigned int static_thresh = 1000; if(vpx_codec_control(&codec[0], VP8E_SET_STATIC_THRESHOLD, static_thresh)) die_codec(&codec[0], "Failed to set static threshold"); } /* Set static thresh = 0 for other encoders for better quality */ for ( i=1; i<NUM_ENCODERS; i++) { unsigned int static_thresh = 0; if(vpx_codec_control(&codec[i], VP8E_SET_STATIC_THRESHOLD, static_thresh)) die_codec(&codec[i], "Failed to set static threshold"); } frame_avail = 1; got_data = 0; while(frame_avail || got_data) { vpx_codec_iter_t iter[NUM_ENCODERS]={NULL}; const vpx_codec_cx_pkt_t *pkt[NUM_ENCODERS]; flags = 0; frame_avail = read_frame_p(infile, &raw[0]); if(frame_avail) { for ( i=1; i<NUM_ENCODERS; i++) { /*Scale the image down a number of times by downsampling factor*/ /* FilterMode 1 or 2 give better psnr than FilterMode 0. */ I420Scale(raw[i-1].planes[VPX_PLANE_Y], raw[i-1].stride[VPX_PLANE_Y], raw[i-1].planes[VPX_PLANE_U], raw[i-1].stride[VPX_PLANE_U], raw[i-1].planes[VPX_PLANE_V], raw[i-1].stride[VPX_PLANE_V], raw[i-1].d_w, raw[i-1].d_h, raw[i].planes[VPX_PLANE_Y], raw[i].stride[VPX_PLANE_Y], raw[i].planes[VPX_PLANE_U], raw[i].stride[VPX_PLANE_U], raw[i].planes[VPX_PLANE_V], raw[i].stride[VPX_PLANE_V], raw[i].d_w, raw[i].d_h, 1); } } /* Encode each frame at multi-levels */ if(vpx_codec_encode(&codec[0], frame_avail? &raw[0] : NULL, frame_cnt, 1, flags, arg_deadline)) die_codec(&codec[0], "Failed to encode frame"); for (i=NUM_ENCODERS-1; i>=0 ; i--) { got_data = 0; while( (pkt[i] = vpx_codec_get_cx_data(&codec[i], &iter[i])) ) { got_data = 1; switch(pkt[i]->kind) { case VPX_CODEC_CX_FRAME_PKT: write_ivf_frame_header(outfile[i], pkt[i]); if(fwrite(pkt[i]->data.frame.buf, 1, pkt[i]->data.frame.sz, outfile[i])); break; case VPX_CODEC_PSNR_PKT: if (show_psnr) { int j; psnr_sse_total[i] += pkt[i]->data.psnr.sse[0]; psnr_samples_total[i] += pkt[i]->data.psnr.samples[0]; for (j = 0; j < 4; j++) { //fprintf(stderr, "%.3lf ", pkt[i]->data.psnr.psnr[j]); psnr_totals[i][j] += pkt[i]->data.psnr.psnr[j]; } psnr_count[i]++; } break; default: break; } printf(pkt[i]->kind == VPX_CODEC_CX_FRAME_PKT && (pkt[i]->data.frame.flags & VPX_FRAME_IS_KEY)? "K":"."); fflush(stdout); } } frame_cnt++; } printf("\n"); fclose(infile); for (i=0; i< NUM_ENCODERS; i++) { printf("Processed %ld frames.\n",(long int)frame_cnt-1); /* Calculate PSNR and print it out */ if ( (show_psnr) && (psnr_count[i]>0) ) { int j; double ovpsnr = vp8_mse2psnr(psnr_samples_total[i], 255.0, psnr_sse_total[i]); fprintf(stderr, "\n ENC%d PSNR (Overall/Avg/Y/U/V)", i); fprintf(stderr, " %.3lf", ovpsnr); for (j = 0; j < 4; j++) { fprintf(stderr, " %.3lf", psnr_totals[i][j]/psnr_count[i]); } } if(vpx_codec_destroy(&codec[i])) die_codec(&codec[i], "Failed to destroy codec"); /* Try to rewrite the file header with the actual frame count */ if(!fseek(outfile[i], 0, SEEK_SET)) write_ivf_file_header(outfile[i], &cfg[i], frame_cnt-1); fclose(outfile[i]); vpx_img_free(&raw[i]); } return EXIT_SUCCESS; }
JNIEXPORT jboolean JNICALL Java_co_splots_recorder_H264Encoder_encode( JNIEnv* env, jobject thiz, jbyteArray data, jobject cameraInfo, jlong timeStamp) { jclass camera_info_class = (*env)->GetObjectClass(env, cameraInfo); jfieldID field_id = (*env)->GetFieldID(env, camera_info_class, "orientation", "I"); jint rotation = (*env)->GetIntField(env, cameraInfo, field_id); field_id = (*env)->GetFieldID(env, camera_info_class, "facing", "I"); jint facing = (*env)->GetIntField(env, cameraInfo, field_id); int nv21_length = (*env)->GetArrayLength(env, data); uint8_t *nv21_input = (uint8_t *) (*env)->GetByteArrayElements(env, data, (jboolean) 0); int width = crop_width; int height = crop_height; int rotate = rotation; int converted_half_size = (width + 1) / 2; uint8_t convert_i420[width * height + (width * height + 1) / 2]; uint8_t *convert_i420_y = convert_i420; uint8_t *convert_i420_u = convert_i420 + (width * height); uint8_t *convert_i420_v = convert_i420_u + (converted_half_size * converted_half_size); if (facing == 1) { height = 0 - height; rotate = (rotate + 180) % 360; } if (ConvertToI420(nv21_input, nv21_length, convert_i420_y, width, convert_i420_v, converted_half_size, convert_i420_u, converted_half_size, crop_x, crop_y, src_width, src_height, width, height, rotate, FOURCC_NV21) != 0) return JNI_FALSE; int scaled_half_size = (dest_width + 1) / 2; int scaled_data_length = dest_width * dest_height + (dest_width * dest_height + 1) / 2; uint8_t scaled_i420[scaled_data_length]; uint8_t* scaled_i420_y = scaled_i420; uint8_t* scaled_i420_u = scaled_i420 + (dest_width * dest_height); uint8_t* scaled_i420_v = scaled_i420_u + (scaled_half_size * scaled_half_size); if (width != dest_width || height != dest_height) { if (I420Scale(convert_i420_y, width, convert_i420_u, converted_half_size, convert_i420_v, converted_half_size, width, height, scaled_i420_y, dest_width, scaled_i420_u, scaled_half_size, scaled_i420_v, scaled_half_size, dest_width, dest_height, kFilterNone) != 0) return JNI_FALSE; } else { scaled_i420_y = convert_i420_y; scaled_i420_u = convert_i420_u; scaled_i420_v = convert_i420_v; } uint8_t *frame = (uint8_t *) malloc(sizeof(uint8_t) * scaled_data_length); uint8_t *frame_y = frame; uint8_t *frame_u = frame_y + (dest_width * dest_height); uint8_t *frame_v = frame_u + (scaled_half_size * scaled_half_size); memcpy(frame_y, scaled_i420_y, dest_width * dest_height); memcpy(frame_u, scaled_i420_v, scaled_half_size * scaled_half_size); memcpy(frame_v, scaled_i420_u, scaled_half_size * scaled_half_size); if (thumbnail == NULL) { thumbnail_size = scaled_data_length; thumbnail_width = dest_width; thumbnail_height = dest_height; thumbnail = (uint8_t *) malloc(sizeof(uint8_t) * thumbnail_size); uint8_t* thumbnail_y = thumbnail; uint8_t* thumbnail_vu = thumbnail + (dest_width * dest_height); I420ToNV12(scaled_i420_y, dest_width, scaled_i420_u, scaled_half_size, scaled_i420_v, scaled_half_size, thumbnail_y, dest_width, thumbnail_vu, dest_width, dest_width, dest_height); } SSourcePicture pic; memset(&pic, 0, sizeof(SSourcePicture)); pic.iPicWidth = dest_width; pic.iPicHeight = dest_width; pic.iColorFormat = videoFormatI420; pic.iStride[0] = dest_width; pic.iStride[1] = pic.iStride[2] = scaled_half_size; pic.uiTimeStamp = (long long) timeStamp; pic.pData[0] = frame; pic.pData[1] = pic.pData[0] + dest_width * dest_height; pic.pData[2] = pic.pData[1] + (scaled_half_size * scaled_half_size); float current_frame_rate = ((float) frame_count * 1000) / ((float) timeStamp); frame_rate_sum += current_frame_rate; LOG("current fps: %f", current_frame_rate); /*if ((*encoder)->SetOption(encoder, ENCODER_OPTION_FRAME_RATE, (void*) ¤t_frame_rate) != cmResultSuccess) LOG("Could not update frame rate.");*/ EVideoFrameType frameType = (*encoder)->EncodeFrame(encoder, &pic, &info); if (frameType == videoFrameTypeInvalid) return JNI_FALSE; LOG("Frame #%d", frame_count); frame_count++; if (frameType != videoFrameTypeSkip) writeInfoToFile(&info); return JNI_TRUE; }