/* +----------------------------------------------------------------------+ | This source file is subject to version 3.0 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_0.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Evan Klinger | +----------------------------------------------------------------------+ */ /* $ Id: $ */ #include "php_pHash.h" #if HAVE_PHASH struct ph_audio_hash { uint32_t *hash; int len; }; struct ph_video_hash { ulong64 *hash; int len; }; struct ph_text_hash { TxtHashPoint *p; int count; }; /* {{{ phpinfo logo definitions */ #include "php_logos.h" static unsigned char pHash_logo[] = { #include "pHash_logos.h" }; /* }}} */ /* {{{ Resource destructors */ int le_ph_video_hash; extern "C" void ph_video_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { ph_video_hash *resource = (ph_video_hash *)(rsrc->ptr); if (resource) { free(resource->hash); free(resource); } } int le_ph_image_hash; extern "C" void ph_image_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { ulong64 *resource = (ulong64 *)(rsrc->ptr); if (resource) free(resource); } int le_ph_audio_hash; extern "C" void ph_audio_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { ph_audio_hash *resource = (ph_audio_hash *)(rsrc->ptr); if (resource) { free(resource->hash); free(resource); } } int le_ph_txt_hash; extern "C" void ph_txt_hash_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) { ph_text_hash *resource = (ph_text_hash *)(rsrc->ptr); if (resource) { free(resource->p); free(resource); } } /* }}} */ /* {{{ pHash_functions[] */ zend_function_entry pHash_functions[] = { #if HAVE_VIDEO_HASH PHP_FE(ph_dct_videohash, ph_dct_videohash_arg_info) #endif /* HAVE_VIDEO_HASH */ #if HAVE_IMAGE_HASH PHP_FE(ph_dct_imagehash, ph_dct_imagehash_arg_info) #endif /* HAVE_IMAGE_HASH */ PHP_FE(ph_texthash, ph_texthash_arg_info) #if HAVE_AUDIO_HASH PHP_FE(ph_audiohash, ph_audiohash_arg_info) #endif /* HAVE_AUDIO_HASH */ #if HAVE_IMAGE_HASH PHP_FE(ph_image_dist, ph_image_dist_arg_info) #endif /* HAVE_IMAGE_HASH */ #if HAVE_VIDEO_HASH PHP_FE(ph_video_dist, ph_video_dist_arg_info) #endif /* HAVE_VIDEO_HASH */ #if HAVE_AUDIO_HASH PHP_FE(ph_audio_dist, ph_audio_dist_arg_info) #endif /* HAVE_AUDIO_HASH */ PHP_FE(ph_compare_text_hashes, ph_compare_text_hashes_arg_info){ NULL, NULL, NULL}}; /* }}} */ /* {{{ pHash_module_entry */ zend_module_entry pHash_module_entry = { STANDARD_MODULE_HEADER, "pHash", pHash_functions, PHP_MINIT(pHash), /* Replace with NULL if there is nothing to do at php startup */ PHP_MSHUTDOWN(pHash), /* Replace with NULL if there is nothing to do at php shutdown */ PHP_RINIT(pHash), /* Replace with NULL if there is nothing to do at request start */ PHP_RSHUTDOWN(pHash), /* Replace with NULL if there is nothing to do at request end */ PHP_MINFO(pHash), PHP_PHASH_VERSION, STANDARD_MODULE_PROPERTIES}; /* }}} */ #ifdef COMPILE_DL_PHASH extern "C" { ZEND_GET_MODULE(pHash) } // extern "C" #endif /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(pHash) { php_register_info_logo("PHASH_LOGO_ID", "", pHash_logo, 49651); le_ph_video_hash = zend_register_list_destructors_ex( ph_video_hash_dtor, NULL, "ph_video_hash", module_number); le_ph_image_hash = zend_register_list_destructors_ex( ph_image_hash_dtor, NULL, "ph_image_hash", module_number); le_ph_audio_hash = zend_register_list_destructors_ex( ph_audio_hash_dtor, NULL, "ph_audio_hash", module_number); le_ph_txt_hash = zend_register_list_destructors_ex( ph_txt_hash_dtor, NULL, "ph_txt_hash", module_number); /* add your stuff here */ return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(pHash) { php_unregister_info_logo("PHASH_LOGO_ID"); /* add your stuff here */ return SUCCESS; } /* }}} */ /* {{{ PHP_RINIT_FUNCTION */ PHP_RINIT_FUNCTION(pHash) { /* add your stuff here */ return SUCCESS; } /* }}} */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ PHP_RSHUTDOWN_FUNCTION(pHash) { /* add your stuff here */ return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(pHash) { if (!sapi_module.phpinfo_as_text) { php_printf("image\n"); php_printf("pHash\n"); php_info_print_table_start(); php_info_print_table_row(2, "Version", PHP_PHASH_VERSION " (beta)"); php_info_print_table_row(2, "Released", "2013-04-23"); php_info_print_table_row(2, "CVS Revision", "$Id: $"); php_info_print_table_row(2, "Authors", "Evan Klinger 'eklinger@phash.org' (lead)\n"); php_info_print_table_end(); /* add your stuff here */ } } /* }}} */ #if HAVE_VIDEO_HASH /* {{{ proto resource ph_video_hash ph_dct_videohash(string file) pHash DCT video hash */ PHP_FUNCTION(ph_dct_videohash) { ph_video_hash *return_res; long return_res_id = -1; const char *file = NULL; int file_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { return; } int len; ulong64 *video_hash = ph_dct_videohash(file, len); if (video_hash) { ph_video_hash *p = (ph_video_hash *)malloc(sizeof(ph_video_hash)); p->hash = video_hash; p->len = len; return_res = p; } else RETURN_FALSE; return_res_id = ZEND_REGISTER_RESOURCE(return_value, return_res, le_ph_video_hash); } /* }}} ph_dct_videohash */ #endif /* HAVE_VIDEO_HASH */ #if HAVE_IMAGE_HASH /* {{{ proto resource ph_image_hash ph_dct_imagehash(string file) pHash DCT image hash */ PHP_FUNCTION(ph_dct_imagehash) { ulong64 *return_res; long return_res_id = -1; const char *file = NULL; int file_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { return; } ulong64 *hash = (ulong64 *)malloc(sizeof(ulong64)); int ret = ph_dct_imagehash(file, *hash); if (ret != 0) { free(hash); RETURN_FALSE; } else return_res = hash; return_res_id = ZEND_REGISTER_RESOURCE(return_value, return_res, le_ph_image_hash); } /* }}} ph_dct_imagehash */ #endif /* HAVE_IMAGE_HASH */ /* {{{ proto resource ph_txt_hash ph_texthash(string file) pHash cyclic text hash */ PHP_FUNCTION(ph_texthash) { ph_text_hash *return_res; long return_res_id = -1; const char *file = NULL; int file_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) { return; } int num; TxtHashPoint *txtHash = ph_texthash(file, &num); if (txtHash) { ph_text_hash *h = (ph_text_hash *)malloc(sizeof(ph_text_hash)); h->p = txtHash; h->count = num; return_res = h; } else RETURN_FALSE; return_res_id = ZEND_REGISTER_RESOURCE(return_value, return_res, le_ph_txt_hash); } /* }}} ph_texthash */ #if HAVE_AUDIO_HASH /* {{{ proto resource ph_audio_hash ph_audiohash(string file, int sample_rate=5512, int channels=1) pHash audio hash */ PHP_FUNCTION(ph_audiohash) { ph_audio_hash *return_res; long return_res_id = -1; const char *file = NULL; int file_len = 0; long sample_rate = 5512; long channels = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &file, &file_len, &sample_rate, &channels) == FAILURE) { return; } int n; float *audiobuf = ph_readaudio(file, sample_rate, channels, NULL, n); if (audiobuf) { int nb_frames; uint32_t *hash = ph_audiohash(audiobuf, n, sample_rate, nb_frames); free(audiobuf); if (hash) { ph_audio_hash *h = (ph_audio_hash *)malloc(sizeof(ph_audio_hash)); h->hash = hash; h->len = nb_frames; return_res = h; } else RETURN_FALSE; } else RETURN_FALSE; return_res_id = ZEND_REGISTER_RESOURCE(return_value, return_res, le_ph_audio_hash); } /* }}} ph_audiohash */ #endif /* HAVE_AUDIO_HASH */ #if HAVE_IMAGE_HASH /* {{{ proto float ph_image_dist(resource ph_image_hash h1,resource ph_image_hash h2) pHash image distance. */ PHP_FUNCTION(ph_image_dist) { zval *h1_res = NULL; int h1_resid = -1; ulong64 *h1; zval *h2_res = NULL; int h2_resid = -1; ulong64 *h2; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &h1_res, &h2_res) == FAILURE) { return; } ZEND_FETCH_RESOURCE(h1, ulong64 *, &h1_res, h1_resid, "ph_image_hash", le_ph_image_hash); ZEND_FETCH_RESOURCE(h2, ulong64 *, &h2_res, h2_resid, "ph_image_hash", le_ph_image_hash); if (h1 && h2) { int dist = ph_hamming_distance(*h1, *h2); RETURN_DOUBLE(dist); } else RETURN_DOUBLE(-1); } /* }}} ph_image_dist */ #endif /* HAVE_IMAGE_HASH */ #if HAVE_VIDEO_HASH /* {{{ proto float ph_video_dist(resource ph_video_hash h1,resource ph_video_hash h2, int thresh=21) pHash video distance. */ PHP_FUNCTION(ph_video_dist) { zval *h1_res = NULL; int h1_resid = -1; ph_video_hash *h1; zval *h2_res = NULL; int h2_resid = -1; ph_video_hash *h2; long thresh = 21; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|l", &h1_res, &h2_res, &thresh) == FAILURE) { return; } ZEND_FETCH_RESOURCE(h1, ph_video_hash *, &h1_res, h1_resid, "ph_video_hash", le_ph_video_hash); ZEND_FETCH_RESOURCE(h2, ph_video_hash *, &h2_res, h2_resid, "ph_video_hash", le_ph_video_hash); if (h1 && h2) { double sim = ph_dct_videohash_dist(h1->hash, h1->len, h2->hash, h2->len, thresh); RETURN_DOUBLE(sim); } else RETURN_DOUBLE(-1); } /* }}} ph_video_dist */ #endif /* HAVE_VIDEO_HASH */ #if HAVE_AUDIO_HASH /* {{{ proto float ph_audio_dist(resource ph_audio_hash h1,resource ph_audio_hash h2, int block_size=256, float thresh=0.30) pHash audio distance. */ PHP_FUNCTION(ph_audio_dist) { zval *h1_res = NULL; int h1_resid = -1; ph_audio_hash *h1; zval *h2_res = NULL; int h2_resid = -1; ph_audio_hash *h2; long block_size = 256; double thresh = 0.30; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr|ld", &h1_res, &h2_res, &block_size, &thresh) == FAILURE) { return; } ZEND_FETCH_RESOURCE(h1, ph_audio_hash *, &h1_res, h1_resid, "ph_audio_hash", le_ph_audio_hash); ZEND_FETCH_RESOURCE(h2, ph_audio_hash *, &h2_res, h2_resid, "ph_audio_hash", le_ph_audio_hash); if (h1 && h2) { int Nc; double *cs = ph_audio_distance_ber(h1->hash, h1->len, h2->hash, h2->len, thresh, block_size, Nc); if (cs) { double max_cs = 0.0; for (int i = 0; i < Nc; ++i) { if (cs[i] > max_cs) { max_cs = cs[i]; } } free(cs); RETURN_DOUBLE(max_cs); } else RETURN_DOUBLE(-1); } else RETURN_DOUBLE(-1); } /* }}} ph_audio_dist */ #endif /* HAVE_AUDIO_HASH */ /* {{{ proto array ph_compare_text_hashes(resource ph_txt_hash h1,resource ph_txt_hash h2) pHash text distance. */ PHP_FUNCTION(ph_compare_text_hashes) { zval *h1_res = NULL; int h1_resid = -1; ph_text_hash *h1; zval *h2_res = NULL; int h2_resid = -1; ph_text_hash *h2; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &h1_res, &h2_res) == FAILURE) { return; } ZEND_FETCH_RESOURCE(h1, ph_text_hash *, &h1_res, h1_resid, "ph_txt_hash", le_ph_txt_hash); ZEND_FETCH_RESOURCE(h2, ph_text_hash *, &h2_res, h2_resid, "ph_txt_hash", le_ph_txt_hash); array_init(return_value); if (h1 && h2) { int count = 0; TxtMatch *m = ph_compare_text_hashes(h1->p, h1->count, h2->p, h2->count, &count); if (m) { for (int i = 0; i < count; ++i) { zval *array; MAKE_STD_ZVAL(array); array_init(array); add_assoc_long(array, "begin", m[i].first_index); add_assoc_long(array, "end", m[i].second_index); add_assoc_long(array, "length", m[i].length); add_next_index_zval(return_value, array); } free(m); } else RETURN_FALSE; } else RETURN_FALSE; } /* }}} ph_compare_text_hashes */ #endif /* HAVE_PHASH */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */