/* encx265.c Copyright (c) 2003-2013 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef USE_X265 #include #include #include "hb.h" #include "hb_dict.h" #include "x265.h" int encx265Init( hb_work_object_t *, hb_job_t * ); int encx265Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void encx265Close( hb_work_object_t * ); void writeNALs(hb_work_private_t *, const x265_nal *, int ); hb_work_object_t hb_encx265 = { WORK_ENCX265, "H.265/HEVC encoder (libx265)", encx265Init, encx265Work, encx265Close }; #define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec #define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 1.4 frames/sec #define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) static const char * const hb_x265_encopt_synonyms[][2] = { { "me", "motion", }, { NULL, NULL, }, }; struct hb_work_private_s { hb_job_t * job; x265_encoder * x265; x265_param * param; x265_picture pic_in; x265_nal * p_nal; uint32_t nal_count; uint8_t * grey_data; uint32_t frames_in; uint32_t frames_out; int chap_mark; // saved chap mark when we're propagating it int64_t last_stop; // Debugging - stop time of previous input frame int64_t next_chap; struct { int64_t duration; } frame_info[FRAME_INFO_SIZE]; int i_type; int numEncode; int64_t i_pts; FILE *fout; }; /*********************************************************************** * hb_work_encx265_init *********************************************************************** * **********************************************************************/ int encx265Init( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); w->private_data = pv; pv->job = job; pv->fout = fopen(job->file, "wb"); fseek(pv->fout, 0, SEEK_SET); x265_param *param = pv->param = x265_param_alloc(); x265_param_default_preset(param, "medium", "psnr"); hb_log("Output video resolution: %dx%d", job->width, job->height); param->sourceWidth = job->width; param->sourceHeight = job->height; param->frameRate = job->vrate/job->vrate_base; param->poolNumThreads = hb_get_cpu_count(); param->logLevel = X265_LOG_INFO; param->frameNumThreads = hb_get_cpu_count(); param->tuQTMaxInterDepth = 1; param->tuQTMaxIntraDepth = 1; hb_dict_t *x265_opts = NULL; if (job->advanced_opts != NULL && *job->advanced_opts != '\0') { x265_opts = hb_encopts_to_dict(job->advanced_opts, job->vcodec); } /* iterate through x265_opts and parse the options */ int ret; hb_dict_entry_t *entry = NULL; while ((entry = hb_dict_next(x265_opts, entry)) != NULL) { ret = x265_param_parse( param, entry->key, entry->value ); /* Let x265 sanity check the options for us */ if( ret == X265_PARAM_BAD_NAME ) hb_log( "x265 options: Unknown suboption %s", entry->key ); if( ret == X265_PARAM_BAD_VALUE ) hb_log( "x265 options: Bad argument %s=%s", entry->key, entry->value ? entry->value : "(null)" ); } hb_dict_free(&x265_opts); param->subpelRefine = 1; param->maxNumMergeCand = 1; param->bEnablePsnr = 1; if (job->vquality > 0) param->rc.qp = (int)job->vquality; param->rc.bitrate = job->vbitrate; x265_setup_primitives(param, 0); pv->x265 = x265_encoder_open( param ); if ( pv->x265 == NULL ) { hb_error("encx265: x265_encoder_open failed."); free( pv ); pv = NULL; return 1; } pv->numEncode = 0; if (!x265_encoder_headers(pv->x265, &pv->p_nal, &pv->nal_count)) { writeNALs(pv, pv->p_nal, pv->nal_count); } x265_picture_init(param, &pv->pic_in); return 0; } void encx265Close( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; x265_param_free(pv->param); x265_encoder_close(pv->x265); fclose(pv->fout); free(pv); w->private_data = NULL; } /* * see comments in definition of 'frame_info' in pv struct for description * of what these routines are doing. */ static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in ) { int i = (in->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; pv->frame_info[i].duration = in->s.stop - in->s.start; } void writeNALs(hb_work_private_t * pv, const x265_nal* nal, int nalcount) { int i; for (i = 0; i < nalcount; i++) { fwrite((const char*)nal->payload, 1, nal->sizeBytes, pv->fout); nal++; } } static hb_buffer_t *x265_encode( hb_work_object_t *w, hb_buffer_t *in ) { hb_work_private_t *pv = w->private_data; hb_job_t *job = pv->job; x265_picture pic_out; int numEncode; pv->pic_in.stride[0] = in->plane[0].stride; pv->pic_in.stride[1] = in->plane[1].stride; pv->pic_in.stride[2] = in->plane[2].stride; pv->pic_in.planes[0] = in->plane[0].data; pv->pic_in.planes[1] = in->plane[1].data; pv->pic_in.planes[2] = in->plane[2].data; pv->pic_in.bitDepth = 8; if( in->s.new_chap && job->chapter_markers ) { pv->i_type = X265_TYPE_IDR; if( pv->next_chap == 0 ) { pv->next_chap = in->s.start; pv->chap_mark = in->s.new_chap; } in->s.new_chap = 0; } else { pv->i_type = X265_TYPE_AUTO; } if( pv->last_stop != in->s.start ) { hb_log("encx265 input continuity err: last stop %"PRId64" start %"PRId64, pv->last_stop, in->s.start); } pv->last_stop = in->s.stop; save_frame_info( pv, in ); pv->pic_in.pts = in->s.start; numEncode = x265_encoder_encode( pv->x265, &pv->p_nal, &pv->nal_count, &pv->pic_in, &pic_out ); pv->numEncode += numEncode; if ( pv->nal_count > 0 ) writeNALs(pv, pv->p_nal, pv->nal_count); return NULL; } int encx265Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t *pv = w->private_data; hb_buffer_t *in = *buf_in; int numEncode; *buf_out = NULL; if (in->size <= 0) { x265_picture pic_out; uint32_t i_nal; x265_nal *nal; x265_stats stats; hb_buffer_t *last_buf = NULL; while (1) { numEncode = x265_encoder_encode(pv->x265, &nal, &i_nal, NULL, &pic_out); if (i_nal <= 0) break; pv->numEncode += numEncode; writeNALs(pv, nal, i_nal); } // Flushed everything - add the eof to the end of the chain. if ( last_buf == NULL ) *buf_out = in; else last_buf->next = in; *buf_in = NULL; x265_encoder_get_stats(pv->x265, &stats, sizeof(stats)); x265_encoder_close(pv->x265); if (stats.encodedPictureCount) { hb_log("X265 encoded %d frames in %.2fs (%.2f fps), %.2f kb/s, ", stats.encodedPictureCount, stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); hb_log("Global PSNR: %.3f\n", stats.globalPsnr); } else hb_log("encoded 0 frames\n"); hb_log("Work done!"); exit(0); return HB_WORK_DONE; } pv->pic_in.poc = pv->frames_in; ++pv->frames_in; ++pv->frames_out; *buf_out = x265_encode( w, in ); return HB_WORK_OK; } const char * hb_x265_encopt_name(const char *name) { int i; for (i = 0; hb_x265_encopt_synonyms[i][0] != NULL; i++) if (!strcmp(name, hb_x265_encopt_synonyms[i][1])) return hb_x265_encopt_synonyms[i][0]; return name; } #endif