FV Flowplayer Video Player - Version 2.2.9

Version Description

  • 2014/06/04 =

  • Feature - share bar added!

  • Bugfix - annoying iOS 7 bug where black lines appear after videos - fixed!

  • Bugfix - "Convert old shortcodes with commas" option changed to work with old [flowplayer] shortcodes only

  • Bugfix - Pro extension settings forgotten if Pro extension was deactivated and options saved afterwards

  • Bugfix - shortcode editor fixes for captions

Download this release

Release Info

Developer FolioVision
Plugin Icon 128x128 FV Flowplayer Video Player
Version 2.2.9
Comparing to
See all releases

Code changes from version 2.1.52 to 2.2.9

controller/backend.php CHANGED
@@ -28,9 +28,9 @@ $fv_fp = new flowplayer_backend();
28
  /**
29
  * WP Hooks
30
  */
31
- add_action('wp_ajax_fv_wp_flowplayer_support_mail', 'fv_wp_flowplayer_support_mail');
32
- add_action('wp_ajax_fv_wp_flowplayer_check_mimetype', 'fv_wp_flowplayer_check_mimetype');
33
- add_action('wp_ajax_fv_wp_flowplayer_check_template', 'fv_wp_flowplayer_check_template');
34
  add_action('wp_ajax_fv_wp_flowplayer_check_files', 'fv_wp_flowplayer_check_files');
35
 
36
  add_action('admin_head', 'flowplayer_head');
@@ -105,7 +105,15 @@ add_action('admin_notices', 'fv_wp_flowplayer_admin_notice');
105
 
106
 
107
  function flowplayer_activate() {
108
- update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer upgraded - please click "Check template" and "Check videos" for automated check of your site at <a href="'.site_url().'/wp-admin/options-general.php?page=fvplayer">the settings page</a> for automated checks!' );
 
 
 
 
 
 
 
 
109
  }
110
 
111
 
@@ -194,8 +202,11 @@ function fv_wp_flowplayer_media_send_to_editor($html, $attachment_id, $attachmen
194
  $serv = $_SERVER['SERVER_NAME'];
195
  $pattern = '/'.$serv.'(.*)/';
196
  preg_match($pattern, $uploaded_video, $matches);
197
- require_once( plugin_dir_path(__FILE__).'../includes/getid3/getid3.php');
198
- // Initialize getID3 engine
 
 
 
199
  $getID3 = new getID3;
200
  if (empty($matches)) {
201
  $ThisFileInfo = $getID3->analyze(realpath($document_root . $uploaded_video));
@@ -385,9 +396,7 @@ function flowplayer_add_media_button() {
385
  $button_tip = 'Insert a Flash Video Player';
386
  $wizard_url = 'media-upload.php?post_id='.$post->ID.'&type=fv-wp-flowplayer';
387
  $button_src = FV_FP_RELATIVE_PATH.'/images/icon.png';
388
- if(!$found) {
389
- $img = '<img src="' . $button_src . '" alt="' . $button_tip . '" />';
390
- }
391
  echo '<a title="Add FV WP Flowplayer" href="#" class="fv-wordpress-flowplayer-button" >'.$img.'</a>';
392
  }
393
 
@@ -435,9 +444,10 @@ function flowplayer_conversion_script() {
435
 
436
  function fv_wp_flowplayer_admin_notice() {
437
  if( $notices = get_option('fv_wordpress_flowplayer_deferred_notices') ) {
438
- echo '<div class="updated">
439
  <p>'.$notices.'</p>
440
- </div>';
 
441
  }
442
 
443
  $conversion = false; //(bool)get_option('fvwpflowplayer_conversion');
@@ -462,13 +472,13 @@ function fv_wp_flowplayer_admin_enqueue_scripts( $page ) {
462
  if( $page !== 'post.php' && $page !== 'post-new.php' ) {
463
  return;
464
  }
465
- wp_register_script('fvwpflowplayer-domwindow', plugins_url().'/fv-wordpress-flowplayer/js/jquery.colorbox-min.js',array('jquery') );
466
  wp_enqueue_script('fvwpflowplayer-domwindow');
467
 
468
- wp_register_script('fvwpflowplayer-shortcode-editor', plugins_url().'/fv-wordpress-flowplayer/js/shortcode-editor.js',array('jquery') );
469
  wp_enqueue_script('fvwpflowplayer-shortcode-editor');
470
 
471
- wp_register_style('fvwpflowplayer-domwindow-css', plugins_url().'/fv-wordpress-flowplayer/css/colorbox.css','','1.0','screen');
472
  wp_enqueue_style('fvwpflowplayer-domwindow-css');
473
  }
474
 
@@ -497,54 +507,102 @@ function fv_wp_flowplayer_admin_init() {
497
 
498
  global $fv_fp;
499
  global $fv_wp_flowplayer_ver, $fv_wp_flowplayer_core_ver;
500
- if( preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) && isset($fv_fp->conf['key_automatic']) && $fv_fp->conf['key_automatic'] == 'true' ) {
 
 
 
 
 
 
501
 
502
  $version = get_option( 'fvwpflowplayer_core_ver' );
503
  if( version_compare( $fv_wp_flowplayer_core_ver, $version ) == 1 ) {
504
  fv_wp_flowplayer_admin_key_update();
505
  }
506
  }
507
-
508
  if(
509
- preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) && version_compare( $fv_wp_flowplayer_core_ver, get_option('fvwpflowplayer_core_ver') ) !== 0 && ( !isset($fv_fp->conf['key_automatic']) || $fv_fp->conf['key_automatic'] != 'true' )
 
 
510
  ) {
511
- global $fv_fp;
512
- $fv_fp->pointer_boxes = array(
513
- 'fv_flowplayer_key_automatic' => array(
514
- 'id' => '#wpadminbar',
515
- 'heading' => __('FV Flowplayer License Update', 'fv_flowplayer'),
516
- 'content' => __('New version of FV Flowplayer core has been installed for your licensed website. Please accept the automatic license key updating (connects to Foliovision servers) or update the key manually by loggin into your Foliovision account.', 'fv_flowplayer'),
517
- 'position' => array( 'edge' => 'top', 'align' => 'center' ),
518
- 'button1' => __('Always auto-update', 'fv_flowplayer'),
519
- 'button2' => __('I\'ll update it manually', 'fv_flowplayer')
520
- )
521
- );
522
- } else if( version_compare( $fv_wp_flowplayer_core_ver, get_option('fvwpflowplayer_core_ver') ) !== 0 && preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) == 0 ) {
523
- update_option( 'fvwpflowplayer_core_ver', $fv_wp_flowplayer_core_ver );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  }
 
 
 
 
525
 
 
 
 
 
526
 
527
  if( isset($_GET['page']) && $_GET['page'] == 'fvplayer' ) {
528
  wp_enqueue_script('common');
529
  wp_enqueue_script('wp-lists');
530
  wp_enqueue_script('postbox');
531
  }
532
-
533
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  }
535
 
536
 
537
  function fv_wp_flowplayer_admin_key_update() {
538
- global $fv_wp_flowplayer_ver, $fv_wp_flowplayer_core_ver, $fv_fp;
539
-
540
- $args = array(
541
- 'body' => array( 'domain' => home_url(), 'plugin' => 'fv-wordpress-flowplayer', 'version' => $fv_wp_flowplayer_ver, 'core_ver' => $fv_wp_flowplayer_core_ver ),
542
- 'timeout' => 20,
543
- 'user-agent' => 'fv-wordpress-flowplayer-'.$fv_wp_flowplayer_ver.' ('.$fv_wp_flowplayer_core_ver.')'
544
- );
545
- $resp = wp_remote_post( 'http://foliovision.com/?fv_remote=true', $args );
546
 
547
- if( $resp['body'] && $data = json_decode( $resp['body'] ) ) {
 
548
  if( $data->domain && $data->key && stripos( home_url(), $data->domain ) !== false ) {
549
  $fv_fp->conf['key'] = $data->key;
550
  update_option( 'fvwpflowplayer', $fv_fp->conf );
@@ -552,11 +610,31 @@ function fv_wp_flowplayer_admin_key_update() {
552
  return true;
553
  }
554
  } else {
 
 
555
  return false;
556
  }
557
  }
558
 
559
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  function fv_wp_flowplayer_edit_form_after_editor( ) {
561
  include dirname( __FILE__ ) . '/../view/wizard.php';
562
  }
@@ -580,7 +658,7 @@ function fv_wp_flowplayer_after_plugin_row( $arg) {
580
  <tr class="plugin-update-tr fv-wordpress-flowplayer-tr">
581
  <td class="plugin-update colspanchange" colspan="3">
582
  <div class="update-message">
583
- <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/download">All Licenses 20% Off</a> - Christmas sale!
584
  </div>
585
  </td>
586
  </tr>
@@ -590,24 +668,30 @@ function fv_wp_flowplayer_after_plugin_row( $arg) {
590
  }
591
 
592
 
593
- function fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random ) {
594
  global $fv_fp;
 
 
595
 
596
- $video_errors = array();
597
 
 
598
  if( $headers && $headers['response']['code'] == '404' ) {
599
- $video_errors[] = 'File not found (HTTP 404)!';
 
600
  } else if( $headers && $headers['response']['code'] == '403' ) {
601
- $video_errors[] = 'Access to video forbidden (HTTP 403)!';
 
602
  } else if( $headers && $headers['response']['code'] != '200' && $headers['response']['code'] != '206' ) {
603
- $video_errors[] = 'Can\'t check the video (HTTP '.$headers['response']['code'].')!';
 
604
  } else {
605
 
606
  if(
607
  ( !isset($headers['headers']['accept-ranges']) || $headers['headers']['accept-ranges'] != 'bytes' ) &&
608
  !isset($headers['headers']['content-range'])
609
  ) {
610
- $video_errors[] = 'Server does not support HTTP range requests! Please check "I\'m getting error about \'HTTP range requests\'" in <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq">our FAQ</a>.';
611
  }
612
 
613
  if(
@@ -639,13 +723,18 @@ function fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random ) {
639
  AddType video/mp4 .mov
640
  # hls transport stream segments:
641
  AddType video/mp2t .ts</pre>
642
- <p>If you are using Microsoft IIS, you need to use the IIS manager. Check our <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq" target="_blank">FAQ</a> for more info.</p>
643
  </div>';
644
 
645
- $video_errors[] = '<p><strong>Bad mime type</strong>: Video served with a bad mime type <tt>'.$headers['headers']['content-type'].'</tt>!'.$meta_note_addition.' (<a href="#" onclick="jQuery(\'.fix-meta-'.$random.'\').toggle(); return false">show fix</a>)</p>'.$fix ;
 
 
 
 
646
  }
647
  }
648
- return $video_errors;
 
649
  }
650
 
651
 
@@ -653,6 +742,51 @@ function fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random ) {
653
  function fv_wp_flowplayer_http_api_curl( $handle ) {
654
  curl_setopt( $handle, CURLOPT_NOBODY, true );
655
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
 
657
 
658
  function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
@@ -670,90 +804,40 @@ function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
670
 
671
  if( isset($URLs) ) {
672
  $all_sources = $URLs;
673
-
674
- $video_warnings = array();
675
- $video_errors = array();
676
- $video_info = array();
677
- $message = false;
678
- $new_info = false;
679
-
680
  foreach( $all_sources AS $source ) {
681
  if( preg_match( '!^rtmp://!', $source, $match ) ) {
682
  $found_rtmp = true;
683
- } else {
684
- if( preg_match('!^http://(www\.)?youtube!',$source) ) {
685
- $video_errors[] = 'Youtube video embeding not supported yet. Please download the video file and put it in as a source directly.';
686
- } else if( !isset($media) && !preg_match( '!\.(m3u8|m3u|avi)$!', $source) ) {
687
- $media = $source;
688
- }
689
-
690
- if( preg_match( '!\.(mp4|m4v)$!', $source, $match ) ) {
691
- $found_mp4 = true;
692
- } else if( preg_match( '!\.mov$!', $source, $match ) ) {
693
- $video_warnings[] = 'We recommend that you re-encode your MOV video into MP4 format. MOV is not be 100% compatible with HTML5 and might not play in Google Chrome. <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only" target="_blank">Read our article about video encoding</a>';
694
- } else if( preg_match( '!\.flv$!', $source, $match ) ) {
695
- $found_flv = true;
696
- } else if( preg_match( '!\.(m3u8|m3u)$!', $source, $match ) ) {
697
- $found_m3u8 = true;
698
- } else if( preg_match( '!\.(avi)$!', $source, $match ) ) {
699
- $found_avi = true;
700
- } else if( preg_match( '!\.(3gp)$!', $source, $match ) ) {
701
- $found_3gp = true;
702
- } else if( preg_match( '!\.(webm)$!', $source, $match ) ) {
703
- $found_webm = true;
704
- }
705
  }
706
- }
707
-
708
- if( isset($found_flv) && !isset($found_mp4) ) {
709
- $video_warnings[] = 'We recommend that you re-encode your FLV video into MP4 format or also provide the video in MP4 format. FLV is not compatible with HTML5 and won\'t play on devices without Flash (iPhone, iPad...). <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only">Read our article about video encoding</a>';
710
- }
711
-
712
- if( isset($found_rtmp) && !isset($found_mp4) ) {
713
- $video_warnings[] = 'We recommend that you also provide your RTMP video in MP4 format. RTMP is not compatible with HTML5 and won\'t play on devices without Flash (iPhone, iPad...). <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only">Read our article about video encoding</a>';
714
- }
715
-
716
- if( isset($found_3gp) && !isset($found_mp4) ) {
717
- $video_warnings[] = 'We recommend that you re-encode your 3GP video into a MP4 format. 3GP is not compatible with all HTML5 players so it won\'t play on these devices if they don\'t have Flash. <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only">Read our article about video encoding</a>';
718
- }
719
-
720
- if( isset($found_m3u8) && count($all_sources) == 1 ) {
721
- $video_warnings[] = 'We recommend that you also provide your M3U8 video in MP4 or WEBM format. HTTP Live Streaming (m3u8) is only supported by Apple iOS devices (iPhone, iPad...). <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only">Read our article about video encoding</a>';
722
- }
723
-
724
- if( isset($found_avi) ) {
725
- $video_errors[] = 'AVI format is not supported by neither HTML5 nor Flash. Please re-encode the video to MP4. <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/encoding#flash-only">Read our article about video encoding</a>';
726
- }
727
-
728
  //$random = rand( 0, 10000 );
729
  $random = (isset($_POST['hash'])) ? trim($_POST['hash']) : false;
730
 
731
  if( isset($media) ) {
732
  $remotefilename = $media;
733
- $url_parts = parse_url( $remotefilename );
734
- $url_parts_encoded = parse_url( $remotefilename );
735
- if( !empty($url_parts['path']) ) {
736
- $url_parts['path'] = join('/', array_map('rawurlencode', explode('/', $url_parts_encoded['path'])));
737
- }
738
- if( !empty($url_parts['query']) ) {
739
- $url_parts['query'] = str_replace( '&amp;', '&', $url_parts_encoded['query'] );
740
- }
741
-
742
- $url_parts['path'] = str_replace( '%2B', '+', $url_parts['path'] );
743
-
744
- $remotefilename_encoded = http_build_url($remotefilename, $url_parts);
745
-
746
  if( $fv_fp->is_secure_amazon_s3($remotefilename_encoded) || 1>0 ) { // skip headers check for Amazon S3, as it's slow
747
  $headers = false;
748
  } else {
749
  $headers = wp_remote_head( trim( str_replace(' ', '%20', $remotefilename_encoded ) ), array( 'method' => 'GET', 'redirection' => 3 ) );
750
  }
751
 
 
752
  if( is_wp_error($headers) ) {
753
  $video_errors[] = 'Error checking '.$media.'!<br />'.print_r($headers,true);
754
  } else {
755
  if( $headers ) {
756
- $video_errors += fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random );
 
 
 
 
 
 
757
  }
758
 
759
  if( function_exists('is_utf8') && is_utf8($remotefilename) ) {
@@ -764,53 +848,43 @@ function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
764
  $video_warnings[] = 'Detailed video check is not available with PHP Safe Mode On. Please contact your webhost support.';
765
  } else {
766
 
767
- require_once( plugin_dir_path(__FILE__).'../includes/getid3/getid3.php');
 
 
768
  $getID3 = new getID3;
769
 
770
- preg_match( '~^\S+://([^/]+)~', $remotefilename, $remote_domain );
771
- preg_match( '~^\S+://([^/]+)~', home_url(), $site_domain );
772
-
773
  if( !function_exists('curl_init') ) {
774
  $video_errors[] = 'cURL for PHP not found, please contact your server administrator.';
775
- } else if( strlen($remote_domain[1]) > 0 && strlen($site_domain[1]) > 0 && $remote_domain[1] != $site_domain[1] ) {
776
  $message = '<p>Analysis of <a class="bluelink" target="_blank" href="'.esc_attr($remotefilename_encoded).'">'.$remotefilename_encoded.'</a></p>';
777
- $video_info['File'] = 'Remote';
778
 
779
  // taken from: http://www.getid3.org/phpBB3/viewtopic.php?f=3&t=1141
780
  $upload_dir = wp_upload_dir();
781
  $localtempfilename = trailingslashit( $upload_dir['basedir'] ).'fv_flowlayer_tmp_'.md5(rand(1,999)).'_'.basename( substr($remotefilename_encoded,0,32) );
782
-
783
  $out = fopen( $localtempfilename,'wb' );
784
  if( $out ) {
 
 
785
 
786
- $ch = curl_init();
787
- curl_setopt( $ch, CURLOPT_URL, $remotefilename_encoded );
788
- curl_setopt( $ch, CURLOPT_RANGE, '0-2097152' );
789
- curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
790
- if( !@ini_get('open_basedir') ) {
791
- @curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
792
- }
793
- curl_setopt( $ch, CURLOPT_HEADER, true );
794
- curl_setopt( $ch, CURLOPT_VERBOSE, 1 );
795
- curl_setopt( $ch, CURLOPT_USERAGENT, 'FV Flowplayer video checker/'.$fv_wp_flowplayer_ver);
796
-
797
- $data = curl_exec($ch);
798
-
799
- $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
800
- $header = substr($data, 0, $header_size);
801
- $body = substr($data, $header_size);
802
-
803
- file_put_contents( $localtempfilename, $body);
804
- if($ch == false) {
805
- $message .= 'CURL Error: '.curl_error ( $ch);
806
  }
807
- curl_close($ch);
808
  fclose($out);
809
 
810
  if( !$headers ) {
811
  $headers = WP_Http::processHeaders( $header );
812
-
813
- $video_errors += fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random );
 
 
 
 
 
 
 
 
814
  if( isset($hearders['headers']['server']) && $hearders['headers']['server'] == 'AmazonS3' && $headers['response']['code'] == '403' ) {
815
  $error = new SimpleXMLElement($body);
816
 
@@ -822,43 +896,24 @@ function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
822
 
823
  }
824
  }
825
-
826
- $ThisFileInfo = $getID3->analyze( $localtempfilename );
827
 
 
 
 
828
  if( !@unlink($localtempfilename) ) {
829
  $video_errors[] = 'Can\'t remove temporary file for video analysis in <tt>'.$localtempfilename.'</tt>!';
830
- }
831
  } else {
832
  $video_errors[] = 'Can\'t create temporary file for video analysis in <tt>'.$localtempfilename.'</tt>!';
833
  }
834
- } else {
835
- $a_link = str_replace( '&amp;', '&', $remotefilename );
836
- $message = '<p>Analysis of <a class="bluelink" target="_blank" href="'.esc_attr($a_link).'">'.$a_link.'</a></p>';
837
- $video_info['File'] = 'Local';
838
-
839
- $document_root = ( isset($_SERVER['SUBDOMAIN_DOCUMENT_ROOT']) && strlen(trim($_SERVER['SUBDOMAIN_DOCUMENT_ROOT'])) > 0 ) ? $_SERVER['SUBDOMAIN_DOCUMENT_ROOT'] : $_SERVER['DOCUMENT_ROOT'];
840
-
841
- global $blog_id;
842
- if( isset($blog_id) && $blog_id > 1 ) {
843
- $upload_dir = wp_upload_dir();
844
- if( stripos($remotefilename, $upload_dir['baseurl']) !== false ) {
845
- $localtempfilename = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $remotefilename );
846
- } else {
847
- $localtempfilename = preg_replace( '~^\S+://[^/]+~', trailingslashit($document_root), preg_replace( '~(\.[a-z]{1,4})/files/~', '$1/wp-content/blogs.dir/'.$blog_id.'/files/', $remotefilename ) );
848
- }
849
- } else {
850
- $localtempfilename = preg_replace( '~^\S+://[^/]+~', trailingslashit($document_root), $remotefilename );
851
- }
852
-
853
- $ThisFileInfo = $getID3->analyze( $localtempfilename );
854
  }
855
-
856
 
857
  /*
858
  Only check file length
859
  */
860
  if( isset($meta_action) && $meta_action == 'check_time' ) {
861
-
862
  if( isset($ThisFileInfo['playtime_seconds']) ) {
863
  $time = $ThisFileInfo['playtime_seconds'];
864
  }
@@ -869,282 +924,11 @@ function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
869
  update_post_meta( $post->ID, '_fv_flowplayer', $fv_flowplayer_meta );
870
  return;
871
  }
872
-
873
-
874
- if( isset($ThisFileInfo['error']) ) {
875
- fv_wp_flowplayer_array_search_by_item( 'not correctly handled', $ThisFileInfo['error'], $check, true );
876
- if( $check ) {
877
- $video_info['Warning'] = 'Video checker doesn\'t support this format.';
878
- } else {
879
- $video_errors[] = implode( '<br />', $ThisFileInfo['error'] );
880
- }
881
- }
882
-
883
- if( isset($ThisFileInfo['fileformat']) ) {
884
- $video_info['Format'] = $ThisFileInfo['fileformat'];
885
- }
886
-
887
- if( isset($ThisFileInfo['quicktime']) ) {
888
- if( !isset($ThisFileInfo['quicktime']['moov']) ) {
889
- $video_warnings[] = 'Video meta data (moov-atom) not found at the start of the file! Please move the meta data to the start of video, otherwise it might have a slow start up time. Plese check the "How do I fix the bad metadata (moov) position?" question in <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq" target="_blank">FAQ</a>.';
890
- } else {
891
- if( $ThisFileInfo['quicktime']['moov']['offset'] > 1024 ) {
892
- $video_warnings[] = 'Meta Data (moov) not found at the start of the file (found at '. number_format( $ThisFileInfo['quicktime']['moov']['offset'] ).' byte)! Please move the meta data to the start of video, otherwise it might have a slow start up time. Plese check the "How do I fix the bad metadata (moov) position?" question in <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq" target="_blank">FAQ</a>.';
893
- } else {
894
- $video_info['Moov position'] = $ThisFileInfo['quicktime']['moov']['offset'];
895
- }
896
-
897
- /*if( isset($ThisFileInfo['quicktime']['moov']['subatoms']) ) {
898
- foreach( $ThisFileInfo['quicktime']['moov']['subatoms'] AS $subatom ) {
899
- if( $subatom['hierarchy'] == 'trak' ) {
900
-
901
- }
902
- }
903
- }*/
904
-
905
- fv_wp_flowplayer_array_search_by_item( 'stts', $ThisFileInfo, $stts );
906
- if( isset($stts[0]) && $stts[0]['number_entries'] > 1 ) {
907
- $video_info['Seek points'] = $stts[0]['number_entries'].' (stts)';
908
- } else {
909
- if( isset($stts[0]['time_to_sample_table'][0]['sample_count']) ) {
910
- $video_info['Seek points'] = $stts[0]['time_to_sample_table'][0]['sample_count'].' (stts sample count)';
911
- } else {
912
- //$video_warnings[] = 'Only one seeking point found, it might be slow to seek in the video.'; // not so useful
913
- }
914
- }
915
-
916
- }
917
- }
918
-
919
- if( isset($ThisFileInfo['audio']['streams']) ) {
920
- $count_streams = count( $ThisFileInfo['audio']['streams'] );
921
- if( $count_streams == 1 ) {
922
- $video_info['Audio'] = $count_streams.' stream, ';
923
- } else {
924
- $video_info['Audio'] = $count_streams.' streams, ';
925
- }
926
- foreach( $ThisFileInfo['audio']['streams'] AS $stream ) {
927
- foreach( array( 'dataformat', 'codec', 'sample_rate', 'channels', 'bits_per_sample', 'channelmode' ) AS $item ) {
928
- if( isset( $stream[$item] ) ) {
929
- $add = $stream[$item];
930
- switch( $item ) {
931
- case 'codec' : $add = '('.$add.')'; break;
932
- case 'sample_rate' : $add .= 'Hz, '; break;
933
- case 'bits_per_sample' : $add .= 'bit, '; break;
934
- case 'channels' : $add .= ' channels, '; break;
935
- }
936
- $video_info['Audio'] .= $add.' ';
937
- }
938
- }
939
- $video_info['Audio'] .= '|';
940
- }
941
- $video_info['Audio'] = trim( $video_info['Audio'], '|' );
942
- }
943
-
944
- $video_info['Video'] = array();
945
- if( isset($ThisFileInfo['video']['streams']) ) {
946
- $count_streams = count( $ThisFileInfo['video']['streams'] );
947
- if( $count_streams == 1 ) {
948
- $video_info['Video'] = $count_streams.' stream, ';
949
- } else {
950
- $video_info['Video'] = $count_streams.' streams, ';
951
- }
952
- foreach( $ThisFileInfo['video']['streams'] AS $stream ) {
953
- foreach( array( 'dataformat', 'resolution_x', 'resolution_y', 'frame_rate' ) AS $item ) {
954
- if( isset( $stream[$item] ) ) {
955
- $add = $stream[$item];
956
- switch( $item ) {
957
- case 'resolution_x' : $add .= ' x'; break;
958
- case 'resolution_y' : $add .= ', '; break;
959
- case 'frame_rate' : $add .= ' fps '; break;
960
- }
961
- $video_info['Video'] .= $add.' ';
962
- }
963
- }
964
- $video_info['Video'] .= '|';
965
- }
966
- $video_info['Video'] = trim( $video_info['Video'], '|' );
967
 
968
- if( isset($ThisFileInfo['video']['bitrate']) ) {
969
- $video_info['Video'] .= number_format( ceil($ThisFileInfo['video']['bitrate']/1024) ).'Kbps ';
970
- }
971
- }
972
-
973
- if( isset($ThisFileInfo['video']['fourcc']) ) {
974
- if( !isset($video_info['Video']) ) $video_info['Video'] = array();
975
- $video_info['Video'][] .= $ThisFileInfo['video']['fourcc'].' codec';
976
- }
977
-
978
- if( isset($ThisFileInfo['quicktime']['ftyp']['signature']) ) {
979
- $video_info['Video'][] .= $ThisFileInfo['quicktime']['ftyp']['signature'].' file type ';
980
- if( strcasecmp( trim($ThisFileInfo['quicktime']['ftyp']['signature']), 'M4V' ) === 0 && preg_match( '~.m4v$~', $remotefilename ) ) {
981
- $m4v_note_addition = false;
982
- /*if( $fv_fp->conf['engine'] == 'default' ) {
983
- $m4v_note_addition = ' Currently you are using the "Default (mixed)" <a href="'.site_url().'/wp-admin/options-general.php?page=fvplayer">Preferred Flowplayer engine</a> setting, so Firefox on Windows will always use Flash for M4V and will play fine.';
984
- } */
985
- $video_errors[] = 'We recommend that you change file extension of M4V videos to MP4, to avoid issues with Firefox on PC. '.$m4v_note_addition;
986
- }
987
- }
988
-
989
- if( isset($ThisFileInfo['video']['codec']) && $ThisFileInfo['video']['codec'] == 'MPEG-1' ) {
990
- $video_info['Video'] = 'MPEG-1 Codec';
991
- $video_errors[] = 'MPEG-1 is not a valid HTML5 video format. You need to re-encode the video into MP4.';
992
- }
993
-
994
- if( isset($video_info['Video']) && is_array($video_info['Video']) ) {
995
- $video_info['Video'] = implode( ', ', $video_info['Video'] );
996
-
997
- $video_format_info = array( 'avc1' => 'H.264 Encoder', 'mp42' => 'MS-MPEG4 v2 Decoder' );
998
- foreach( $video_format_info AS $key => $item ) {
999
- $video_info['Video'] = str_replace( $key, $key.' ('.$item.')', $video_info['Video'] );
1000
- }
1001
- }
1002
-
1003
-
1004
  }
1005
  } // end is_wp_error check
1006
 
1007
  } // end isset($media)
1008
-
1009
-
1010
- if( $video_errors ) {
1011
- foreach( $video_errors AS $key => $item ) {
1012
- if( preg_match( '!Atom at offset \d+ claims to go beyond end-of-file!', $item ) ) {
1013
- unset( $video_errors[$key] ); // we are not interested in this particular warning, as we don't download the full file
1014
- }
1015
- }
1016
- }
1017
-
1018
-
1019
- if( $video_errors ) {
1020
- $message_items = array();
1021
- foreach( $video_errors AS $key => $item ) {
1022
- if( $item && stripos( $item, '</p>' ) === false ) {
1023
- $item = '<p><strong>Error</strong>: '.$item.'</p>';
1024
- }
1025
- $message_items[] = $item;
1026
- }
1027
- $message .= implode("\n", $message_items);
1028
- }
1029
-
1030
- if( $video_warnings ) {
1031
- $message_items = array();
1032
- foreach( $video_warnings AS $key => $item ) {
1033
- if( $item && stripos( $item, '</p>' ) === false ) {
1034
- $item = '<p><strong>Warning</strong>: '.$item.'</p>';
1035
- }
1036
- $message_items[] = $item;
1037
- }
1038
- $message .= implode("\n", $message_items);
1039
- }
1040
-
1041
-
1042
- if( isset($video_info) ) {
1043
- $message_items = array();
1044
- foreach( $video_info AS $key => $item ) {
1045
- $message_item = '';
1046
- if( !is_int($key) ) {
1047
- $message_item .= $key.': ';
1048
- }
1049
- $message_item .= '<tt>'.$item.'</tt>';
1050
- $message_items[] = $message_item;
1051
- }
1052
- $message .= '<p>'.implode("<br />\n", $message_items).'</p>';
1053
- }
1054
-
1055
- $message = '<div class="mail-content-notice">'.$message.'</div>';
1056
-
1057
- if( isset($ThisFileInfo) && $ThisFileInfo ) {
1058
- $more_info = var_export($ThisFileInfo,true);
1059
-
1060
- $note = '(Note: read about remote file analysis in <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq">FAQ</a>)';
1061
- $more_info = str_replace( array('Unknown QuickTime atom type', 'Atom at offset'), array($note.' Unknown QuickTime atom type', $note.' Atom at offset'), $more_info );
1062
-
1063
- $lines = explode( "\n", $more_info );
1064
-
1065
- $depth = 0;
1066
- $new_info = '<p>'.$note.'</p><div class="fv-wp-flowplayer-notice-parsed level-0">';
1067
- foreach( $lines AS $line ) {
1068
- $class = ( $depth > 0 ) ? ' indent' : '';
1069
-
1070
- if( strcmp( trim($line), 'array (' ) == 0 ) {
1071
- if( $depth == 0 ) {
1072
- $new_info .= '<div class="fv-wp-flowplayer-notice-parsed level-0 row close"></div>';
1073
- }
1074
- $depth++;
1075
- $new_info .= '<div class="fv-wp-flowplayer-notice-parsed level-'.$depth.''.$class.'">';
1076
- continue;
1077
- }
1078
- if( strcmp( trim($line), '),' ) == 0 ) {
1079
- $depth--;
1080
- $new_info .= '</div><!-- .level-'.$depth.' -->';
1081
- $new_info .= '<div class="fv-wp-flowplayer-notice-parsed level-'.$depth.' row close"></div>';
1082
- continue;
1083
- }
1084
-
1085
-
1086
- if( $depth > 7 ) {
1087
- $style = ' fv-wp-fp-hidden';
1088
- } else {
1089
- $style = '';
1090
- }
1091
-
1092
- $line_i = explode( " => ", trim($line), 2 );
1093
- if( !$line_i ) {
1094
- continue;
1095
- }
1096
-
1097
- $line_html = '<div class="row'.$class.$style.'"><span>'.ucfirst( str_replace( "' =>", '', trim($line_i[0],"' ")) ).'</span><span class="value">'.( (isset($line_i[1])) ? trim(rtrim($line_i[1],", "),"' ") : '' ).'</span><div style="clear:both;"></div></div>';
1098
-
1099
- $new_info .= $line_html."\n";
1100
- }
1101
- $new_info .= '</div>';
1102
-
1103
- }
1104
-
1105
- $message .= '<div class="support-'.$random.'">';
1106
- $message .= '<textarea id="wpfp_support_'.$random.'" class="wpfp_message_field" onclick="if( this.value == \'Enter your comment\' ) this.value = \'\'" style="width: 98%; height: 150px">Enter your comment</textarea>';
1107
- $message .= '<p><a class="techinfo" href="#" onclick="jQuery(\'.more-'.$random.'\').toggle(); return false">Technical info</a> <img id="wpfp_spin_'.$random.'" src="'.site_url().'/wp-includes/images/wpspin.gif" style="display: none; " /> <input type="button" onclick="fv_wp_flowplayer_admin_support_mail(\''.$random.'\', this); return false" value="Send report to Foliovision" /></p>';
1108
- $message .= '</div>';
1109
- $message .= '<div class="more-'.$random.' mail-content-details" style="display: none; "><p>Plugin version: '.$fv_wp_flowplayer_ver.'</p>'.$new_info.'</div>';
1110
-
1111
-
1112
- if( count($video_errors) > 0 ) {
1113
- $issues_text = '<span class="vid-issues">Video Issues</span>';
1114
- } else if( count($video_warnings) ) {
1115
- $issues_text = '<span class="vid-warning">Video Warnings</span>';
1116
- } else {
1117
- $issues_text = '<span class="vid-ok">Video OK</span>';
1118
- }
1119
- $message = "<div onclick='fv_wp_flowplayer_admin_show_notice(\"$random\", this.parent); return false' class='fv_wp_flowplayer_notice_head'>Report Issue</div><small>Admin: <a class='fv_wp_flowplayer_dialog_link' href='#' onclick='fv_wp_flowplayer_admin_show_notice(\"$random\", this); return false'>$issues_text</a></small><div id='fv_wp_fp_notice_$random' class='fv_wp_fp_notice_content' style='display: none;'>$message</div>\n";
1120
-
1121
- $json = @json_encode( array( $message, count( $video_errors ), count( $video_warnings ) ) );
1122
- $last_error = ( function_exists('json_last_error') ) ? json_last_error() : true;
1123
-
1124
- if( $last_error ) {
1125
- if( function_exists('mb_check_encoding') && function_exists('utf8_encode') ) {
1126
- if(!mb_check_encoding($message, 'UTF-8')) {
1127
- $message = utf8_encode($message);
1128
- }
1129
- } else {
1130
- $message = htmlentities( $message, ENT_QUOTES, 'utf-8', FALSE);
1131
- $message = ( $message ) ? $message : 'Admin: Error parsing JSON';
1132
- }
1133
-
1134
- $json = json_encode( array( $message, count( $video_errors ), count( $video_warnings ) ) );
1135
- $last_error = ( function_exists('json_last_error') ) ? json_last_error() : false;
1136
- if( $last_error ) {
1137
- echo json_encode( array( 'Admin: JSON error: '.$last_error, count( $video_errors ), count( $video_warnings ) ) );
1138
- } else {
1139
- echo $json;
1140
- }
1141
- } else {
1142
- echo $json;
1143
- }
1144
- die();
1145
-
1146
- } else {
1147
- die('-1');
1148
  }
1149
  }
1150
 
@@ -1166,12 +950,7 @@ function fv_wp_flowplayer_check_script_version( $url ) {
1166
  }
1167
 
1168
 
1169
- function fv_wp_flowplayer_check_jquery_version( $url, &$array, $key ) {
1170
- if( preg_match( '!/jquery(\.[a-zA-Z]{2,}|-[a-zA-Z]{3,})[^/]*?\.js!', $url ) ) { // jquery.ui.core.min.js, jquery-outline-1.1.js
1171
- unset( $array[$key] );
1172
- return 2;
1173
- }
1174
-
1175
  $url_mod = preg_replace( '!\?.+!', '', $url );
1176
  if( preg_match( '!(\d+.[\d\.]+)!', $url_mod, $version ) && $version[1] ) {
1177
  if( version_compare($version[1], '1.7.1') == -1 ) {
@@ -1200,12 +979,16 @@ function fv_wp_flowplayer_check_files() {
1200
  if( stripos( $_SERVER['HTTP_REFERER'], home_url() ) === 0 ) {
1201
  global $wpdb;
1202
  define('VIDEO_DIR', '/videos/');
 
 
 
 
1203
 
1204
- $videos1 = $wpdb->get_results( "SELECT ID, post_content FROM $wpdb->posts WHERE post_type != 'revision' AND post_content LIKE '%[flowplayer %'" );
1205
- $videos2 = $wpdb->get_results( "SELECT ID, post_content FROM $wpdb->posts WHERE post_type != 'revision' AND post_content LIKE '%[fvplayer %'" );
1206
 
1207
  $videos = array_merge( $videos1, $videos2 );
1208
-
1209
  $source_servers = array();
1210
 
1211
  $shortcodes_count = 0;
@@ -1243,77 +1026,49 @@ function fv_wp_flowplayer_check_files() {
1243
 
1244
  $ok = array();
1245
  $errors = array();
1246
-
1247
  $count = 0;
1248
  foreach( $source_servers AS $server => $videos ) {
1249
-
1250
- //echo $server."\n";
1251
-
 
 
 
 
 
 
 
 
1252
  if( stripos( trim($videos[0]['src']), 'rtmp://' ) === false ) {
1253
- $headers = get_headers( trim($videos[0]['src']) );
1254
- }
1255
- if( isset($headers) && $headers ) {
1256
-
1257
- $posts_links = '';
1258
- foreach( $videos AS $video ) {
1259
- $posts_links .= '<a href="'.home_url().'?p='.$video['post_id'].'">'.$video['post_id'].'</a> ';
1260
- }
1261
-
1262
- foreach( $headers AS $line ) {
1263
- if( stripos( $line, 'Content-Type:' ) !== FALSE ) {
1264
- preg_match( '~Content-Type: (\S+)$~', $line, $match );
1265
- $mime_matched = ( isset($match[1]) ) ? $match[1] : '';
1266
-
1267
- if(
1268
- ( !preg_match( '~video/mp4$~', $line ) && stripos( $videos[0]['src'], '.mp4' ) !== FALSE ) ||
1269
- ( !preg_match( '~video/x-m4v$~', $line ) && stripos( $videos[0]['src'], '.m4v' ) !== FALSE )
1270
- ) {
1271
- if( strpos( $server, 'amazonaws' ) !== false ) {
1272
- $fix = '<p>It\'s important to set this correctly, otherwise the videos will not play in HTML5 mode in Internet Explorer 9 and 10.</p><blockquote><code>Using your Amazon AWS Management Console, you can go though your videos and find file content type under the "Metadata" tab in an object\'s "Properties" pane and fix it to "video/mp4" for MP4 and "video/x-m4v" for M4V files.</code></blockquote>';
1273
- } else {
1274
- $fix = '<p>It\'s important to set this correctly, otherwise the videos will not play in HTML5 mode in Internet Explorer 9 and 10.</p><p>Make sure you put this into your .htaccess file, or ask your server admin to upgrade the web server mime type configuration:</p> <blockquote><pre><code>AddType video/mp4 .mp4
1275
- AddType video/webm .webm
1276
- AddType video/ogg .ogv
1277
- AddType application/x-mpegurl .m3u8
1278
- AddType video/x-m4v .m4v
1279
- # hls transport stream segments:
1280
- AddType video/mp2t .ts</code></pre></blockquote>';
1281
- }
1282
-
1283
- $errors[] = 'Server <code>'.$server.'</code> uses bad mime type <code>'.$mime_matched.'</code> for MP4 or M4V videos! (<a href="#" onclick="jQuery(\'#fv-flowplayer-warning-'.$count.'\').toggle(); return false">click to see a list of posts</a>) (<a href="#" onclick="jQuery(\'#fv-flowplayer-info-'.$count.'\').toggle(); return false">show fix</a>) <div id="fv-flowplayer-warning-'.$count.'" style="display: none; ">'.$posts_links.'</div> <div id="fv-flowplayer-info-'.$count.'" style="display: none; ">'.$fix.'</div>';
1284
- } else if(
1285
- ( !preg_match( '~video/webm$~', $line ) && stripos( $videos[0]['src'], '.webm' ) !== FALSE )
1286
- ) {
1287
- if( strpos( $server, 'amazonaws' ) !== false ) {
1288
- $fix = '<p>It\'s important to set this correctly, otherwise the videos will not play in older Firefox.</p><blockquote><code>Using your Amazon AWS Management Console, you can go though your videos and find file content type under the "Metadata" tab in an object\'s "Properties" pane and fix it to "video/webm" for WEBM videos.</code></blockquote>';
1289
- } else {
1290
- $fix = '<p>It\'s important to set this correctly, otherwise the videos will not play in older Firefox.</p><p>Make sure you put this into your .htaccess file, or ask your server admin to upgrade the web server mime type configuration:</p> <blockquote><pre><code>AddType video/mp4 .mp4
1291
- AddType video/webm .webm
1292
- AddType video/ogg .ogv
1293
- AddType application/x-mpegurl .m3u8
1294
- AddType video/x-m4v .m4v
1295
- # hls transport stream segments:
1296
- AddType video/mp2t .ts</code></pre></blockquote>';
1297
- }
1298
-
1299
- $errors[] = 'Server <code>'.$server.'</code> uses bad mime type <code>'.$mime_matched.'</code> for MP4 or M4V videos! (<a href="#" onclick="jQuery(\'#fv-flowplayer-warning-'.$count.'\').toggle(); return false">click to see a list of posts</a>) (<a href="#" onclick="jQuery(\'#fv-flowplayer-info-'.$count.'\').toggle(); return false">show fix</a>) <div id="fv-flowplayer-warning-'.$count.'" style="display: none; ">'.$posts_links.'</div> <div id="fv-flowplayer-info-'.$count.'" style="display: none; ">'.$fix.'</div>';
1300
- } else if( stripos( $videos[0]['src'], '.mp4' ) !== FALSE || stripos( $videos[0]['src'], '.m4v' ) !== FALSE ) {
1301
- $ok[] = 'Server <code>'.$server.'</code> appears to serve correct mime type <code>'.$mime_matched.'</code> for MP4 and M4V videos.';
1302
- }
1303
- }
1304
- }
1305
-
1306
- $count++;
1307
- }
1308
-
1309
- if( $server == 'http://lifeinamovie.com' ) {
1310
- //break;
1311
- }
1312
-
1313
  }
 
 
 
 
1314
 
1315
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1316
- echo json_encode($output);
1317
  die();
1318
  }
1319
  die('-1');
@@ -1329,18 +1084,14 @@ function fv_wp_flowplayer_check_template() {
1329
  if( is_wp_error( $response ) ) {
1330
  $error_message = $response->get_error_message();
1331
  $output = array( 'error' => $error_message );
1332
- } else {
1333
-
1334
- $active_plugins = get_option( 'active_plugins' );
1335
  foreach( $active_plugins AS $plugin ) {
1336
  if( stripos( $plugin, 'wp-minify' ) !== false ) {
1337
  $errors[] = "You are using <strong>WP Minify</strong>, so the script checks would not be accurate. Please check your videos manually.";
1338
- $wp_minify_options = get_option('wp_minify');
1339
- if( isset($wp_minify_options['js_in_footer']) && $wp_minify_options['js_in_footer'] ) {
1340
- $errors[] = "Please make sure that you turn off Settings -> WP Minify -> 'Place Minified JavaScript in footer'.";
1341
- }
1342
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1343
- echo json_encode($output);
1344
  die();
1345
  }
1346
  }
@@ -1348,19 +1099,21 @@ function fv_wp_flowplayer_check_template() {
1348
  if( function_exists( 'w3_instance' ) && $minify = w3_instance('W3_Plugin_Minify') ) {
1349
  if( $minify->_config->get_boolean('minify.js.enable') ) {
1350
  $errors[] = "You are using <strong>W3 Total Cache</strong> with JS Minify enabled. The template check might not be accurate. Please check your videos manually.";
 
 
1351
  }
1352
  }
1353
-
1354
- if( stripos($response['body'],'<!--fv-flowplayer-footer-->') === false ) {
1355
- $errors[] = "It appears that your template is not using the wp_footer() hook. Advanced FV Flowplayer functions may not work properly.";
1356
- } else {
1357
- $ok[] = "wp_footer found in your template!";
1358
- }
1359
 
 
 
1360
  $response['body'] = preg_replace( '$<!--[\s\S]+?-->$', '', $response['body'] ); // handle HTML comments
1361
 
1362
  // check Flowplayer scripts
1363
- preg_match_all( '!<script[^>]*?src=[\'"]([^\'"]*?flowplayer[^\'"]*?\.js[^\'"]*?)[\'"][^>]*?>\s*?</script>!', $response['body'], $flowplayer_scripts );
1364
  if( count($flowplayer_scripts[1]) > 0 ) {
1365
  if( count($flowplayer_scripts[1]) > 1 ) {
1366
  $errors[] = "It appears there are <strong>multiple</strong> Flowplayer scripts on your site, your videos might not be playing, please check. There might be some other plugin adding the script.";
@@ -1375,13 +1128,13 @@ function fv_wp_flowplayer_check_template() {
1375
  }
1376
  }
1377
  } else if( count($flowplayer_scripts[1]) < 1 ) {
1378
- $errors[] = "It appears there are <strong>no</strong> Flowplayer scripts on your site, your videos might not be playing, please check. Check your header.php file if it contains wp_head() function call!";
1379
  }
1380
 
1381
 
1382
  // check jQuery scripts
1383
- preg_match_all( '!<script[^>]*?src=[\'"]([^\'"]*?jquery[^\'"]*?\.js[^\'"]*?)[\'"][^>]*?>\s*?</script>!', $response['body'], $jquery_scripts );
1384
- if( count($jquery_scripts[1]) > 0 ) {
1385
  foreach( $jquery_scripts[1] AS $jkey => $jquery_script ) {
1386
  $check = fv_wp_flowplayer_check_jquery_version( $jquery_script, $jquery_scripts[1], $jkey );
1387
  if( $check == - 1 ) {
@@ -1395,28 +1148,28 @@ function fv_wp_flowplayer_check_template() {
1395
  $errors[] = "jQuery library <code>$jquery_script</code> found, but unable to check version, please make sure it's at least 1.7.1.";
1396
  }
1397
  }
 
1398
  if( count($jquery_scripts[1]) > 1 ) {
1399
- $errors[] = "It appears there are <strong>multiple</strong> jQuery libraries on your site, your videos might not be playing, please check.\n";
1400
  }
1401
  } else if( count($jquery_scripts[1]) < 1 ) {
1402
  $errors[] = "It appears there are <strong>no</strong> jQuery library on your site, your videos might not be playing, please check.\n";
1403
  }
1404
 
1405
 
1406
- if( $fv_flowplayer_pos > 0 && $jquery_pos > 0 && $jquery_pos > $fv_flowplayer_pos ) {
1407
  $errors[] = "It appears your Flowplayer JavaScript library is loading before jQuery. Your videos probably won't work. Please make sure your jQuery library is loading using the standard Wordpress function - wp_enqueue_scripts(), or move it above wp_head() in your header.php template.";
1408
  }
1409
-
1410
-
1411
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1412
  }
1413
- echo json_encode($output);
1414
  die();
1415
  }
1416
 
1417
  die('-1');
1418
  }
1419
-
1420
 
1421
  function fv_wp_flowplayer_array_search_by_item( $find, $in_array, &$found, $like = false ) {
1422
  global $fv_wp_flowplayer_array_search_by_item_depth;
@@ -1560,6 +1313,137 @@ function fv_wp_flowplayer_pointers_ajax() {
1560
  }
1561
  die();
1562
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1563
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1564
 
1565
- ?>
 
 
 
 
 
 
 
 
28
  /**
29
  * WP Hooks
30
  */
31
+ add_action('wp_ajax_fv_wp_flowplayer_support_mail', 'fv_wp_flowplayer_support_mail');
32
+ add_action('wp_ajax_fv_wp_flowplayer_activate_extension', 'fv_wp_flowplayer_activate_extension');
33
+ add_action('wp_ajax_fv_wp_flowplayer_check_template', 'fv_wp_flowplayer_check_template');
34
  add_action('wp_ajax_fv_wp_flowplayer_check_files', 'fv_wp_flowplayer_check_files');
35
 
36
  add_action('admin_head', 'flowplayer_head');
105
 
106
 
107
  function flowplayer_activate() {
108
+
109
+ }
110
+
111
+
112
+ function flowplayer_deactivate() {
113
+ if( flowplayer::is_licensed() ) {
114
+ delete_transient( 'fv_flowplayer_license' );
115
+ }
116
+ delete_option( 'fv_flowplayer_extension_install' );
117
  }
118
 
119
 
202
  $serv = $_SERVER['SERVER_NAME'];
203
  $pattern = '/'.$serv.'(.*)/';
204
  preg_match($pattern, $uploaded_video, $matches);
205
+
206
+ // Initialize getID3 engine
207
+ if ( ! class_exists( 'getID3' ) ) {
208
+ require( ABSPATH . WPINC . '/ID3/getid3.php' );
209
+ }
210
  $getID3 = new getID3;
211
  if (empty($matches)) {
212
  $ThisFileInfo = $getID3->analyze(realpath($document_root . $uploaded_video));
396
  $button_tip = 'Insert a Flash Video Player';
397
  $wizard_url = 'media-upload.php?post_id='.$post->ID.'&type=fv-wp-flowplayer';
398
  $button_src = FV_FP_RELATIVE_PATH.'/images/icon.png';
399
+ $img = (!$found) ? '<img src="' . $button_src . '" alt="' . $button_tip . '" />' : '';
 
 
400
  echo '<a title="Add FV WP Flowplayer" href="#" class="fv-wordpress-flowplayer-button" >'.$img.'</a>';
401
  }
402
 
444
 
445
  function fv_wp_flowplayer_admin_notice() {
446
  if( $notices = get_option('fv_wordpress_flowplayer_deferred_notices') ) {
447
+ echo '<div class="updated inline">
448
  <p>'.$notices.'</p>
449
+ </div>';
450
+ delete_option('fv_wordpress_flowplayer_deferred_notices');
451
  }
452
 
453
  $conversion = false; //(bool)get_option('fvwpflowplayer_conversion');
472
  if( $page !== 'post.php' && $page !== 'post-new.php' ) {
473
  return;
474
  }
475
+ wp_register_script('fvwpflowplayer-domwindow', flowplayer::get_plugin_url().'/js/jquery.colorbox-min.js',array('jquery') );
476
  wp_enqueue_script('fvwpflowplayer-domwindow');
477
 
478
+ wp_register_script('fvwpflowplayer-shortcode-editor', flowplayer::get_plugin_url().'/js/shortcode-editor.js',array('jquery') );
479
  wp_enqueue_script('fvwpflowplayer-shortcode-editor');
480
 
481
+ wp_register_style('fvwpflowplayer-domwindow-css', flowplayer::get_plugin_url().'/css/colorbox.css','','1.0','screen');
482
  wp_enqueue_style('fvwpflowplayer-domwindow-css');
483
  }
484
 
507
 
508
  global $fv_fp;
509
  global $fv_wp_flowplayer_ver, $fv_wp_flowplayer_core_ver;
510
+ if(
511
+ preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) &&
512
+ (
513
+ ( isset($fv_fp->conf['key_automatic']) && $fv_fp->conf['key_automatic'] == 'true' ) ||
514
+ ( isset($fv_fp->conf['video_checker_agreement']) && $fv_fp->conf['video_checker_agreement'] == 'true' )
515
+ )
516
+ ) {
517
 
518
  $version = get_option( 'fvwpflowplayer_core_ver' );
519
  if( version_compare( $fv_wp_flowplayer_core_ver, $version ) == 1 ) {
520
  fv_wp_flowplayer_admin_key_update();
521
  }
522
  }
523
+
524
  if(
525
+ isset($fv_fp->conf['disable_videochecker']) && $fv_fp->conf['disable_videochecker'] == 'false' &&
526
+ ( !isset($fv_fp->conf['video_checker_agreement']) || $fv_fp->conf['video_checker_agreement'] != 'true' ) &&
527
+ ( !isset($fv_fp->conf['key_automatic']) || $fv_fp->conf['key_automatic'] != 'true' )
528
  ) {
529
+ $fv_fp->pointer_boxes['fv_flowplayer_video_checker_service'] = array(
530
+ 'id' => '#wp-admin-bar-new-content',
531
+ 'pointerClass' => 'fv_flowplayer_video_checker_service',
532
+ 'heading' => __('FV Player Video Checker', 'fv_flowplayer'),
533
+ 'content' => __("<p>FV Player includes a free video checker which will check your videos for any encoding errors and helps ensure smooth playback of all your videos. To work its magic, our video checker must contact our server.</p><p>Would you like to enable the video encoding checker?</p>", 'fv_flowplayer'),
534
+ 'position' => array( 'edge' => 'top', 'align' => 'center' ),
535
+ 'button1' => __('Allow', 'fv_flowplayer'),
536
+ 'button2' => __('Disable the video checker', 'fv_flowplayer')
537
+ );
538
+ } else {
539
+ if(
540
+ preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) && version_compare( $fv_wp_flowplayer_core_ver, get_option('fvwpflowplayer_core_ver') ) !== 0 &&
541
+ ( !isset($fv_fp->conf['key_automatic']) || $fv_fp->conf['key_automatic'] != 'true' ) &&
542
+ ( !isset($fv_fp->conf['video_checker_agreement']) || $fv_fp->conf['video_checker_agreement'] != 'true' )
543
+ ) {
544
+ $fv_fp->pointer_boxes['fv_flowplayer_key_automatic'] = array(
545
+ 'id' => '#wp-admin-bar-new-content',
546
+ 'pointerClass' => 'fv_flowplayer_key_automatic',
547
+ 'pointerWidth' => 340,
548
+ 'heading' => __('FV Flowplayer License Update', 'fv_flowplayer'),
549
+ 'content' => __('New version of FV Flowplayer core has been installed for your licensed website. Please accept the automatic license key updating (connects to Foliovision servers) or update the key manually by loggin into your Foliovision account.', 'fv_flowplayer'),
550
+ 'position' => array( 'edge' => 'top', 'align' => 'center' ),
551
+ 'button1' => __('Always auto-update', 'fv_flowplayer'),
552
+ 'button2' => __("I'll update it manually", 'fv_flowplayer')
553
+ );
554
+ } else if( version_compare( $fv_wp_flowplayer_core_ver, get_option('fvwpflowplayer_core_ver') ) !== 0 && preg_match( '!^\$\d+!', $fv_fp->conf['key'] ) == 0 ) {
555
+ update_option( 'fvwpflowplayer_core_ver', $fv_wp_flowplayer_core_ver );
556
+ }
557
  }
558
+
559
+ $aOptions = get_option( 'fvwpflowplayer' );
560
+ if( !isset($aOptions['version']) || version_compare( $fv_wp_flowplayer_ver, $aOptions['version'] ) ) {
561
+ update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer upgraded - please click "Check template" and "Check videos" for automated check of your site at <a href="'.site_url().'/wp-admin/options-general.php?page=fvplayer">the settings page</a> for automated checks!' );
562
 
563
+ $aOptions['version'] = $fv_wp_flowplayer_ver;
564
+ update_option( 'fvwpflowplayer', $aOptions );
565
+ $fv_fp->css_writeout();
566
+ }
567
 
568
  if( isset($_GET['page']) && $_GET['page'] == 'fvplayer' ) {
569
  wp_enqueue_script('common');
570
  wp_enqueue_script('wp-lists');
571
  wp_enqueue_script('postbox');
572
  }
573
+
574
+
575
+ if( flowplayer::is_licensed() ) {
576
+ if ( false === ( $aCheck = get_transient( 'fv_flowplayer_license' ) ) ) {
577
+ $aCheck = fv_wp_flowplayer_license_check( array('action' => 'check') );
578
+ if( $aCheck ) {
579
+ set_transient( 'fv_flowplayer_license', $aCheck, 60*60*24 );
580
+ } else {
581
+ set_transient( 'fv_flowplayer_license', json_decode(json_encode( array('error' => 'Error checking license') ), FALSE), 60*60*24 );
582
+ }
583
+ }
584
+
585
+ $aCheck = get_transient( 'fv_flowplayer_license' );
586
+ $aInstalled = get_option('fv_flowplayer_extension_install');
587
+ if(
588
+ isset($aCheck->valid) && $aCheck->valid &&
589
+ (
590
+ !isset($aInstalled['fv_player_pro']) ||
591
+ ( isset($_REQUEST['nonce_fv_player_pro_install']) && wp_verify_nonce( $_REQUEST['nonce_fv_player_pro_install'], 'fv_player_pro_install') )
592
+ )
593
+ ) {
594
+ fv_wp_flowplayer_install_extension('fv_player_pro');
595
+ }
596
+
597
+ }
598
  }
599
 
600
 
601
  function fv_wp_flowplayer_admin_key_update() {
602
+ global $fv_fp, $fv_wp_flowplayer_core_ver;
 
 
 
 
 
 
 
603
 
604
+ $data = fv_wp_flowplayer_license_check( array('action' => 'key_update') );
605
+ if( isset($data->domain) ) { // todo: test
606
  if( $data->domain && $data->key && stripos( home_url(), $data->domain ) !== false ) {
607
  $fv_fp->conf['key'] = $data->key;
608
  update_option( 'fvwpflowplayer', $fv_fp->conf );
610
  return true;
611
  }
612
  } else {
613
+ update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer License upgrade failed - please check if you are running the plugin on your licensed domain.' );
614
+ update_option( 'fvwpflowplayer_core_ver', $fv_wp_flowplayer_core_ver );
615
  return false;
616
  }
617
  }
618
 
619
 
620
+ function fv_wp_flowplayer_license_check( $aArgs ) {
621
+ global $fv_wp_flowplayer_ver, $fv_wp_flowplayer_core_ver;
622
+
623
+ $args = array(
624
+ 'body' => array( 'plugin' => 'fv-wordpress-flowplayer', 'version' => $fv_wp_flowplayer_ver, 'core_ver' => $fv_wp_flowplayer_core_ver, 'type' => home_url(), 'action' => $aArgs['action'] ),
625
+ 'timeout' => 20,
626
+ 'user-agent' => 'fv-wordpress-flowplayer-'.$fv_wp_flowplayer_ver.' ('.$fv_wp_flowplayer_core_ver.')'
627
+ );
628
+ $resp = wp_remote_post( 'http://foliovision.com/?fv_remote=true', $args );
629
+
630
+ if( isset($resp['body']) && $resp['body'] && $data = json_decode( preg_replace( '~[\s\S]*?<FVFLOWPLAYER>(.*?)</FVFLOWPLAYER>[\s\S]*?~', '$1', $resp['body'] ) ) ) {
631
+ return $data;
632
+ } else {
633
+ return false;
634
+ }
635
+ }
636
+
637
+
638
  function fv_wp_flowplayer_edit_form_after_editor( ) {
639
  include dirname( __FILE__ ) . '/../view/wizard.php';
640
  }
658
  <tr class="plugin-update-tr fv-wordpress-flowplayer-tr">
659
  <td class="plugin-update colspanchange" colspan="3">
660
  <div class="update-message">
661
+ <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/download">All Licenses 20% Off</a> - Easter sale!
662
  </div>
663
  </td>
664
  </tr>
668
  }
669
 
670
 
671
+ function fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random, $args = false ) {
672
  global $fv_fp;
673
+
674
+ $args = wp_parse_args( $args, array( 'talk_bad_mime' => 'Video served with a bad mime type' , 'wrap'=>'p' ) );
675
 
676
+ $sOutput = '';
677
 
678
+ $bFatal = false;
679
  if( $headers && $headers['response']['code'] == '404' ) {
680
+ $video_errors[] = 'File not found (HTTP 404)!';
681
+ $bFatal = true;
682
  } else if( $headers && $headers['response']['code'] == '403' ) {
683
+ $video_errors[] = 'Access to video forbidden (HTTP 403)!';
684
+ $bFatal = true;
685
  } else if( $headers && $headers['response']['code'] != '200' && $headers['response']['code'] != '206' ) {
686
+ $video_errors[] = 'Can\'t check the video (HTTP '.$headers['response']['code'].')!';
687
+ $bFatal = true;
688
  } else {
689
 
690
  if(
691
  ( !isset($headers['headers']['accept-ranges']) || $headers['headers']['accept-ranges'] != 'bytes' ) &&
692
  !isset($headers['headers']['content-range'])
693
  ) {
694
+ $video_errors[] = 'Server does not support HTTP range requests! Please check <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq#getting-error-about-range-requests">our FAQ</a>.';
695
  }
696
 
697
  if(
723
  AddType video/mp4 .mov
724
  # hls transport stream segments:
725
  AddType video/mp2t .ts</pre>
726
+ <p>If you are using Microsoft IIS, you need to use the IIS manager. Check our <a href="http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer/faq#video-doesnt-play-internet-explorer" target="_blank">FAQ</a> for more info.</p>
727
  </div>';
728
 
729
+ $sOutput = ( $args['wrap'] ) ? '<'.$args['wrap'].'>' : '';
730
+ $sOutput .= '<strong>Bad mime type</strong>: '.$args['talk_bad_mime'].' <tt>'.$headers['headers']['content-type'].'</tt>!'.$meta_note_addition.' (<a href="#" onclick="jQuery(\'.fix-meta-'.$random.'\').toggle(); return false">show fix</a>)';
731
+ $sOutput .= ( $args['wrap'] ) ? '</'.$args['wrap'].'>' : '';
732
+ $sOutput .= $fix;
733
+ $video_errors[] = $sOutput;
734
  }
735
  }
736
+
737
+ return array( $video_errors, (isset($headers['headers']['content-type'])) ? $headers['headers']['content-type'] : '', $bFatal );
738
  }
739
 
740
 
742
  function fv_wp_flowplayer_http_api_curl( $handle ) {
743
  curl_setopt( $handle, CURLOPT_NOBODY, true );
744
  }
745
+
746
+
747
+ function fv_wp_flowplayer_http( $sURL, $args ) {
748
+ global $fv_wp_flowplayer_ver;
749
+
750
+ $args = wp_parse_args( $args, array( 'file' => false, 'size' => 2097152, 'quick_check' => false ) );
751
+ extract($args);
752
+
753
+ $iTimeout = ($quick_check) ? 10 : 20;
754
+
755
+ $ch = curl_init();
756
+ curl_setopt( $ch, CURLOPT_URL, $sURL );
757
+ curl_setopt( $ch, CURLOPT_RANGE, '0-'.$size );
758
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
759
+ if( !@ini_get('open_basedir') ) {
760
+ @curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
761
+ }
762
+ curl_setopt( $ch, CURLOPT_HEADER, true );
763
+ curl_setopt( $ch, CURLOPT_VERBOSE, 1 );
764
+ curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $iTimeout );
765
+ curl_setopt( $ch, CURLOPT_TIMEOUT, $iTimeout );
766
+ curl_setopt( $ch, CURLOPT_USERAGENT, 'FV Flowplayer video checker/'.$fv_wp_flowplayer_ver);
767
+
768
+ $data = curl_exec($ch);
769
+
770
+ $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
771
+ $header = substr($data, 0, $header_size);
772
+ $body = substr($data, $header_size);
773
+
774
+ if( $file ) {
775
+ file_put_contents( $file, $body);
776
+ }
777
+ $sError = ($ch == false) ? 'CURL Error: '.curl_error ( $ch) : false;
778
+ if( curl_errno($ch) == 28 ) {
779
+ $sError .= "Connection timeout, can't check the video.";
780
+ } else if(!curl_errno($ch) ) {
781
+ $aInfo = curl_getinfo($ch);
782
+ if( $aInfo['total_time'] > $iTimeout*0.9 ) {
783
+ $sError .= "Connection timeout, can't check the video.";
784
+ }
785
+ }
786
+ curl_close($ch);
787
+
788
+ return array( $header, $sError );
789
+ }
790
 
791
 
792
  function fv_wp_flowplayer_check_mimetype( $URLs = false, $meta = false ) {
804
 
805
  if( isset($URLs) ) {
806
  $all_sources = $URLs;
807
+
 
 
 
 
 
 
808
  foreach( $all_sources AS $source ) {
809
  if( preg_match( '!^rtmp://!', $source, $match ) ) {
810
  $found_rtmp = true;
811
+ } else if( !isset($media) && !preg_match( '!\.(m3u8|m3u|avi)$!', $source) ) {
812
+ $media = $source;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
  }
814
+ }
815
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
816
  //$random = rand( 0, 10000 );
817
  $random = (isset($_POST['hash'])) ? trim($_POST['hash']) : false;
818
 
819
  if( isset($media) ) {
820
  $remotefilename = $media;
821
+ $remotefilename_encoded = flowplayer::get_encoded_url($remotefilename);
822
+
 
 
 
 
 
 
 
 
 
 
 
823
  if( $fv_fp->is_secure_amazon_s3($remotefilename_encoded) || 1>0 ) { // skip headers check for Amazon S3, as it's slow
824
  $headers = false;
825
  } else {
826
  $headers = wp_remote_head( trim( str_replace(' ', '%20', $remotefilename_encoded ) ), array( 'method' => 'GET', 'redirection' => 3 ) );
827
  }
828
 
829
+ $bValidFile = true;
830
  if( is_wp_error($headers) ) {
831
  $video_errors[] = 'Error checking '.$media.'!<br />'.print_r($headers,true);
832
  } else {
833
  if( $headers ) {
834
+ list( $aVideoErrors, $sContentType, $bFatal ) = fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random );
835
+ if( $bFatal ) {
836
+ $bValidFile = false;
837
+ }
838
+ if( $aVideoErrors ) {
839
+ $video_errors = array_merge( $video_errors, $aVideoErrors );
840
+ }
841
  }
842
 
843
  if( function_exists('is_utf8') && is_utf8($remotefilename) ) {
848
  $video_warnings[] = 'Detailed video check is not available with PHP Safe Mode On. Please contact your webhost support.';
849
  } else {
850
 
851
+ if ( ! class_exists( 'getID3' ) ) {
852
+ require( ABSPATH . WPINC . '/ID3/getid3.php' );
853
+ }
854
  $getID3 = new getID3;
855
 
 
 
 
856
  if( !function_exists('curl_init') ) {
857
  $video_errors[] = 'cURL for PHP not found, please contact your server administrator.';
858
+ } else {
859
  $message = '<p>Analysis of <a class="bluelink" target="_blank" href="'.esc_attr($remotefilename_encoded).'">'.$remotefilename_encoded.'</a></p>';
 
860
 
861
  // taken from: http://www.getid3.org/phpBB3/viewtopic.php?f=3&t=1141
862
  $upload_dir = wp_upload_dir();
863
  $localtempfilename = trailingslashit( $upload_dir['basedir'] ).'fv_flowlayer_tmp_'.md5(rand(1,999)).'_'.basename( substr($remotefilename_encoded,0,32) );
864
+
865
  $out = fopen( $localtempfilename,'wb' );
866
  if( $out ) {
867
+ list( $header, $sHTTPError ) = fv_wp_flowplayer_http( $remotefilename_encoded, array( 'file' => $localtempfilename ) );
868
+
869
 
870
+ if( $sHTTPError ) {
871
+ $video_errors[] = $sHTTPError;
872
+ $bValidFile = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  }
 
874
  fclose($out);
875
 
876
  if( !$headers ) {
877
  $headers = WP_Http::processHeaders( $header );
878
+
879
+ list( $aVideoErrors, $sContentType, $bFatal ) = fv_wp_flowplayer_check_headers( $headers, $remotefilename, $random );
880
+ if( $bFatal ) {
881
+ $bValidFile = false;
882
+ }
883
+
884
+ if( $aVideoErrors ) {
885
+ $video_errors = array_merge( $video_errors, $aVideoErrors );
886
+ }
887
+
888
  if( isset($hearders['headers']['server']) && $hearders['headers']['server'] == 'AmazonS3' && $headers['response']['code'] == '403' ) {
889
  $error = new SimpleXMLElement($body);
890
 
896
 
897
  }
898
  }
 
 
899
 
900
+ if( $bValidFile ) {
901
+ $ThisFileInfo = $getID3->analyze( $localtempfilename );
902
+ }
903
  if( !@unlink($localtempfilename) ) {
904
  $video_errors[] = 'Can\'t remove temporary file for video analysis in <tt>'.$localtempfilename.'</tt>!';
905
+ }
906
  } else {
907
  $video_errors[] = 'Can\'t create temporary file for video analysis in <tt>'.$localtempfilename.'</tt>!';
908
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  }
910
+
911
 
912
  /*
913
  Only check file length
914
  */
915
  if( isset($meta_action) && $meta_action == 'check_time' ) {
916
+
917
  if( isset($ThisFileInfo['playtime_seconds']) ) {
918
  $time = $ThisFileInfo['playtime_seconds'];
919
  }
924
  update_post_meta( $post->ID, '_fv_flowplayer', $fv_flowplayer_meta );
925
  return;
926
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
927
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
  }
929
  } // end is_wp_error check
930
 
931
  } // end isset($media)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
932
  }
933
  }
934
 
950
  }
951
 
952
 
953
+ function fv_wp_flowplayer_check_jquery_version( $url, &$array, $key ) {
 
 
 
 
 
954
  $url_mod = preg_replace( '!\?.+!', '', $url );
955
  if( preg_match( '!(\d+.[\d\.]+)!', $url_mod, $version ) && $version[1] ) {
956
  if( version_compare($version[1], '1.7.1') == -1 ) {
979
  if( stripos( $_SERVER['HTTP_REFERER'], home_url() ) === 0 ) {
980
  global $wpdb;
981
  define('VIDEO_DIR', '/videos/');
982
+
983
+ $bNotDone = false;
984
+ $tStart = microtime(true);
985
+ $tMax = ( @ini_get('max_execution_time') ) ? @ini_get('max_execution_time') - 5 : 25;
986
 
987
+ $videos1 = $wpdb->get_results( "SELECT ID, post_content FROM $wpdb->posts WHERE post_type != 'revision' AND post_status != 'trash' AND post_content LIKE '%[flowplayer %'" );
988
+ $videos2 = $wpdb->get_results( "SELECT ID, post_content FROM $wpdb->posts WHERE post_type != 'revision' AND post_status != 'trash' AND post_content LIKE '%[fvplayer %'" );
989
 
990
  $videos = array_merge( $videos1, $videos2 );
991
+
992
  $source_servers = array();
993
 
994
  $shortcodes_count = 0;
1026
 
1027
  $ok = array();
1028
  $errors = array();
1029
+
1030
  $count = 0;
1031
  foreach( $source_servers AS $server => $videos ) {
1032
+
1033
+ $tCurrent = microtime(true);
1034
+ if( $tCurrent - $tStart > $tMax ) {
1035
+ $bNotDone = true;
1036
+ break;
1037
+ }
1038
+
1039
+ if( stripos( $videos[0]['src'], '.mp4' ) === FALSE /*&& stripos( $videos[0]['src'], '.m4v' ) === FALSE*/ ) {
1040
+ continue;
1041
+ }
1042
+
1043
  if( stripos( trim($videos[0]['src']), 'rtmp://' ) === false ) {
1044
+ list( $header, $message_out ) = fv_wp_flowplayer_http( trim($videos[0]['src']), array( 'quick_check' => 'true', 'size' => 65536 ) );
1045
+ if( $header ) {
1046
+ $headers = WP_Http::processHeaders( $header );
1047
+ list( $new_errors, $mime_type, $fatal ) = fv_wp_flowplayer_check_headers( $headers, trim($videos[0]['src']), rand(0,999), array( 'talk_bad_mime' => 'Server <code>'.$server.'</code> uses incorrect mime type for MP4 ', 'wrap' => false ) );
1048
+ if( $fatal ) {
1049
+ continue;
1050
+ }
1051
+ if( $new_errors ) {
1052
+ $sPostsLinks = false;
1053
+ foreach( $videos AS $video ) {
1054
+ $sPostsLinks .= '<a href="'.home_url().'?p='.$video['post_id'].'">'.$video['post_id'].'</a> ';
1055
+ }
1056
+ $errors[] = $new_errors.'(<a href="#" onclick="jQuery(\'#fv-flowplayer-warning-'.$count.'\').toggle(); return false">click to see a list of posts</a>) <div id="fv-flowplayer-warning-'.$count.'" style="display: none; ">'.$sPostsLinks.'</div>';
1057
+ $count++;
1058
+ continue;
1059
+ } else {
1060
+ $ok[] = 'Server <code>'.$server.'</code> appears to serve correct mime type <code>'.$mime_type.'</code> for MP4 videos.';
1061
+ }
1062
+ }
1063
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  }
1065
+
1066
+ if( $bNotDone ) {
1067
+ $ok[] = '<strong>Not all the servers were checked as you use a lot of them, increase your PHP execution time or check your other videos by hand.</strong>';
1068
+ }
1069
 
1070
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1071
+ echo '<FVFLOWPLAYER>'.json_encode($output).'</FVFLOWPLAYER>';
1072
  die();
1073
  }
1074
  die('-1');
1084
  if( is_wp_error( $response ) ) {
1085
  $error_message = $response->get_error_message();
1086
  $output = array( 'error' => $error_message );
1087
+ } else {
1088
+
1089
+ $active_plugins = get_option( 'active_plugins' );
1090
  foreach( $active_plugins AS $plugin ) {
1091
  if( stripos( $plugin, 'wp-minify' ) !== false ) {
1092
  $errors[] = "You are using <strong>WP Minify</strong>, so the script checks would not be accurate. Please check your videos manually.";
 
 
 
 
1093
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1094
+ echo '<FVFLOWPLAYER>'.json_encode($output).'</FVFLOWPLAYER>';
1095
  die();
1096
  }
1097
  }
1099
  if( function_exists( 'w3_instance' ) && $minify = w3_instance('W3_Plugin_Minify') ) {
1100
  if( $minify->_config->get_boolean('minify.js.enable') ) {
1101
  $errors[] = "You are using <strong>W3 Total Cache</strong> with JS Minify enabled. The template check might not be accurate. Please check your videos manually.";
1102
+ $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1103
+ echo '<FVFLOWPLAYER>'.json_encode($output).'</FVFLOWPLAYER>';
1104
  }
1105
  }
1106
+
1107
+ if( stripos( $response['body'], '/html5.js') === FALSE && stripos( $response['body'], '/html5shiv.js') === FALSE ) {
1108
+ $errors[] = 'html5.js not found in your template! Videos might not play in old browsers, like Internet Explorer 6-8. Get it <a href="https://code.google.com/p/html5shim/">here</a> and put it into your template.';
1109
+ }
 
 
1110
 
1111
+ $ok[] = 'Template checker has changed. Just open any of your videos on your site and see if you get a red warning message about JavaScript not working.';
1112
+
1113
  $response['body'] = preg_replace( '$<!--[\s\S]+?-->$', '', $response['body'] ); // handle HTML comments
1114
 
1115
  // check Flowplayer scripts
1116
+ preg_match_all( '!<script[^>]*?src=[\'"]([^\'"]*?flowplayer[0-9.-]*?(?:\.min)?\.js[^\'"]*?)[\'"][^>]*?>\s*?</script>!', $response['body'], $flowplayer_scripts );
1117
  if( count($flowplayer_scripts[1]) > 0 ) {
1118
  if( count($flowplayer_scripts[1]) > 1 ) {
1119
  $errors[] = "It appears there are <strong>multiple</strong> Flowplayer scripts on your site, your videos might not be playing, please check. There might be some other plugin adding the script.";
1128
  }
1129
  }
1130
  } else if( count($flowplayer_scripts[1]) < 1 ) {
1131
+ $errors[] = "It appears there are <strong>no</strong> Flowplayer scripts on your site, your videos might not be playing, please check. Check your template's header.php file if it contains wp_head() function call and footer.php should contain wp_footer()!";
1132
  }
1133
 
1134
 
1135
  // check jQuery scripts
1136
+ preg_match_all( '!<script[^>]*?src=[\'"]([^\'"]*?jquery[0-9.-]*?(?:\.min)?\.js[^\'"]*?)[\'"][^>]*?>\s*?</script>!', $response['body'], $jquery_scripts );
1137
+ if( count($jquery_scripts[1]) > 0 ) {
1138
  foreach( $jquery_scripts[1] AS $jkey => $jquery_script ) {
1139
  $check = fv_wp_flowplayer_check_jquery_version( $jquery_script, $jquery_scripts[1], $jkey );
1140
  if( $check == - 1 ) {
1148
  $errors[] = "jQuery library <code>$jquery_script</code> found, but unable to check version, please make sure it's at least 1.7.1.";
1149
  }
1150
  }
1151
+
1152
  if( count($jquery_scripts[1]) > 1 ) {
1153
+ $errors[] = "It appears there are <strong>multiple</strong> jQuery libraries on your site, your videos might not be playing or may play with defects, please check.\n";
1154
  }
1155
  } else if( count($jquery_scripts[1]) < 1 ) {
1156
  $errors[] = "It appears there are <strong>no</strong> jQuery library on your site, your videos might not be playing, please check.\n";
1157
  }
1158
 
1159
 
1160
+ if( $fv_flowplayer_pos > 0 && $jquery_pos > 0 && $jquery_pos > $fv_flowplayer_pos && count($jquery_scripts[1]) < 1 ) {
1161
  $errors[] = "It appears your Flowplayer JavaScript library is loading before jQuery. Your videos probably won't work. Please make sure your jQuery library is loading using the standard Wordpress function - wp_enqueue_scripts(), or move it above wp_head() in your header.php template.";
1162
  }
1163
+
 
1164
  $output = array( 'errors' => $errors, 'ok' => $ok/*, 'html' => $response['body'] */);
1165
  }
1166
+ echo '<FVFLOWPLAYER>'.json_encode($output).'</FVFLOWPLAYER>';
1167
  die();
1168
  }
1169
 
1170
  die('-1');
1171
  }
1172
+
1173
 
1174
  function fv_wp_flowplayer_array_search_by_item( $find, $in_array, &$found, $like = false ) {
1175
  global $fv_wp_flowplayer_array_search_by_item_depth;
1313
  }
1314
  die();
1315
  }
1316
+
1317
+ if( isset($_POST['key']) && $_POST['key'] == 'fv_flowplayer_video_checker_service' && isset($_POST['value']) ) {
1318
+ check_ajax_referer('fv_flowplayer_video_checker_service');
1319
+ $conf = get_option( 'fvwpflowplayer' );
1320
+ if( $conf ) {
1321
+ if( $_POST['value'] == 'true' ) {
1322
+ $conf['disable_videochecker'] = 'false';
1323
+ $conf['video_checker_agreement'] = 'true';
1324
+ } else {
1325
+ $conf['disable_videochecker'] = 'true';
1326
+ }
1327
+ update_option( 'fvwpflowplayer', $conf );
1328
+ }
1329
+ die();
1330
+ }
1331
  }
1332
+
1333
+
1334
+ // allow .vtt subtitle files
1335
+ add_filter( 'wp_check_filetype_and_ext', 'fv_flowplayer_filetypes', 10, 4 );
1336
+
1337
+ function fv_flowplayer_filetypes( $aFile ) {
1338
+ $aArgs = func_get_args();
1339
+ foreach( array( 'vtt', 'webm', 'ogg') AS $item ) {
1340
+ if( isset($aArgs[2]) && preg_match( '~\.'.$item.'~', $aArgs[2] ) ) {
1341
+ $aFile['type'] = $item;
1342
+ $aFile['ext'] = $item;
1343
+ $aFile['proper_filename'] = $aArgs[2];
1344
+ }
1345
+ }
1346
+ return $aFile;
1347
+ }
1348
+
1349
+
1350
+ /*
1351
+ * Check the extension info from plugin license transient and activate the plugin
1352
+ */
1353
+ function fv_wp_flowplayer_install_extension( $plugin_package = 'fv_player_pro' ) {
1354
+ global $hook_suffix;
1355
+
1356
+ $aInstalled = ( get_option('fv_flowplayer_extension_install' ) ) ? get_option('fv_flowplayer_extension_install' ) : array();
1357
+ $aInstalled = array_merge( $aInstalled, array( $plugin_package => false ) );
1358
+ update_option('fv_flowplayer_extension_install', $aInstalled );
1359
+
1360
+ $aPluginInfo = get_transient( 'fv_flowplayer_license' );
1361
+ $plugin_basename = $aPluginInfo->{$plugin_package}->slug;
1362
+ $download_url = $aPluginInfo->{$plugin_package}->url;
1363
+
1364
+ $url = wp_nonce_url( site_url().'/wp-admin/options-general.php?page=fvplayer', 'fv_player_pro_install', 'nonce_fv_player_pro_install' );
1365
+
1366
+ set_current_screen();
1367
+
1368
+ ob_start();
1369
+ if ( false === ( $creds = request_filesystem_credentials( $url, '', false, false, false ) ) ) {
1370
+ $form = ob_get_clean();
1371
+ include( ABSPATH . 'wp-admin/admin-header.php' );
1372
+ echo fv_wp_flowplayer_install_extension_talk($form);
1373
+ include( ABSPATH . 'wp-admin/admin-footer.php' );
1374
+ die;
1375
+ }
1376
+
1377
+ if ( ! WP_Filesystem( $creds ) ) {
1378
+ ob_start();
1379
+ request_filesystem_credentials( $url, $method, true, false, false );
1380
+ $form = ob_get_clean();
1381
+ include( ABSPATH . 'wp-admin/admin-header.php' );
1382
+ echo fv_wp_flowplayer_install_extension_talk($form);
1383
+ include( ABSPATH . 'wp-admin/admin-footer.php' );
1384
+ die;
1385
+ }
1386
+
1387
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1388
+
1389
+ if( !is_plugin_active($plugin_basename) || is_wp_error(validate_plugin($plugin_basename)) ) {
1390
+ echo '<div style="display: none;">';
1391
+ $objInstaller = new Plugin_Upgrader();
1392
+ $objInstaller->install( $download_url );
1393
+ echo '</div>';
1394
+ wp_cache_flush();
1395
+
1396
+ if ( is_wp_error( $objInstaller->skin->result ) ) {
1397
+ update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer Pro extension install failed - '.$objInstaller->skin->result->get_error_message() );
1398
+ $bResult = false;
1399
+ } else {
1400
+ if ( $objInstaller->plugin_info() ) {
1401
+ $plugin_basename = $objInstaller->plugin_info();
1402
+
1403
+ }
1404
+
1405
+ $activate = activate_plugin( $plugin_basename );
1406
+ if ( is_wp_error( $activate ) ) {
1407
+ update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer Pro extension install failed - '.$activate->get_error_message() );
1408
+ $bResult = false;
1409
+ }
1410
+ }
1411
+ }
1412
+
1413
+ if( !isset($bResult) ) {
1414
+ if( !isset($_GET['page']) || strcmp($_GET['page'],'fvplayer') != 0 ) {
1415
+ update_option( 'fv_wordpress_flowplayer_deferred_notices', 'FV Flowplayer Pro extension installed - check the new <a href="'.site_url().'/wp-admin/options-general.php?page=fvplayer#fv_player_pro">Pro features!</a>!' );
1416
+ }
1417
+ $bResult = true;
1418
+ }
1419
+
1420
+ $aInstalled = ( get_option('fv_flowplayer_extension_install' ) ) ? get_option('fv_flowplayer_extension_install' ) : array();
1421
+ $aInstalled = array_merge( $aInstalled, array( $plugin_package => $bResult ) );
1422
+ update_option('fv_flowplayer_extension_install', $aInstalled );
1423
+
1424
+ return $bResult;
1425
+ }
1426
+
1427
+
1428
+ function fv_wp_flowplayer_install_extension_talk( $content ) {
1429
+ $content = preg_replace( '~<h3.*?</h3>~', '<h3>FV Player Pro auto-installation</h3><p>As a FV Flowplayer license holder, we would like to automatically install our Pro extension for you.</p>', $content );
1430
+ $content = preg_replace( '~(<input[^>]*?type="submit"[^>]*?>)~', '$1 <a href="'.site_url().'/wp-admin/options-general.php?page=fvplayer'.'">Skip the Pro addon install</a>', $content );
1431
+ return $content;
1432
+ }
1433
+
1434
+
1435
+ function fv_wp_flowplayer_activate_extension() {
1436
+ check_ajax_referer( 'fv_wp_flowplayer_activate_extension', 'nonce' );
1437
+ if( !isset( $_POST['plugin'] ) ) {
1438
+ die();
1439
+ }
1440
 
1441
+ $activate = activate_plugin( $_POST['plugin'] );
1442
+ if ( is_wp_error( $activate ) ) {
1443
+ echo "<FVFLOWPLAYER>".json_encode( array( 'message' => $activate->get_error_message(), 'error' => $activate->get_error_message() ) )."</FVFLOWPLAYER>";
1444
+ die();
1445
+ }
1446
+
1447
+ echo "<FVFLOWPLAYER>".json_encode( array( 'message' => 'Success!', 'plugin' => $_POST['plugin'] ) )."</FVFLOWPLAYER>";
1448
+ die();
1449
+ }
controller/frontend.php CHANGED
@@ -21,19 +21,19 @@ include_once(dirname( __FILE__ ) . '/../models/flowplayer-frontend.php');
21
 
22
  $fv_fp = new flowplayer_frontend();
23
 
24
- /**
25
- * WP Hooks
26
- */
27
  //add_action('the_content', 'flowplayer_content_remove_commas');
28
  add_action('wp_head', 'flowplayer_head');
29
  add_action('wp_footer','flowplayer_prepare_scripts',9);
30
- add_action('wp_footer','flowplayer_display_scripts',100);
31
- // Addition for 0.9.15
32
  add_action('widget_text','flowplayer_content');
33
  add_action('wp_enqueue_scripts', 'flowplayer_jquery');
34
- /**
35
- * END WP Hooks
36
- */
 
 
 
 
37
 
38
 
39
  function flowplayer_content_remove_commas($content) {
@@ -250,30 +250,44 @@ function flowplayer_content( $content ) {
250
  */
251
  function flowplayer_prepare_scripts() {
252
  global $fv_fp, $fv_wp_flowplayer_ver;
253
- $sPluginUrl = preg_replace( '~^.*://~', '//', FV_FP_RELATIVE_PATH );
254
 
255
- $sCommercialKey = (isset($fv_fp->conf['key']) && $fv_fp->conf['key'] != 'false' && strlen($fv_fp->conf['key']) > 0) ? $fv_fp->conf['key'] : '';
256
- $sLogo = ($sCommercialKey && isset($fv_fp->conf['logo']) && $fv_fp->conf['logo'] != 'false' && strlen($fv_fp->conf['logo']) > 0) ? $fv_fp->conf['logo'] : '';
257
-
258
- if( $fv_fp->load_mediaelement && !wp_script_is('wp-mediaelement') ) {
259
- wp_enqueue_script( 'flowplayer-mediaelement', plugins_url( '/fv-wordpress-flowplayer/mediaelement/mediaelement-and-player.min.js' ), array('jquery'), $fv_wp_flowplayer_ver, true );
260
- }
261
- wp_localize_script( 'flowplayer', 'fv_flowplayer_playlists', $fv_fp->aPlaylists );
262
- wp_localize_script( 'flowplayer', 'fv_fp_ajaxurl', site_url().'/wp-admin/admin-ajax.php' );
263
 
264
- $aConf = array('embed' => array( 'library' => $sPluginUrl.'/flowplayer/fv-flowplayer.min.js', 'script' => $sPluginUrl.'/flowplayer/embed.min.js', 'skin' => $sPluginUrl.'/css/flowplayer.css', 'swf' => $sPluginUrl.'/flowplayer/flowplayer.swf?ver='.$fv_wp_flowplayer_ver ) );
265
- if( $sCommercialKey ) $aConf['key'] = $sCommercialKey;
266
- if( $sLogo ) $aConf['logo'] = $sLogo;
267
- wp_localize_script( 'flowplayer', 'fv_flowplayer_conf', $aConf );
268
- if( $fv_fp->conf['fixed_size'] == 'false' ) {
269
- wp_localize_script( 'flowplayer', 'fv_flowplayer_safety_resize_do', array(true) );
270
- }
271
- if( current_user_can('manage_options') ) {
272
- wp_localize_script( 'flowplayer', 'fv_flowplayer_admin_input', array(true) );
273
- }
274
- if( isset($GLOBALS['fv_fp_scripts']) ) {
275
- foreach( $GLOBALS['fv_fp_scripts'] AS $sKey => $aScripts ) {
276
- wp_localize_script( 'flowplayer', $sKey.'_array', $aScripts );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
  }
279
  }
@@ -306,4 +320,26 @@ function fv_flowplayer_the_content( $c ) {
306
  }
307
  add_filter( 'the_content', 'fv_flowplayer_the_content', 0 );
308
 
309
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  $fv_fp = new flowplayer_frontend();
23
 
 
 
 
24
  //add_action('the_content', 'flowplayer_content_remove_commas');
25
  add_action('wp_head', 'flowplayer_head');
26
  add_action('wp_footer','flowplayer_prepare_scripts',9);
27
+ add_action('wp_footer','flowplayer_display_scripts',100);
 
28
  add_action('widget_text','flowplayer_content');
29
  add_action('wp_enqueue_scripts', 'flowplayer_jquery');
30
+
31
+
32
+ function fv_flowplayer_remove_bad_scripts() {
33
+ wp_deregister_script( 'flowplayer' );
34
+ }
35
+ add_action( 'wp_print_scripts', 'fv_flowplayer_remove_bad_scripts', 100 );
36
+
37
 
38
 
39
  function flowplayer_content_remove_commas($content) {
250
  */
251
  function flowplayer_prepare_scripts() {
252
  global $fv_fp, $fv_wp_flowplayer_ver;
 
253
 
254
+ if(
255
+ isset($GLOBALS['fv_fp_scripts']) ||
256
+ (isset($fv_fp->conf['js-everywhere']) && strcmp($fv_fp->conf['js-everywhere'],'true') == 0 ) ||
257
+ isset($_GET['fv_wp_flowplayer_check_template'])
258
+ ) {
259
+ wp_enqueue_script( 'flowplayer', flowplayer::get_plugin_url().'/flowplayer/fv-flowplayer.min.js', array('jquery'), $fv_wp_flowplayer_ver, true );
260
+
261
+ $sPluginUrl = preg_replace( '~^.*://~', '//', FV_FP_RELATIVE_PATH );
262
 
263
+ $sCommercialKey = (isset($fv_fp->conf['key']) && $fv_fp->conf['key'] != 'false' && strlen($fv_fp->conf['key']) > 0) ? $fv_fp->conf['key'] : '';
264
+ $sLogo = ($sCommercialKey && isset($fv_fp->conf['logo']) && $fv_fp->conf['logo'] != 'false' && strlen($fv_fp->conf['logo']) > 0) ? $fv_fp->conf['logo'] : '';
265
+
266
+ if( $fv_fp->load_mediaelement && !wp_script_is('wp-mediaelement') ) {
267
+ wp_enqueue_script( 'flowplayer-mediaelement', flowplayer::get_plugin_url().'/mediaelement/mediaelement-and-player.min.js', array('jquery'), $fv_wp_flowplayer_ver, true );
268
+ }
269
+ wp_localize_script( 'flowplayer', 'fv_flowplayer_playlists', $fv_fp->aPlaylists );
270
+ wp_localize_script( 'flowplayer', 'fv_fp_ajaxurl', site_url().'/wp-admin/admin-ajax.php' );
271
+
272
+ $aConf = array('embed' => array( 'library' => $sPluginUrl.'/flowplayer/fv-flowplayer.min.js', 'script' => $sPluginUrl.'/flowplayer/embed.min.js', 'skin' => $sPluginUrl.'/css/flowplayer.css', 'swf' => $sPluginUrl.'/flowplayer/flowplayer.swf?ver='.$fv_wp_flowplayer_ver ) );
273
+ if( $sCommercialKey ) $aConf['key'] = $sCommercialKey;
274
+ if( !isset($fv_fp->conf['fixed_size']) || strcmp($fv_fp->conf['fixed_size'],'true') != 0 ) {
275
+ $aConf['safety_resize'] = true;
276
+ }
277
+ if( current_user_can('manage_options') && $fv_fp->conf['disable_videochecker'] != 'true' ) {
278
+ $aConf['video_checker_site'] = home_url();
279
+ }
280
+ if( $sLogo ) $aConf['logo'] = $sLogo;
281
+ wp_localize_script( 'flowplayer', 'fv_flowplayer_conf', $aConf );
282
+ if( current_user_can('manage_options') ) {
283
+ wp_localize_script( 'flowplayer', 'fv_flowplayer_admin_input', array(true) );
284
+ wp_localize_script( 'flowplayer', 'fv_flowplayer_admin_js_test', array(true) );
285
+ }
286
+
287
+ if( count($GLOBALS['fv_fp_scripts']) > 0 ) {
288
+ foreach( $GLOBALS['fv_fp_scripts'] AS $sKey => $aScripts ) {
289
+ wp_localize_script( 'flowplayer', $sKey.'_array', $aScripts );
290
+ }
291
  }
292
  }
293
  }
320
  }
321
  add_filter( 'the_content', 'fv_flowplayer_the_content', 0 );
322
 
323
+
324
+ /*
325
+ Handle attachment pages which contain videos
326
+ */
327
+ function fv_flowplayer_attachment_page_video( $c ) {
328
+ global $post;
329
+ if( stripos($post->post_mime_type, 'video/') !== 0 && stripos($post->post_mime_type, 'audio/') !== 0 ) {
330
+ return $c;
331
+ }
332
+
333
+ if( !$src = wp_get_attachment_url($post->ID) ) {
334
+ return $c;
335
+ }
336
+
337
+ $meta = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
338
+ $size = (isset($meta['width']) && isset($meta['height']) && intval($meta['width'])>0 && intval($meta['height'])>0 ) ? ' width="'.intval($meta['width']).'" height="'.intval($meta['height']).'"' : false;
339
+
340
+ $shortcode = '[fvplayer src="'.$src.'"'.$size.']';
341
+
342
+ $c = preg_replace( '~<p class=.attachment.[\s\S]*?</p>~', $shortcode, $c );
343
+ return $c;
344
+ }
345
+ add_filter( 'prepend_attachment', 'fv_flowplayer_attachment_page_video' );
controller/shortcodes.php CHANGED
@@ -27,7 +27,7 @@ add_shortcode('fvplayer','flowplayer_content_handle');
27
  function flowplayer_content_handle( $atts, $content = null, $tag ) {
28
  global $fv_fp;
29
 
30
- if( $fv_fp->conf['parse_commas'] == 'true' ) {
31
 
32
  if( !isset( $atts['src'] ) ) {
33
  foreach( $atts AS $key => $att ) {
@@ -87,7 +87,7 @@ function flowplayer_content_handle( $atts, $content = null, $tag ) {
87
 
88
  }
89
  /// End of addition
90
-
91
  extract( shortcode_atts( array(
92
  'src' => '',
93
  'src1' => '',
@@ -112,10 +112,14 @@ function flowplayer_content_handle( $atts, $content = null, $tag ) {
112
  'align' => '',
113
  'rtmp' => '',
114
  'rtmp_path' => '',
115
- 'playlist' => ''
 
 
 
 
116
  ), $atts ) );
117
-
118
- if( $fv_fp->conf['parse_commas'] == 'true' ) {
119
  $arguments['width'] = preg_replace('/\,/', '', $width);
120
  $arguments['height'] = preg_replace('/\,/', '', $height);
121
  $arguments['autoplay'] = preg_replace('/\,/', '', $autoplay);
@@ -138,8 +142,13 @@ function flowplayer_content_handle( $atts, $content = null, $tag ) {
138
  $arguments['align'] = preg_replace('/\,/', '', $align);
139
  $arguments['rtmp'] = preg_replace('/\,/', '', $rtmp);
140
  $arguments['rtmp_path'] = preg_replace('/\,/', '', $rtmp_path);
141
- $arguments['playlist'] = $playlist;
142
- $src = trim( preg_replace('/\,/', '', $src) );
 
 
 
 
 
143
  } else {
144
  $arguments = shortcode_atts( array(
145
  'src' => '',
@@ -165,18 +174,115 @@ function flowplayer_content_handle( $atts, $content = null, $tag ) {
165
  'align' => '',
166
  'rtmp' => '',
167
  'rtmp_path' => '',
168
- 'playlist' => ''
 
 
 
 
169
  ), $atts );
170
  }
 
 
171
 
172
-
173
- if( $src != '' || ( strlen($arguments['rtmp']) && strlen($arguments['rtmp_path']) ) ) {
174
  // build new player
175
  $new_player = $fv_fp->build_min_player($src,$arguments);
176
  if (!empty($new_player['script'])) {
177
  $GLOBALS['fv_fp_scripts'] = $new_player['script'];
178
  }
 
179
  }
180
- return $new_player['html'];
181
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  ?>
27
  function flowplayer_content_handle( $atts, $content = null, $tag ) {
28
  global $fv_fp;
29
 
30
+ if( $fv_fp->conf['parse_commas'] == 'true' && strcmp($tag,'flowplayer') == 0 ) {
31
 
32
  if( !isset( $atts['src'] ) ) {
33
  foreach( $atts AS $key => $att ) {
87
 
88
  }
89
  /// End of addition
90
+
91
  extract( shortcode_atts( array(
92
  'src' => '',
93
  'src1' => '',
112
  'align' => '',
113
  'rtmp' => '',
114
  'rtmp_path' => '',
115
+ 'playlist' => '',
116
+ 'admin_warning' => '',
117
+ 'live' => '',
118
+ 'caption' => '',
119
+ 'logo' => ''
120
  ), $atts ) );
121
+
122
+ if( $fv_fp->conf['parse_commas'] == 'true' && strcmp($tag,'flowplayer') == 0 ) {
123
  $arguments['width'] = preg_replace('/\,/', '', $width);
124
  $arguments['height'] = preg_replace('/\,/', '', $height);
125
  $arguments['autoplay'] = preg_replace('/\,/', '', $autoplay);
142
  $arguments['align'] = preg_replace('/\,/', '', $align);
143
  $arguments['rtmp'] = preg_replace('/\,/', '', $rtmp);
144
  $arguments['rtmp_path'] = preg_replace('/\,/', '', $rtmp_path);
145
+ $arguments['playlist'] = $playlist;
146
+ $arguments['admin_warning'] = $admin_warning;
147
+ $arguments['live'] = $live;
148
+ $arguments['caption'] = $caption;
149
+ $arguments['logo'] = $logo;
150
+ $src = trim( preg_replace('/\,/', '', $src) );
151
+ $arguments['src'] = $src;
152
  } else {
153
  $arguments = shortcode_atts( array(
154
  'src' => '',
174
  'align' => '',
175
  'rtmp' => '',
176
  'rtmp_path' => '',
177
+ 'playlist' => '',
178
+ 'admin_warning' => '',
179
+ 'live' => '',
180
+ 'caption' => '',
181
+ 'logo' => ''
182
  ), $atts );
183
  }
184
+
185
+ $arguments = apply_filters( 'fv_flowplayer_shortcode', $arguments, $fv_fp, $atts );
186
 
187
+ if( $src != '' || ( ( ( strlen($fv_fp->conf['rtmp']) && $fv_fp->conf['rtmp'] != 'false' ) || strlen($arguments['rtmp'])) && strlen($arguments['rtmp_path']) ) ) {
 
188
  // build new player
189
  $new_player = $fv_fp->build_min_player($src,$arguments);
190
  if (!empty($new_player['script'])) {
191
  $GLOBALS['fv_fp_scripts'] = $new_player['script'];
192
  }
193
+ return $new_player['html'];
194
  }
195
+ return false;
196
  }
197
+
198
+
199
+
200
+
201
+ add_filter( 'the_content', 'fv_flowplayer_optimizepress', 1 );
202
+
203
+ function fv_flowplayer_optimizepress( $post_content ) {
204
+
205
+ if( stripos( $post_content, '[video_player type="url"' ) === false ) {
206
+ return $post_content;
207
+ }
208
+
209
+ $post_content = preg_replace_callback( '~\[video_player.*?\].*?\[/video_player\]~', 'fv_flowplayer_optimizepress_bridge', $post_content );
210
+ return $post_content;
211
+ }
212
+
213
+ function fv_flowplayer_optimizepress_bridge( $input ) {
214
+ $video = $input[0];
215
+
216
+ $atts = shortcode_parse_atts($video);
217
+
218
+ $default = array(
219
+ 'type' => 'embed', // na
220
+ 'hide_controls' => 'N', // todo
221
+ 'auto_play' => 'N', // ok
222
+ 'auto_buffer' => 'N', // todo
223
+ 'width' => 511, // ok
224
+ 'height' => 288, // ok
225
+ 'margin_top' => 0, // todo
226
+ 'margin_bottom' => 20, // todo
227
+ 'border_size' => 0, // todo
228
+ 'border_color' => '#fff', // todo
229
+ 'placeholder' => '', // ok
230
+ 'align' => 'center', // ok
231
+ 'youtube_url' => '', // na
232
+ 'youtube_auto_play' => 'N', // na
233
+ 'youtube_hide_controls' => 'N', // na
234
+ 'youtube_remove_logo' => 'N', // na
235
+ 'youtube_show_title_bar' => 'N', // na
236
+ 'youtube_force_hd' => '', // na
237
+ 'url1' => '', // ok
238
+ 'url2' => '', // ok
239
+ );
240
+ $vars = shortcode_atts($default, $atts);
241
+
242
+ $shortcode = '[fvplayer';
243
+
244
+ $content = preg_replace( '~\[video_player.*?\](.*?)\[/video_player\]~', '$1', $video );
245
+ $content = base64_decode($content);
246
+ if(preg_match('|(https?://[^<"]+)|im',$content,$matches)){
247
+ $shortcode .= ' src="'.$matches[1].'"';
248
+ }
249
+ $url1 = base64_decode($atts['url1']);
250
+ if(preg_match('|(https?://[^<"]+)|im',$url1,$matches)){
251
+ $shortcode .= ' src1="'.$matches[1].'"';
252
+ }
253
+ $url2 = base64_decode($atts['url2']);
254
+ if(preg_match('|(https?://[^<"]+)|im',$url2,$matches)){
255
+ $shortcode .= ' src2="'.$matches[1].'"';
256
+ }
257
+
258
+ if( $vars['placeholder'] ) {
259
+ $shortcode .= ' splash="'.$vars['placeholder'].'"';
260
+ }
261
+
262
+ if( $vars['auto_play'] == 'Y' ) {
263
+ $shortcode .= ' autoplay="true"';
264
+ }
265
+
266
+ $shortcode .= ' width="'.$vars['width'].'"';
267
+ $shortcode .= ' height="'.$vars['height'].'"';
268
+ $shortcode .= ' align="'.$vars['align'].'"';
269
+
270
+ if( current_user_can('manage_options') &&
271
+ (
272
+ ( isset($vars['margin-top']) && $vars['margin-top'] > 0 ) ||
273
+ ( isset($vars['margin-bottom']) && $vars['margin-bottom'] > 0 && $vars['margin-bottom'] != 20 ) ||
274
+ ( isset($vars['hide_controls']) && $vars['hide_controls'] == 'Y' ) ||
275
+ ( isset($vars['auto_buffer']) && $vars['auto_buffer'] == 'Y' ) ||
276
+ ( isset($vars['border_size']) && $vars['border_size'] > 0 ) ||
277
+ isset($vars['border_color'])
278
+ )
279
+ ) {
280
+ $shortcode .= ' admin_warning="Admin note: Some of the OptimizePress styling parameters are not supported by FV Flowplayer. Please visit the <a href=\''.admin_url('options-general.php?page=fvplayer').'\'>settings</a> and set your global appearance preferences there."';
281
+ }
282
+
283
+ $shortcode .= ']';
284
+
285
+ return $shortcode;
286
+ }
287
+
288
  ?>
css/admin.css CHANGED
@@ -1,4 +1,6 @@
1
- #content .fv-wp-flowplayer-notice-small, .fv-wp-flowplayer-notice-small { color: <?php echo trim($this->conf['timeColor']); ?>; position: absolute; top: 1%; left: 1%; z-index: 2;}
 
 
2
  #content .fv-wp-flowplayer-notice, .fv-wp-flowplayer-notice { color: black !important; background-color: #FFFFE0; border-color: #E6DB55; margin: -1%; padding: 0 0.6em; border-radius: 3px 3px 3px 3px; border-style: solid; border-width: 1px; line-height: 15px; z-index: 100; width: 97%; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif }
3
  #content .fv-wp-flowplayer-notice strong, .fv-wp-flowplayer-notice strong { font-weight: bold; }
4
  #content .fv-wp-flowplayer-notice blockquote, .fv-wp-flowplayer-notice blockquote { font-size: 12px; }
@@ -15,12 +17,14 @@
15
  .fv-wp-flowplayer-notice-small .fv_wp_flowplayer_notice_head { display: none; }
16
  .fv-wp-flowplayer-notice.fv-wp-flowplayer-notice .fv_wp_flowplayer_notice_head { display: block; text-align: center; font-size: 14px; border-bottom: 1px solid #7a7a7a; padding: 10px; line-height: 1; margin: 0 -0.6em 1em -0.6em; background-color: #e3e3e3; background-repeat: repeat-x; background-image: -moz-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -ms-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -webkit-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -o-linear-gradient(top, #e3e3e3, #aeaeae); background-image: linear-gradient(top, #e3e3e3, #aeaeae); border-radius: 3px 3px 0 0; }
17
 
18
- .fv-wp-flowplayer-notice-small .vid-issues { color: red; }
19
- .fv-wp-flowplayer-notice-small .vid-warning { color: orange; }
20
  .fv-wp-flowplayer-notice-small .vid-ok { color: green; }
21
- .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-issues { color: #670004; }
22
- .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-warning { color: #ac641e; }
 
 
23
  .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-ok { color: #114b00; }
 
 
24
 
25
  .fv-wp-fp-hidden { display: none; }
26
  .fv-wp-flowplayer-notice-parsed .row { text-align: left; border-bottom: 1px solid lightgray; border-right: 1px solid lightgray; border-left: 1px solid lightgray; padding-left: 5px; font-size: 12px; clear: both; }
@@ -35,65 +39,8 @@
35
  .fv-wp-flowplayer-notice-parsed.level-6 { background: #d0d0d0; }
36
  .fv-wp-flowplayer-notice-parsed.level-7 { background: #c8c8c8; }
37
  .mail-content-details { height: 200px; overflow: auto; width: 100%; }
38
- /* Administration edit */
39
- .flowplayer-wrapper { min-width: 55%; display: inline-block; float: right;}
40
- @media only screen and (max-width: 940px) {.flowplayer-wrapper { width: 100%; float: none;} .form-table2.flowplayer-settings {width: 100% !important;}}
41
 
42
- #fv_flowplayer_ad {
43
- width: 690px;
44
- height: 238px;
45
- display: block;
46
- margin: 20px 10px;
47
- padding: 15px 10px 0 20px;
48
- border: 1px solid #ccc;
49
- background: url("../images/fabric-plaid-bg.png");
50
- text-rendering: optimizelegibility;
51
- color: #353535;
52
- }
53
- .text-part {
54
- float: left;
55
- width: 310px;
56
- overflow: hidden;
57
- display: inline-block;
58
- }
59
- .graphic-part {
60
- float: right;
61
- width: 320px;
62
- }
63
- #fv_flowplayer_ad h2 {
64
- text-shadow: 1px 1px 1px #fff;
65
- padding: 9px 0;
66
- }
67
- /* paragraph */
68
- #fv_flowplayer_ad p {
69
- font-size: 14px;
70
- }
71
- a.red-button {
72
- background: url("../images/red-button-large.png") no-repeat center top;
73
- height: 40px;
74
- display: block;
75
- color: #fff;
76
- font-size: 16px;
77
- font-weight: 400;
78
- text-decoration: none;
79
- line-height: 1.3;
80
- padding: 10px 0;
81
- width: 260px;
82
- text-align: center;
83
- }
84
- .red-button:hover {
85
- color: #fff;
86
- }
87
- .red-text {
88
- color: #9d1a28;
89
- font-size: 20px;
90
- }
91
- #fv_flowplayer_ad ul {
92
- margin: 15px 0;
93
- font-size: 16px;
94
- }
95
- #fv_flowplayer_ad ul li {
96
- /* background: url("../images/list-bullet.png") no-repeat 0 center;
97
- padding-left: 30px; */
98
- margin-bottom: 2px;
99
- }
1
+ #content .fv-wp-flowplayer-notice-small, .fv-wp-flowplayer-notice-small { color: <?php echo trim($this->conf['timeColor']); ?>; position: absolute; top: 1%; left: 1%; z-index: 2; }
2
+ #content .flowplayer.is-mouseout .fv-wp-flowplayer-notice-small, .flowplayer.is-mouseout .fv-wp-flowplayer-notice-small { top: 1%; -webkit-transition:top .15s .3s;-moz-transition:top .15s .3s;transition:top .15s .3s }
3
+ #content .flowplayer.is-mouseover.is-ready .fv-wp-flowplayer-notice-small, .flowplayer.is-mouseover.is-ready .fv-wp-flowplayer-notice-small { top: 32px }
4
  #content .fv-wp-flowplayer-notice, .fv-wp-flowplayer-notice { color: black !important; background-color: #FFFFE0; border-color: #E6DB55; margin: -1%; padding: 0 0.6em; border-radius: 3px 3px 3px 3px; border-style: solid; border-width: 1px; line-height: 15px; z-index: 100; width: 97%; font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif }
5
  #content .fv-wp-flowplayer-notice strong, .fv-wp-flowplayer-notice strong { font-weight: bold; }
6
  #content .fv-wp-flowplayer-notice blockquote, .fv-wp-flowplayer-notice blockquote { font-size: 12px; }
17
  .fv-wp-flowplayer-notice-small .fv_wp_flowplayer_notice_head { display: none; }
18
  .fv-wp-flowplayer-notice.fv-wp-flowplayer-notice .fv_wp_flowplayer_notice_head { display: block; text-align: center; font-size: 14px; border-bottom: 1px solid #7a7a7a; padding: 10px; line-height: 1; margin: 0 -0.6em 1em -0.6em; background-color: #e3e3e3; background-repeat: repeat-x; background-image: -moz-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -ms-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -webkit-linear-gradient(top, #e3e3e3, #aeaeae); background-image: -o-linear-gradient(top, #e3e3e3, #aeaeae); background-image: linear-gradient(top, #e3e3e3, #aeaeae); border-radius: 3px 3px 0 0; }
19
 
 
 
20
  .fv-wp-flowplayer-notice-small .vid-ok { color: green; }
21
+ .fv-wp-flowplayer-notice-small .vid-warning { color: orange; }
22
+ .fv-wp-flowplayer-notice-small .vid-issues { color: red; }
23
+
24
+
25
  .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-ok { color: #114b00; }
26
+ .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-warning { color: #ac641e; }
27
+ .fv-wp-flowplayer-notice-small.fv-wp-flowplayer-notice .vid-issues { color: #670004; }
28
 
29
  .fv-wp-fp-hidden { display: none; }
30
  .fv-wp-flowplayer-notice-parsed .row { text-align: left; border-bottom: 1px solid lightgray; border-right: 1px solid lightgray; border-left: 1px solid lightgray; padding-left: 5px; font-size: 12px; clear: both; }
39
  .fv-wp-flowplayer-notice-parsed.level-6 { background: #d0d0d0; }
40
  .fv-wp-flowplayer-notice-parsed.level-7 { background: #c8c8c8; }
41
  .mail-content-details { height: 200px; overflow: auto; width: 100%; }
 
 
 
42
 
43
+
44
+
45
+ .flowplayer-wrapper { min-width: 50%; display: inline-block; float: right;}
46
+ @media only screen and (max-width: 940px) {.flowplayer-wrapper { width: 100%; float: none;} .form-table2.flowplayer-settings {width: 100% !important;}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/colorbox.css CHANGED
@@ -5,13 +5,13 @@ ColorBox Core Style
5
  The following rules are the styles that are consistant between themes.
6
  Avoid changing this area to maintain compatability with future versions of ColorBox.
7
  */
8
- #colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:100; overflow:hidden;}
9
- #cboxOverlay{position:fixed; width:100%; height:100%;}
10
  #cboxMiddleLeft, #cboxBottomLeft{clear:left;}
11
  #cboxContent{position:relative; overflow:hidden;}
12
  #cboxLoadedContent{overflow:auto;}
13
  #cboxLoadedContent iframe{display:block; width:100%; height:100%; border:0; background-color: white;}
14
- #cboxTitle{margin:5px;}
15
  #cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
16
  #cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
17
 
5
  The following rules are the styles that are consistant between themes.
6
  Avoid changing this area to maintain compatability with future versions of ColorBox.
7
  */
8
+ #colorbox, #cboxOverlay, #cboxWrapper, #colorbox.fv-flowplayer-shortcode-editor, #cboxOverlay.fv-flowplayer-shortcode-editor, #cboxWrapper.fv-flowplayer-shortcode-editor{position:absolute; top:0; left:0; z-index:100; overflow:hidden;}
9
+ #cboxOverlay, #cboxOverlay.fv-flowplayer-shortcode-editor{position:fixed; width:100%; height:100%; z-index: 100}
10
  #cboxMiddleLeft, #cboxBottomLeft{clear:left;}
11
  #cboxContent{position:relative; overflow:hidden;}
12
  #cboxLoadedContent{overflow:auto;}
13
  #cboxLoadedContent iframe{display:block; width:100%; height:100%; border:0; background-color: white;}
14
+ .fv-flowplayer-shortcode-editor #cboxTitle{margin:5px; text-align: left; bottom: auto }
15
  #cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
16
  #cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
17
 
css/flowplayer.css CHANGED
@@ -1,4 +1,4 @@
1
- .flowplayer{position:relative;width:100%;background-size:contain;background-repeat:no-repeat;background-position:center center;display:inline-block;}
2
  .flowplayer *{font-weight:inherit;font-family:inherit;font-style:inherit;text-decoration:inherit;font-size:100%;padding:0;border:0;margin:0;list-style-type:none}
3
  .flowplayer a:focus{outline:0}
4
  .flowplayer video{width:100%}
@@ -29,6 +29,7 @@
29
  .flowplayer .fp-help em{background:#eee;-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em;margin-right:.4em;padding:.3em .6em;color:#333}
30
  .flowplayer .fp-help small{font-size:90%;color:#aaa}
31
  .flowplayer .fp-help .fp-close{display:block}
 
32
  @media (max-width: 600px){.flowplayer .fp-help p{font-size:9px}
33
  }.flowplayer .fp-subtitle{position:absolute;bottom:40px;left:-99999em;z-index:10;text-align:center;width:100%;opacity:0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);-webkit-transition:opacity .3s;-moz-transition:opacity .3s;transition:opacity .3s;}
34
  .flowplayer .fp-subtitle p{display:inline;background-color:#333;background-color:rgba(51,51,51,0.9);color:#eee;padding:.1em .4em;font-size:16px;line-height:1.6;}
@@ -148,6 +149,7 @@
148
  .flowplayer.is-splash,.flowplayer.is-poster{cursor:pointer;}
149
  .flowplayer.is-splash .fp-controls,.flowplayer.is-poster .fp-controls,.flowplayer.is-splash .fp-fullscreen,.flowplayer.is-poster .fp-fullscreen,.flowplayer.is-splash .fp-unload,.flowplayer.is-poster .fp-unload,.flowplayer.is-splash .fp-time,.flowplayer.is-poster .fp-time,.flowplayer.is-splash .fp-embed,.flowplayer.is-poster .fp-embed{display:none !important}
150
  .flowplayer.is-poster .fp-engine{top:-9999em}
 
151
  .flowplayer.is-loading .fp-waiting {height: 1em;}
152
  .flowplayer.is-loading .fp-waiting em {width:1em;height:1em;}
153
  .flowplayer.is-loading .fp-controls,.flowplayer.is-loading .fp-time{display:none}
@@ -175,17 +177,9 @@
175
  .flowplayer.is-disabled .fp-progress{background-color:#999}
176
  .flowplayer .fp-embed{position:absolute;top:5px;left:5px;display:block;width:25px;height:20px;background-position:3px -237px;}
177
  .is-rtl.flowplayer .fp-embed{background-position:22px -237px;left:auto;right:5px}
178
- .flowplayer .fp-embed-code{position:absolute;display:none;top:10px;left:40px;background-color:#333;padding:3px 5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 0 3px #ccc;-moz-box-shadow:0 0 3px #ccc;box-shadow:0 0 3px #ccc;font-size:12px;}
179
- .flowplayer .fp-embed-code:before{content:'';width:0;height:0;position:absolute;top:2px;left:-10px;border:5px solid transparent;border-right-color:#333}
180
- .is-rtl.flowplayer .fp-embed-code{left:auto;right:40px;}
181
- .is-rtl.flowplayer .fp-embed-code:before{left:auto;right:-10px;border-right-color:transparent;border-left-color:#333}
182
- .flowplayer .fp-embed-code textarea{width:400px;height:16px;font-family:monaco,"courier new",verdana;color:#777;white-space:nowrap;resize:none;overflow:hidden;border:0;outline:0;background-color:transparent;color:#ccc}
183
- .flowplayer .fp-embed-code label{display:block;color:#999}
184
- .flowplayer.is-embedding .fp-embed,.flowplayer.is-embedding .fp-embed-code{display:block;opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
185
  .flowplayer.aside-time .fp-embed{left:85px;}
186
  .is-rtl.flowplayer.aside-time .fp-embed{left:auto;right:85px}
187
- .flowplayer.aside-time .fp-embed-code{left:115px;}
188
- .is-rtl.flowplayer.aside-time .fp-embed-code{left:auto;right:115px}
189
  .flowplayer.aside-time.is-embedding .fp-time{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
190
  .flowplayer.is-long.aside-time .fp-embed{left:130px;}
191
  .is-rtl.flowplayer.is-long.aside-time .fp-embed{left:auto;right:130px}
@@ -227,6 +221,65 @@
227
  .flowplayer.color-alt2 .fp-progress{background-color:#900}
228
  .flowplayer.color-alt2.is-touch.is-mouseover .fp-progress:before{background-color:#900}
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  .flowplayer .fp-prev, .flowplayer .fp-next{/* next and prev buttons */position:absolute;top:44%;cursor:pointer;-webkit-transition:opacity .5s;-moz-transition:opacity .5s;transition:opacity .5s;background:url("img/playlist-buttons.png");opacity:0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);height:20px;width:12px;}
231
  .is-mouseover.is-ready.flowplayer .fp-prev,.is-mouseover.is-ready.flowplayer .fp-next{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
232
  .flowplayer .fp-prev{left:-20px;background-position:0 -200px;}
@@ -235,9 +288,11 @@
235
  .flowplayer .fp-next:hover{background-position:0 -500px}
236
 
237
  .fp-playlist-external { text-align:center; }
238
- .fp-playlist-external a { display:inline-block;width:120px;height:80px;margin:0 10px;-webkit-transition:all .3s;-moz-transition:all .3s;transition:all .3s;border:4px solid #fff;background:gray;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-size:auto 100px;background-position:center;}
239
- .fp-playlist-external a:hover { border-color:#ccc }
240
- .fp-playlist-external a.is-active { border-color:#52b9e4; }
 
 
241
 
242
  .add_media span.wp-media-buttons-icon {
243
  background: url("img/media-button.png") no-repeat scroll left top;
@@ -278,8 +333,9 @@ a #add-format, a #add-rtmp {
278
  }
279
 
280
 
 
 
281
  .flowplayer .fp-volume { text-align: left; }
282
- #content .flowplayer .fp-embed-code, .flowplayer .fp-embed-code { padding: 3px 7px; }
283
  #content .flowplayer a, .flowplayer a, .flowplayer a:hover { text-decoration: none; border-bottom: none; }
284
 
285
  .flowplayer .wpfp_custom_ad { display: none; overflow: hidden; }
@@ -301,4 +357,10 @@ MediaElement.js
301
  .mejs-container{position:relative;background:#000;font-family:Helvetica,Arial;text-align:left;vertical-align:top;text-indent:0; margin: 0 auto 28px auto; }
302
  .me-plugin{position:absolute;}.mejs-embed,.mejs-embed body{width:100%;height:100%;margin:0;padding:0;background:#000;overflow:hidden;}.mejs-container-fullscreen{position:fixed;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:1000;}.mejs-container-fullscreen .mejs-mediaelement,.mejs-container-fullscreen video{width:100%;height:100%;}.mejs-clear{clear:both;}.mejs-background{position:absolute;top:0;left:0;}.mejs-mediaelement{position:absolute;top:0;left:0;width:100%;height:100%;}.mejs-poster{position:absolute;top:0;left:0;background-size:contain;background-position:50% 50%;background-repeat:no-repeat;}:root .mejs-poster img{display:none;}.mejs-poster img{border:0;padding:0;border:0;}.mejs-overlay{position:absolute;top:0;left:0;}.mejs-overlay-play{cursor:pointer;}.mejs-overlay-button{position:absolute;top:50%;left:50%;width:100px;height:100px;margin:-50px 0 0 -50px;background:url(bigplay.svg) no-repeat;}.no-svg .mejs-overlay-button{background-image:url(bigplay.png);}.mejs-overlay:hover .mejs-overlay-button{background-position:0 -100px;}.mejs-overlay-loading{position:absolute;top:50%;left:50%;width:80px;height:80px;margin:-40px 0 0 -40px;background:#333;background:url(background.png);background:rgba(0,0,0,0.9);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.9)),to(rgba(0,0,0,0.9)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-moz-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-o-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-ms-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:linear-gradient(rgba(50,50,50,0.9),rgba(0,0,0,0.9));}.mejs-overlay-loading span{display:block;width:80px;height:80px;background:transparent url(loading.gif) 50% 50% no-repeat;}.mejs-container .mejs-controls{position:absolute;list-style-type:none;margin:0;padding:0;bottom:0;left:0;background:url(background.png);background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));height:30px;width:100%;}
303
  .mejs-container .mejs-controls div{list-style-type:none;background-image:none;display:block;float:left;margin:0;padding:0;width:26px;height:26px;font-size:11px;line-height:11px;/*font-family:Helvetica,Arial*/;border:0;}
304
- .mejs-controls .mejs-button button{cursor:pointer;display:block;font-size:0;line-height:0;text-decoration:none;margin:7px 5px;padding:0;position:absolute;height:16px;width:16px;border:0;background:transparent url(controls.svg) no-repeat;}.no-svg .mejs-controls .mejs-button button{background-image:url(controls.png);}.mejs-controls .mejs-button button:focus{outline:solid 1px yellow;}.mejs-container .mejs-controls .mejs-time{color:#fff;display:block;height:17px;width:auto;padding:8px 3px 0 3px;overflow:hidden;text-align:center;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}.mejs-container .mejs-controls .mejs-time span{color:#fff;font-size:11px;line-height:12px;display:block;float:left;margin:1px 2px 0 0;width:auto;}.mejs-controls .mejs-play button{background-position:0 0;}.mejs-controls .mejs-pause button{background-position:0 -16px;}.mejs-controls .mejs-stop button{background-position:-112px 0;}.mejs-controls div.mejs-time-rail{width:200px;padding-top:5px;}.mejs-controls .mejs-time-rail span{display:block;position:absolute;width:180px;height:10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer;}.mejs-controls .mejs-time-rail .mejs-time-total{margin:5px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-time-rail .mejs-time-buffering{width:100%;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:15px 15px;-moz-background-size:15px 15px;-o-background-size:15px 15px;background-size:15px 15px;-webkit-animation:buffering-stripes 2s linear infinite;-moz-animation:buffering-stripes 2s linear infinite;-ms-animation:buffering-stripes 2s linear infinite;-o-animation:buffering-stripes 2s linear infinite;animation:buffering-stripes 2s linear infinite;}@-webkit-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-moz-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-ms-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-o-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}.mejs-controls .mejs-time-rail .mejs-time-loaded{background:#3caac8;background:rgba(60,170,200,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(44,124,145,0.8)),to(rgba(78,183,212,0.8)));background:-webkit-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-moz-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-o-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-ms-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:linear-gradient(rgba(44,124,145,0.8),rgba(78,183,212,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-current{background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-handle{display:none;position:absolute;margin:0;width:10px;background:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;border:solid 2px #333;top:-2px;text-align:center;}.mejs-controls .mejs-time-rail .mejs-time-float{position:absolute;display:none;background:#eee;width:36px;height:17px;border:solid 1px #333;top:-26px;margin-left:-18px;text-align:center;color:#111;}.mejs-controls .mejs-time-rail .mejs-time-float-current{margin:2px;width:30px;display:block;text-align:center;left:0;}.mejs-controls .mejs-time-rail .mejs-time-float-corner{position:absolute;display:block;width:0;height:0;line-height:0;border:solid 5px #eee;border-color:#eee transparent transparent transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:15px;left:13px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float{width:48px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-current{width:44px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-corner{left:18px;}.mejs-controls .mejs-fullscreen-button button{background-position:-32px 0;}.mejs-controls .mejs-unfullscreen button{background-position:-32px -16px;}.mejs-controls .mejs-mute button{background-position:-16px -16px;}.mejs-controls .mejs-unmute button{background-position:-16px 0;}.mejs-controls .mejs-volume-button{position:relative;}.mejs-controls .mejs-volume-button .mejs-volume-slider{display:none;height:115px;width:25px;background:url(background.png);background:rgba(50,50,50,0.7);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:-115px;left:0;z-index:1;position:absolute;margin:0;}.mejs-controls .mejs-volume-button:hover{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-total{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.5);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-current{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.9);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-handle{position:absolute;left:4px;top:-3px;width:16px;height:6px;background:#ddd;background:rgba(255,255,255,0.9);cursor:N-resize;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0;}.mejs-controls div.mejs-horizontal-volume-slider{height:26px;width:60px;position:relative;}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-handle{display:none;}.mejs-controls .mejs-captions-button{position:relative;}.mejs-controls .mejs-captions-button button{background-position:-48px 0;}.mejs-controls .mejs-captions-button .mejs-captions-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-controls .mejs-captions-button .mejs-captions-translations{font-size:10px;margin:0 0 5px 0;}.mejs-chapters{position:absolute;top:0;left:0;-xborder-right:solid 1px #fff;width:10000px;z-index:1;}.mejs-chapters .mejs-chapter{position:absolute;float:left;background:#222;background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#323232,endColorstr=#000000);overflow:hidden;border:0;}.mejs-chapters .mejs-chapter .mejs-chapter-block{font-size:11px;color:#fff;padding:5px;display:block;border-right:solid 1px #333;border-bottom:solid 1px #333;cursor:pointer;}.mejs-chapters .mejs-chapter .mejs-chapter-block-last{border-right:none;}.mejs-chapters .mejs-chapter .mejs-chapter-block:hover{background:#666;background:rgba(102,102,102,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(102,102,102,0.7)),to(rgba(50,50,50,0.6)));background:-webkit-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-moz-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-o-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-ms-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:linear-gradient(rgba(102,102,102,0.7),rgba(50,50,50,0.6));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#666666,endColorstr=#323232);}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-title{font-size:12px;font-weight:bold;display:block;white-space:nowrap;text-overflow:ellipsis;margin:0 0 3px 0;line-height:12px;}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-timespan{font-size:12px;line-height:12px;margin:3px 0 4px 0;display:block;white-space:nowrap;text-overflow:ellipsis;}.mejs-captions-layer{position:absolute;bottom:0;left:0;text-align:center;line-height:22px;font-size:12px;color:#fff;}.mejs-captions-layer a{color:#fff;text-decoration:underline;}.mejs-captions-layer[lang=ar]{font-size:20px;font-weight:normal;}.mejs-captions-position{position:absolute;width:100%;bottom:15px;left:0;}.mejs-captions-position-hover{bottom:45px;}.mejs-captions-text{padding:3px 5px;background:url(background.png);background:rgba(20,20,20,0.8);}.me-cannotplay a{color:#fff;font-weight:bold;}.me-cannotplay span{padding:15px;display:block;}.mejs-controls .mejs-loop-off button{background-position:-64px -16px;}.mejs-controls .mejs-loop-on button{background-position:-64px 0;}.mejs-controls .mejs-backlight-off button{background-position:-80px -16px;}.mejs-controls .mejs-backlight-on button{background-position:-80px 0;}.mejs-controls .mejs-picturecontrols-button{background-position:-96px 0;}.mejs-contextmenu{position:absolute;width:150px;padding:10px;border-radius:4px;top:0;left:0;background:#fff;border:solid 1px #999;z-index:1001;}.mejs-contextmenu .mejs-contextmenu-separator{height:1px;font-size:0;margin:5px 6px;background:#333;}.mejs-contextmenu .mejs-contextmenu-item{font-family:Helvetica,Arial;font-size:12px;padding:4px 6px;cursor:pointer;color:#333;}.mejs-contextmenu .mejs-contextmenu-item:hover{background:#2C7C91;color:#fff;}.mejs-controls .mejs-sourcechooser-button{position:relative;}.mejs-controls .mejs-sourcechooser-button button{background-position:-128px 0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-postroll-layer{position:absolute;bottom:0;left:0;width:100%;height:100%;background:url(background.png);background:rgba(50,50,50,0.7);z-index:1000;overflow:hidden;}.mejs-postroll-layer-content{width:100%;height:100%;}.mejs-postroll-close{position:absolute;right:0;top:0;background:url(background.png);background:rgba(50,50,50,0.7);color:#fff;padding:4px;z-index:100;cursor:pointer;}
 
 
 
 
 
 
1
+ .flowplayer{position:relative;width:100%;background-size:contain;background-repeat:no-repeat;background-position:center center;display:inline-block; -webkit-backface-visibility: hidden }
2
  .flowplayer *{font-weight:inherit;font-family:inherit;font-style:inherit;text-decoration:inherit;font-size:100%;padding:0;border:0;margin:0;list-style-type:none}
3
  .flowplayer a:focus{outline:0}
4
  .flowplayer video{width:100%}
29
  .flowplayer .fp-help em{background:#eee;-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em;margin-right:.4em;padding:.3em .6em;color:#333}
30
  .flowplayer .fp-help small{font-size:90%;color:#aaa}
31
  .flowplayer .fp-help .fp-close{display:block}
32
+
33
  @media (max-width: 600px){.flowplayer .fp-help p{font-size:9px}
34
  }.flowplayer .fp-subtitle{position:absolute;bottom:40px;left:-99999em;z-index:10;text-align:center;width:100%;opacity:0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);-webkit-transition:opacity .3s;-moz-transition:opacity .3s;transition:opacity .3s;}
35
  .flowplayer .fp-subtitle p{display:inline;background-color:#333;background-color:rgba(51,51,51,0.9);color:#eee;padding:.1em .4em;font-size:16px;line-height:1.6;}
149
  .flowplayer.is-splash,.flowplayer.is-poster{cursor:pointer;}
150
  .flowplayer.is-splash .fp-controls,.flowplayer.is-poster .fp-controls,.flowplayer.is-splash .fp-fullscreen,.flowplayer.is-poster .fp-fullscreen,.flowplayer.is-splash .fp-unload,.flowplayer.is-poster .fp-unload,.flowplayer.is-splash .fp-time,.flowplayer.is-poster .fp-time,.flowplayer.is-splash .fp-embed,.flowplayer.is-poster .fp-embed{display:none !important}
151
  .flowplayer.is-poster .fp-engine{top:-9999em}
152
+ .flowplayer.is-loading video.fp-engine{position:absolute;top:-9999em}
153
  .flowplayer.is-loading .fp-waiting {height: 1em;}
154
  .flowplayer.is-loading .fp-waiting em {width:1em;height:1em;}
155
  .flowplayer.is-loading .fp-controls,.flowplayer.is-loading .fp-time{display:none}
177
  .flowplayer.is-disabled .fp-progress{background-color:#999}
178
  .flowplayer .fp-embed{position:absolute;top:5px;left:5px;display:block;width:25px;height:20px;background-position:3px -237px;}
179
  .is-rtl.flowplayer .fp-embed{background-position:22px -237px;left:auto;right:5px}
180
+
 
 
 
 
 
 
181
  .flowplayer.aside-time .fp-embed{left:85px;}
182
  .is-rtl.flowplayer.aside-time .fp-embed{left:auto;right:85px}
 
 
183
  .flowplayer.aside-time.is-embedding .fp-time{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
184
  .flowplayer.is-long.aside-time .fp-embed{left:130px;}
185
  .is-rtl.flowplayer.is-long.aside-time .fp-embed{left:auto;right:130px}
221
  .flowplayer.color-alt2 .fp-progress{background-color:#900}
222
  .flowplayer.color-alt2.is-touch.is-mouseover .fp-progress:before{background-color:#900}
223
 
224
+ .flowplayer .fp-embed-code{display:none;}
225
+ .flowplayer .fvp-share-bar{opacity:0;display:none;position:absolute;top:0px;left:0px;background-color:rgba(0, 0, 0, 0.3);font-size:12px;width:100%}
226
+ .is-rtl.flowplayer .fvp-share-bar{left:auto;}
227
+ .flowplayer .fvp-share-bar textarea{width:400px;min-height:130px;font-family: "Lucida Console", Monaco, monospace; color:#777; white-space:nowrap; resize:none;overflow:hidden;border:0;outline:0;background-color:transparent;color:#ccc; border: 1px solid #888;}
228
+ .flowplayer.is-embedding .fp-embed,.flowplayer.is-embedding .fvp-share-bar{display:block;opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
229
+ .fvp-share-bar .fvp-sharing, #content .fvp-share-bar .fvp-sharing {
230
+ /*clear: both;
231
+ width: 102px;*/
232
+ min-height: 24px;
233
+ margin: 7px auto;
234
+ float:left;
235
+ font-family:"Lucida Console", Monaco, monospace
236
+ }
237
+ .fvp-share-bar label {
238
+ text-align: center;
239
+ }
240
+
241
+ .fvp-share-bar .fvp-sharing li {
242
+ float: left;
243
+ display: inline-block;
244
+ margin: 0 5px;
245
+ padding: 0;
246
+ }
247
+
248
+ .fvp-share-bar .fvp-sharing a {
249
+ width: 24px;
250
+ height: 24px;
251
+ display: block;
252
+ overflow: hidden;
253
+ background: url("img/flp-share-icons.png") no-repeat;
254
+ text-indent: -9999em;
255
+ }
256
+
257
+ .fvp-share-bar .fvp-sharing a.sharing-twitter {
258
+ background-position: 0 -28px;
259
+ }
260
+
261
+ .fvp-share-bar .fvp-sharing a.sharing-google {
262
+ background-position: 0 -56px;
263
+ }
264
+
265
+ .fvp-share-bar .fvp-sharing a.sharing-email {
266
+ background-position: 0 -84px;
267
+ }
268
+ .flowplayer .fvp-share-bar label{float:left;color:#fff;padding: 6px 10px;}
269
+ .flowplayer .fp-embed{display:none}
270
+ /*.flowplayer.is-mouseover.is-ready .fp-embed-code{display:block}*/
271
+ .flowplayer.is-mouseout .fvp-share-bar{opacity:0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);-webkit-transition:opacity .15s .3s;-moz-transition:opacity .15s .3s;transition:opacity .15s .3s}
272
+ .flowplayer.is-mouseout .fvp-share-bar.visible{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
273
+ .flowplayer.is-mouseover.is-ready .fvp-share-bar{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
274
+ .flowplayer .fp-fullscreen {z-index:1}
275
+ /*.flowplayer .fvp-share-bar {display:none;}*/
276
+ .flowplayer .embed-code {padding: 0 10px 10px; display: none; clear: both; background-color: rgba(0, 0, 0, 0); }
277
+ .flowplayer .embed-code label {clear: both; padding-left: 0px; padding-top: 0; line-height: 16px;}
278
+ #content .flowplayer .fvp-share-bar textarea, .flowplayer .fvp-share-bar textarea { padding: 3px; line-height: 1.4; white-space: pre-wrap; color: black !important; height: auto; min-height: 130px; width: 99%; font-size: 10px; background-color:rgba(255, 255, 255, 1); box-shadow: none; border-radius: 0; cursor: text; }
279
+ #content .flowplayer.is-ready .fvp-share-bar.visible .embed-code, .flowplayer.is-ready .fvp-share-bar.visible .embed-code { display: block; }
280
+ /*#content .flowplayer.is-ready .fp-embed-code.visible textarea, .flowplayer.is-ready .fp-embed-code.visible textarea { height: 140px; }*/
281
+ a.embed-code-toggle { font-weight: bold; color: white; }
282
+
283
  .flowplayer .fp-prev, .flowplayer .fp-next{/* next and prev buttons */position:absolute;top:44%;cursor:pointer;-webkit-transition:opacity .5s;-moz-transition:opacity .5s;transition:opacity .5s;background:url("img/playlist-buttons.png");opacity:0;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);height:20px;width:12px;}
284
  .is-mouseover.is-ready.flowplayer .fp-prev,.is-mouseover.is-ready.flowplayer .fp-next{opacity:1;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100)}
285
  .flowplayer .fp-prev{left:-20px;background-position:0 -200px;}
288
  .flowplayer .fp-next:hover{background-position:0 -500px}
289
 
290
  .fp-playlist-external { text-align:center; }
291
+ .fp-playlist-external a { width:148px; display:inline-block; vertical-align:top; margin:5px 0px 0px 0px;}
292
+ .fp-playlist-external a span { display:block;width:120px;height:80px;margin:0 10px;-webkit-transition:all .3s;-moz-transition:all .3s;transition:all .3s;border:4px solid #fff;background:gray;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-size:auto 100px;background-position:center;}
293
+ .fp-playlist-external a:hover span { border-color:#ccc }
294
+ .fp-playlist-external a.is-active span { border-color:#52b9e4; }
295
+ .fp-playlist-external a.is-active { color:#52b9e4;-webkit-transition:all .3s;-moz-transition:all .3s;transition:all .3s; }
296
 
297
  .add_media span.wp-media-buttons-icon {
298
  background: url("img/media-button.png") no-repeat scroll left top;
333
  }
334
 
335
 
336
+ .flowplayer.video1, .flowplayer.video2, .flowplayer.video3, .flowplayer.video4, .flowplayer.video5, .flowplayer.video6, .flowplayer.video7, .flowplayer.video8, .flowplayer.video9, .flowplayer.video10, .flowplayer.video11, .flowplayer.video12, .flowplayer.video13, .flowplayer.video14, .flowplayer.video15, .flowplayer.video16 { background-image: none !important; }
337
+
338
  .flowplayer .fp-volume { text-align: left; }
 
339
  #content .flowplayer a, .flowplayer a, .flowplayer a:hover { text-decoration: none; border-bottom: none; }
340
 
341
  .flowplayer .wpfp_custom_ad { display: none; overflow: hidden; }
357
  .mejs-container{position:relative;background:#000;font-family:Helvetica,Arial;text-align:left;vertical-align:top;text-indent:0; margin: 0 auto 28px auto; }
358
  .me-plugin{position:absolute;}.mejs-embed,.mejs-embed body{width:100%;height:100%;margin:0;padding:0;background:#000;overflow:hidden;}.mejs-container-fullscreen{position:fixed;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:1000;}.mejs-container-fullscreen .mejs-mediaelement,.mejs-container-fullscreen video{width:100%;height:100%;}.mejs-clear{clear:both;}.mejs-background{position:absolute;top:0;left:0;}.mejs-mediaelement{position:absolute;top:0;left:0;width:100%;height:100%;}.mejs-poster{position:absolute;top:0;left:0;background-size:contain;background-position:50% 50%;background-repeat:no-repeat;}:root .mejs-poster img{display:none;}.mejs-poster img{border:0;padding:0;border:0;}.mejs-overlay{position:absolute;top:0;left:0;}.mejs-overlay-play{cursor:pointer;}.mejs-overlay-button{position:absolute;top:50%;left:50%;width:100px;height:100px;margin:-50px 0 0 -50px;background:url(bigplay.svg) no-repeat;}.no-svg .mejs-overlay-button{background-image:url(bigplay.png);}.mejs-overlay:hover .mejs-overlay-button{background-position:0 -100px;}.mejs-overlay-loading{position:absolute;top:50%;left:50%;width:80px;height:80px;margin:-40px 0 0 -40px;background:#333;background:url(background.png);background:rgba(0,0,0,0.9);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.9)),to(rgba(0,0,0,0.9)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-moz-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-o-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:-ms-linear-gradient(top,rgba(50,50,50,0.9),rgba(0,0,0,0.9));background:linear-gradient(rgba(50,50,50,0.9),rgba(0,0,0,0.9));}.mejs-overlay-loading span{display:block;width:80px;height:80px;background:transparent url(loading.gif) 50% 50% no-repeat;}.mejs-container .mejs-controls{position:absolute;list-style-type:none;margin:0;padding:0;bottom:0;left:0;background:url(background.png);background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));height:30px;width:100%;}
359
  .mejs-container .mejs-controls div{list-style-type:none;background-image:none;display:block;float:left;margin:0;padding:0;width:26px;height:26px;font-size:11px;line-height:11px;/*font-family:Helvetica,Arial*/;border:0;}
360
+ .mejs-controls .mejs-button button{cursor:pointer;display:block;font-size:0;line-height:0;text-decoration:none;margin:7px 5px;padding:0;position:absolute;height:16px;width:16px;border:0;background:transparent url(controls.svg) no-repeat;}.no-svg .mejs-controls .mejs-button button{background-image:url(controls.png);}.mejs-controls .mejs-button button:focus{outline:solid 1px yellow;}.mejs-container .mejs-controls .mejs-time{color:#fff;display:block;height:17px;width:auto;padding:8px 3px 0 3px;overflow:hidden;text-align:center;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}.mejs-container .mejs-controls .mejs-time span{color:#fff;font-size:11px;line-height:12px;display:block;float:left;margin:1px 2px 0 0;width:auto;}.mejs-controls .mejs-play button{background-position:0 0;}.mejs-controls .mejs-pause button{background-position:0 -16px;}.mejs-controls .mejs-stop button{background-position:-112px 0;}.mejs-controls div.mejs-time-rail{width:200px;padding-top:5px;}.mejs-controls .mejs-time-rail span{display:block;position:absolute;width:180px;height:10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer;}.mejs-controls .mejs-time-rail .mejs-time-total{margin:5px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-time-rail .mejs-time-buffering{width:100%;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:15px 15px;-moz-background-size:15px 15px;-o-background-size:15px 15px;background-size:15px 15px;-webkit-animation:buffering-stripes 2s linear infinite;-moz-animation:buffering-stripes 2s linear infinite;-ms-animation:buffering-stripes 2s linear infinite;-o-animation:buffering-stripes 2s linear infinite;animation:buffering-stripes 2s linear infinite;}@-webkit-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-moz-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-ms-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@-o-keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}@keyframes buffering-stripes{from{background-position:0 0;}to{background-position:30px 0;}}.mejs-controls .mejs-time-rail .mejs-time-loaded{background:#3caac8;background:rgba(60,170,200,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(44,124,145,0.8)),to(rgba(78,183,212,0.8)));background:-webkit-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-moz-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-o-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:-ms-linear-gradient(top,rgba(44,124,145,0.8),rgba(78,183,212,0.8));background:linear-gradient(rgba(44,124,145,0.8),rgba(78,183,212,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-current{background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));width:0;}.mejs-controls .mejs-time-rail .mejs-time-handle{display:none;position:absolute;margin:0;width:10px;background:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;border:solid 2px #333;top:-2px;text-align:center;}.mejs-controls .mejs-time-rail .mejs-time-float{position:absolute;display:none;background:#eee;width:36px;height:17px;border:solid 1px #333;top:-26px;margin-left:-18px;text-align:center;color:#111;}.mejs-controls .mejs-time-rail .mejs-time-float-current{margin:2px;width:30px;display:block;text-align:center;left:0;}.mejs-controls .mejs-time-rail .mejs-time-float-corner{position:absolute;display:block;width:0;height:0;line-height:0;border:solid 5px #eee;border-color:#eee transparent transparent transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:15px;left:13px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float{width:48px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-current{width:44px;}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-corner{left:18px;}.mejs-controls .mejs-fullscreen-button button{background-position:-32px 0;}.mejs-controls .mejs-unfullscreen button{background-position:-32px -16px;}.mejs-controls .mejs-mute button{background-position:-16px -16px;}.mejs-controls .mejs-unmute button{background-position:-16px 0;}.mejs-controls .mejs-volume-button{position:relative;}.mejs-controls .mejs-volume-button .mejs-volume-slider{display:none;height:115px;width:25px;background:url(background.png);background:rgba(50,50,50,0.7);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:-115px;left:0;z-index:1;position:absolute;margin:0;}.mejs-controls .mejs-volume-button:hover{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-total{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.5);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-current{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,0.9);margin:0;}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-handle{position:absolute;left:4px;top:-3px;width:16px;height:6px;background:#ddd;background:rgba(255,255,255,0.9);cursor:N-resize;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0;}.mejs-controls div.mejs-horizontal-volume-slider{height:26px;width:60px;position:relative;}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#333;background:rgba(50,50,50,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(30,30,30,0.8)),to(rgba(60,60,60,0.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-moz-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-o-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:-ms-linear-gradient(top,rgba(30,30,30,0.8),rgba(60,60,60,0.8));background:linear-gradient(rgba(30,30,30,0.8),rgba(60,60,60,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#fff;background:rgba(255,255,255,0.8);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(255,255,255,0.9)),to(rgba(200,200,200,0.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-moz-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-o-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:-ms-linear-gradient(top,rgba(255,255,255,0.9),rgba(200,200,200,0.8));background:linear-gradient(rgba(255,255,255,0.9),rgba(200,200,200,0.8));}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-handle{display:none;}.mejs-controls .mejs-captions-button{position:relative;}.mejs-controls .mejs-captions-button button{background-position:-48px 0;}.mejs-controls .mejs-captions-button .mejs-captions-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-controls .mejs-captions-button .mejs-captions-translations{font-size:10px;margin:0 0 5px 0;}.mejs-chapters{position:absolute;top:0;left:0;-xborder-right:solid 1px #fff;width:10000px;z-index:1;}.mejs-chapters .mejs-chapter{position:absolute;float:left;background:#222;background:rgba(0,0,0,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(50,50,50,0.7)),to(rgba(0,0,0,0.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-moz-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-o-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:-ms-linear-gradient(top,rgba(50,50,50,0.7),rgba(0,0,0,0.7));background:linear-gradient(rgba(50,50,50,0.7),rgba(0,0,0,0.7));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#323232,endColorstr=#000000);overflow:hidden;border:0;}.mejs-chapters .mejs-chapter .mejs-chapter-block{font-size:11px;color:#fff;padding:5px;display:block;border-right:solid 1px #333;border-bottom:solid 1px #333;cursor:pointer;}.mejs-chapters .mejs-chapter .mejs-chapter-block-last{border-right:none;}.mejs-chapters .mejs-chapter .mejs-chapter-block:hover{background:#666;background:rgba(102,102,102,0.7);background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(102,102,102,0.7)),to(rgba(50,50,50,0.6)));background:-webkit-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-moz-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-o-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:-ms-linear-gradient(top,rgba(102,102,102,0.7),rgba(50,50,50,0.6));background:linear-gradient(rgba(102,102,102,0.7),rgba(50,50,50,0.6));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,startColorstr=#666666,endColorstr=#323232);}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-title{font-size:12px;font-weight:bold;display:block;white-space:nowrap;text-overflow:ellipsis;margin:0 0 3px 0;line-height:12px;}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-timespan{font-size:12px;line-height:12px;margin:3px 0 4px 0;display:block;white-space:nowrap;text-overflow:ellipsis;}.mejs-captions-layer{position:absolute;bottom:0;left:0;text-align:center;line-height:22px;font-size:12px;color:#fff;}.mejs-captions-layer a{color:#fff;text-decoration:underline;}.mejs-captions-layer[lang=ar]{font-size:20px;font-weight:normal;}.mejs-captions-position{position:absolute;width:100%;bottom:15px;left:0;}.mejs-captions-position-hover{bottom:45px;}.mejs-captions-text{padding:3px 5px;background:url(background.png);background:rgba(20,20,20,0.8);}.me-cannotplay a{color:#fff;font-weight:bold;}.me-cannotplay span{padding:15px;display:block;}.mejs-controls .mejs-loop-off button{background-position:-64px -16px;}.mejs-controls .mejs-loop-on button{background-position:-64px 0;}.mejs-controls .mejs-backlight-off button{background-position:-80px -16px;}.mejs-controls .mejs-backlight-on button{background-position:-80px 0;}.mejs-controls .mejs-picturecontrols-button{background-position:-96px 0;}.mejs-contextmenu{position:absolute;width:150px;padding:10px;border-radius:4px;top:0;left:0;background:#fff;border:solid 1px #999;z-index:1001;}.mejs-contextmenu .mejs-contextmenu-separator{height:1px;font-size:0;margin:5px 6px;background:#333;}.mejs-contextmenu .mejs-contextmenu-item{font-family:Helvetica,Arial;font-size:12px;padding:4px 6px;cursor:pointer;color:#333;}.mejs-contextmenu .mejs-contextmenu-item:hover{background:#2C7C91;color:#fff;}.mejs-controls .mejs-sourcechooser-button{position:relative;}.mejs-controls .mejs-sourcechooser-button button{background-position:-128px 0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,0.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li{margin:0 0 6px 0;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li label{width:100px;float:left;padding:4px 0 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px;}.mejs-postroll-layer{position:absolute;bottom:0;left:0;width:100%;height:100%;background:url(background.png);background:rgba(50,50,50,0.7);z-index:1000;overflow:hidden;}.mejs-postroll-layer-content{width:100%;height:100%;}.mejs-postroll-close{position:absolute;right:0;top:0;background:url(background.png);background:rgba(50,50,50,0.7);color:#fff;padding:4px;z-index:100;cursor:pointer;}
361
+
362
+ .fvfp_admin_error { position: absolute; top: 10%; z-index: 2; text-align: center; width: 100%; /*color: #fff;*/ }
363
+ .fvfp_admin_error a { /*color: #fff;*/ text-decoration: underline; }
364
+ #content .fvfp_admin_error a { /*color: #fff;*/ text-decoration: underline; }
365
+ .fvfp_admin_error_content { /*background: #dd0000;*/ padding: 1% 5%; width: 65%; margin: 0 auto; -webkit-box-shadow: 3px 3px 30px 0px rgba(50, 50, 50, 0.96); -moz-box-shadow: 3px 3px 30px 0px rgba(50, 50, 50, 0.96); box-shadow: 3px 3px 30px 0px rgba(50, 50, 50, 0.96);}
366
+
css/img/flp-share-icons.png ADDED
Binary file
css/{ad.css → license.css} RENAMED
@@ -11,6 +11,16 @@
11
  text-rendering: optimizelegibility;
12
  color: #353535;
13
  }
 
 
 
 
 
 
 
 
 
 
14
  .text-part {
15
  float: left;
16
  width: 310px;
11
  text-rendering: optimizelegibility;
12
  color: #353535;
13
  }
14
+ #fv_flowplayer_addon_pro {
15
+ float: left;
16
+ display: block;
17
+ margin: 10px;
18
+ padding: 0 10px;
19
+ border: 1px solid #ccc;
20
+ background: url("../images/fabric-plaid-bg.png");
21
+ text-rendering: optimizelegibility;
22
+ color: #353535;
23
+ }
24
  .text-part {
25
  float: left;
26
  width: 310px;
flowplayer.php CHANGED
@@ -3,8 +3,7 @@
3
  Plugin Name: FV Wordpress Flowplayer
4
  Plugin URI: http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer
5
  Description: Embed videos (MP4, WEBM, OGV, FLV) into posts or pages. Uses Flowplayer 5.
6
- Version: 2.1.52
7
- Author: Foliovision
8
  Author URI: http://foliovision.com/
9
  License: GPL-3.0
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.txt
@@ -36,7 +35,7 @@ if( is_admin() ) {
36
 
37
  include( dirname( __FILE__ ) . '/controller/backend.php' );
38
 
39
- register_activation_hook( __FILE__, 'flowplayer_activate' );
40
 
41
  } else {
42
  /**
@@ -46,7 +45,6 @@ if( is_admin() ) {
46
  require_once( dirname( __FILE__ ) . '/controller/shortcodes.php');
47
  }
48
 
49
- $fv_wp_flowplayer_ver = '2.1.52';
50
- $fv_wp_flowplayer_core_ver = '5.4.6';
51
 
52
- ?>
 
3
  Plugin Name: FV Wordpress Flowplayer
4
  Plugin URI: http://foliovision.com/wordpress/plugins/fv-wordpress-flowplayer
5
  Description: Embed videos (MP4, WEBM, OGV, FLV) into posts or pages. Uses Flowplayer 5.
6
+ Version: 2.2.9
 
7
  Author URI: http://foliovision.com/
8
  License: GPL-3.0
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.txt
35
 
36
  include( dirname( __FILE__ ) . '/controller/backend.php' );
37
 
38
+ register_deactivation_hook( __FILE__, 'flowplayer_deactivate' );
39
 
40
  } else {
41
  /**
45
  require_once( dirname( __FILE__ ) . '/controller/shortcodes.php');
46
  }
47
 
 
 
48
 
49
+ $fv_wp_flowplayer_ver = '2.2.9';
50
+ $fv_wp_flowplayer_core_ver = '5.4.6';
flowplayer/fv-flowplayer.min.js CHANGED
@@ -3,11 +3,15 @@
3
  Flowplayer Unlimited v5.4.6 (2013-12-17) | flowplayer.org/license
4
 
5
  */!function(e){function u(t,n){var r="obj"+(""+Math.random()).slice(2,15),i='<object class="fp-engine" id="'+r+'" name="'+r+'" ';i+=e.browser.msie?'classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">':' data="'+t+'" type="application/x-shockwave-flash">';var s={width:"100%",height:"100%",allowscriptaccess:"always",wmode:"transparent",quality:"high",flashvars:"",movie:t+(e.browser.msie?"?"+r:""),name:r};return e.each(n,function(e,t){s.flashvars+=e+"="+t+"&"}),e.each(s,function(e,t){i+='<param name="'+e+'" value="'+t+'"/>'}),i+="</object>",e(i)}function l(e,t){return t=t||100,Math.round(e*t)/t}function c(e){return/mpegurl/i.test(e)?"application/x-mpegurl":"video/"+e}function h(e){return/^(video|application)/.test(e)||(e=c(e)),!!a.canPlayType(e).replace("no","")}function p(t,n){var r=e.grep(t,function(e){return e.type===n});return r.length?r[0]:null}function g(e){var t=e.attr("src"),n=e.attr("type")||"",r=t.split(m)[1];return n=/mpegurl/.test(n)?"mpegurl":n.replace("video/",""),{src:t,suffix:r||n,type:n||r}}function y(t){var n=this,r=[];e("source",t).each(function(){r.push(g(e(this)))}),r.length||r.push(g(t)),n.initialSources=r,n.resolve=function(t){return t?(e.isArray(t)?t={sources:e.map(t,function(t){var n,r=e.extend({},t);return e.each(t,function(e,t){n=e}),r.type=n,r.src=t[n],delete r[n],r})}:typeof t=="string"&&(t={src:t,sources:[]},e.each(r,function(e,n){n.type!="flash"&&t.sources.push({type:n.type,src:t.src.replace(m,"."+n.suffix+"$2")})})),t):{sources:r}}}function b(e){return e=parseInt(e,10),e>=10?e:"0"+e}function w(e){e=e||0;var t=Math.floor(e/3600),n=Math.floor(e/60);return e-=n*60,t>=1?(n-=t*60,t+":"+b(n)+":"+b(e)):b(n)+":"+b(e)}!function(e){if(!e.browser){var t=e.browser={},n=navigator.userAgent.toLowerCase(),r=/(chrome)[ \/]([\w.]+)/.exec(n)||/(safari)[ \/]([\w.]+)/.exec(n)||/(webkit)[ \/]([\w.]+)/.exec(n)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(n)||/(msie) ([\w.]+)/.exec(n)||n.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(n)||[];r[1]&&(t[r[1]]=!0,t.version=r[2]||"0")}}(jQuery),e(function(){typeof e.fn.flowplayer=="function"&&e("video").parent(".flowplayer").flowplayer()});var t=[],n=[],r=window.navigator.userAgent;window.flowplayer=function(r){return e.isFunction(r)?n.push(r):typeof r=="number"||r===undefined?t[r||0]:e(r).data("flowplayer")},e(window).on("beforeunload",function(){e.each(t,function(t,n){n.conf.splash?n.unload():n.bind("error",function(){e(".flowplayer.is-error .fp-message").remove()})})});var i=!1;try{typeof window.localStorage=="object"&&(window.localStorage.flowplayerTestStorage="test",i=!0)}catch(s){}e.extend(flowplayer,{version:"5.4.6",engine:{},conf:{},support:{},defaults:{debug:!1,disabled:!1,engine:"html5",fullscreen:window==window.top,keyboard:!0,ratio:9/16,adaptiveRatio:!1,flashfit:!1,rtmp:0,splash:!1,live:!1,swf:"//releases.flowplayer.org/5.4.6/commercial/flowplayer.swf",speeds:[.25,.5,1,1.5,2],tooltip:!0,volume:i?localStorage.muted=="true"?0:isNaN(localStorage.volume)?1:localStorage.volume||1:1,errors:["","Video loading aborted","Network error","Video not properly encoded","Video file not found","Unsupported video","Skin not found","SWF file not found","Subtitles not found","Invalid RTMP URL","Unsupported video format. Try installing Adobe Flash."],errorUrls:["","","","","","","","","","","http://get.adobe.com/flashplayer/"],playlist:[]}});var o=1;e.fn.flowplayer=function(r,i){return typeof r=="string"&&(r={swf:r}),e.isFunction(r)&&(i=r,r={}),!r&&this.data("flowplayer")||this.each(function(){var s=e(this).addClass("is-loading"),u=e.extend({},flowplayer.defaults,flowplayer.conf,r,s.data()),a=e("video",s).addClass("fp-engine").removeAttr("controls"),f=a.length?new y(a):null,l={},c,h;if(u.playlist.length){var p=a.attr("preload"),d;a.length&&a.replaceWith(d=e("<p />")),a=e("<video />").addClass("fp-engine"),d?d.replaceWith(a):s.prepend(a),flowplayer.support.video&&a.attr("preload",p),typeof u.playlist[0]=="string"?a.attr("src",u.playlist[0]):e.each(u.playlist[0],function(t,n){for(var r in n)n.hasOwnProperty(r)&&a.append(e("<source />").attr({type:"video/"+r,src:n[r]}))}),f=new y(a)}var v=s.data("flowplayer");v&&v.unload(),s.data("fp-player_id",s.data("fp-player_id")||o++);try{l=window.localStorage||l}catch(m){}var g=this.currentStyle&&this.currentStyle.direction==="rtl"||window.getComputedStyle&&window.getComputedStyle(this,null).getPropertyValue("direction")==="rtl";g&&s.addClass("is-rtl");var b=v||{conf:u,currentSpeed:1,volumeLevel:typeof u.volume=="undefined"?l.volume*1:u.volume,video:{},disabled:!1,finished:!1,loading:!1,muted:l.muted=="true"||u.muted,paused:!1,playing:!1,ready:!1,splash:!1,rtl:g,load:function(t,n){if(b.error||b.loading||b.disabled)return;t=f.resolve(t),e.extend(t,h.pick(t.sources));if(t.src){var r=e.Event("load");s.trigger(r,[b,t,h]),r.isDefaultPrevented()?b.loading=!1:(h.load(t),e.isFunction(t)&&(n=t),n&&s.one("ready",n))}return b},pause:function(e){return b.ready&&!b.seeking&&!b.disabled&&!b.loading&&(h.pause(),b.one("pause",e)),b},resume:function(){return b.ready&&b.paused&&!b.disabled&&(h.resume(),b.finished&&(b.trigger("resume",[b]),b.finished=!1)),b},toggle:function(){return b.ready?b.paused?b.resume():b.pause():b.load()},seek:function(t,n){if(b.ready){if(typeof t=="boolean"){var r=b.video.duration*.1;t=b.video.time+(t?r:-r)}t=c=Math.min(Math.max(t,0),b.video.duration).toFixed(1);var i=e.Event("beforeseek");s.trigger(i,[b,t]),i.isDefaultPrevented()?(b.seeking=!1,s.toggleClass("is-seeking",b.seeking)):(h.seek(t),e.isFunction(n)&&s.one("seek",n))}return b},seekTo:function(e,t){var n=e===undefined?c:b.video.duration*.1*e;return b.seek(n,t)},mute:function(e){return e===undefined&&(e=!b.muted),l.muted=b.muted=e,l.volume=isNaN(l.volume)?u.volume:l.volume,b.volume(e?0:l.volume,!0),b.trigger("mute",e),b},volume:function(e,t){return b.ready&&(e=Math.min(Math.max(e,0),1),t||(l.volume=e),h.volume(e)),b},speed:function(t,n){return b.ready&&(typeof t=="boolean"&&(t=u.speeds[e.inArray(b.currentSpeed,u.speeds)+(t?1:-1)]||b.currentSpeed),h.speed(t),n&&s.one("speed",n)),b},stop:function(){return b.ready&&(b.pause(),b.seek(0,function(){s.trigger("stop")})),b},unload:function(){return s.hasClass("is-embedding")||(u.splash?(b.trigger("unload"),h.unload()):b.stop()),b},disable:function(e){return e===undefined&&(e=!b.disabled),e!=b.disabled&&(b.disabled=e,b.trigger("disable",e)),b}};b.conf=e.extend(b.conf,u),e.each(["bind","one","unbind"],function(e,t){b[t]=function(e,n){return s[t](e,n),b}}),b.trigger=function(e,t){return s.trigger(e,[b,t]),b},s.data("flowplayer")||s.bind("boot",function(){e.each(["autoplay","loop","preload","poster"],function(e,t){var n=a.attr(t);n!==undefined&&(u[t]=n?n:!0)});if(u.splash||s.hasClass("is-splash")||!flowplayer.support.firstframe)b.forcedSplash=!u.splash&&!s.hasClass("is-splash"),b.splash=u.splash=u.autoplay=!0,s.addClass("is-splash"),flowplayer.support.video&&a.attr("preload","none");if(u.live||s.hasClass("is-live"))b.live=u.live=!0,s.addClass("is-live");e.each(n,function(e){this(b,s)}),h=flowplayer.engine[u.engine],h&&(h=h(b,s)),h.pick(f.initialSources)?b.engine=u.engine:e.each(flowplayer.engine,function(e,t){if(e!=u.engine)return h=this(b,s),h.pick(f.initialSources)&&(b.engine=e),!1}),t.push(b);if(!b.engine)return b.trigger("error",{code:flowplayer.support.flashVideo?5:10});u.splash?b.unload():b.load(),u.disabled&&b.disable(),h.volume(b.volumeLevel),s.one("ready",i)}).bind("load",function(t,n,r){u.splash&&e(".flowplayer").filter(".is-ready, .is-loading").not(s).each(function(){var t=e(this).data("flowplayer");t.conf.splash&&t.unload()}),s.addClass("is-loading"),n.loading=!0}).bind("ready",function(e,t,n){function r(){s.removeClass("is-loading"),t.loading=!1}n.time=0,t.video=n,u.splash?s.one("progress",r):r(),t.muted?t.mute(!0):t.volume(t.volumeLevel)}).bind("unload",function(e){u.splash&&a.remove(),s.removeClass("is-loading"),b.loading=!1}).bind("ready unload",function(e){var t=e.type=="ready";s.toggleClass("is-splash",!t).toggleClass("is-ready",t),b.ready=t,b.splash=!t}).bind("progress",function(e,t,n){t.video.time=n}).bind("speed",function(e,t,n){t.currentSpeed=n}).bind("volume",function(e,t,n){t.volumeLevel=Math.round(n*100)/100,t.muted?n&&t.mute(!1):l.volume=n}).bind("beforeseek seek",function(e){b.seeking=e.type=="beforeseek",s.toggleClass("is-seeking",b.seeking)}).bind("ready pause resume unload finish stop",function(e,t,n){b.paused=/pause|finish|unload|stop/.test(e.type),e.type=="ready"&&(b.paused=u.preload=="none",n&&(b.paused=!n.duration||!u.autoplay&&u.preload!="none")),b.playing=!b.paused,s.toggleClass("is-paused",b.paused).toggleClass("is-playing",b.playing),b.load.ed||b.pause()}).bind("finish",function(e){b.finished=!0}).bind("error",function(){a.remove()}),s.trigger("boot",[b,s]).data("flowplayer",b)})},!function(){var t=function(e){var t=/Version\/(\d\.\d)/.exec(e);return t&&t.length>1?parseFloat(t[1],10):0},n=flowplayer.support,r=e.browser,i=e("<video loop autoplay preload/>")[0],s=r.msie,o=navigator.userAgent,u=/iPad|MeeGo/.test(o)&&!/CriOS/.test(o),a=/iPad/.test(o)&&/CriOS/.test(o),f=/iP(hone|od)/i.test(o)&&!/iPad/.test(o),l=/Android/.test(o)&&!/Firefox/.test(o),c=/Android/.test(o)&&/Firefox/.test(o),h=/Silk/.test(o),p=/IEMobile/.test(o),d=u?t(o):0,v=l?parseFloat(/Android\ (\d\.\d)/.exec(o)[1],10):0;e.extend(n,{subtitles:!!i.addTextTrack,fullscreen:!l&&(typeof document.webkitCancelFullScreen=="function"&&!/Mac OS X 10_5.+Version\/5\.0\.\d Safari/.test(o)||document.mozFullScreenEnabled||typeof document.exitFullscreen=="function"),inlineBlock:!(s&&r.version<8),touch:"ontouchstart"in window,dataload:!u&&!f&&!p,zeropreload:!s&&!l,volume:!u&&!l&&!f&&!h&&!a,cachedVideoTag:!u&&!f&&!a&&!p,firstframe:!f&&!u&&!l&&!h&&!a&&!p&&!c,inlineVideo:!f&&!p&&(!l||v>=3),hlsDuration:!r.safari||u||f||a,seekable:!u&&!a});try{var m=navigator.plugins["Shockwave Flash"],g=s?(new ActiveXObject("ShockwaveFlash.ShockwaveFlash")).GetVariable("$version"):m.description;!s&&!m[0].enabledPlugin?n.flashVideo=!1:(g=g.split(/\D+/),g.length&&!g[0]&&(g=g.slice(1)),n.flashVideo=g[0]>9||g[0]==9&&g[3]>=115)}catch(y){}try{n.video=!!i.canPlayType,n.video&&i.canPlayType("video/mp4")}catch(b){n.video=!1}n.animation=function(){var t=["","Webkit","Moz","O","ms","Khtml"],n=e("<p/>")[0];for(var r=0;r<t.length;r++)if(n.style[t[r]+"AnimationName"]!=="undefined")return!0}()}(),window.attachEvent&&window.attachEvent("onbeforeunload",function(){__flash_savedUnloadHandler=__flash_unloadHandler=function(){}}),flowplayer.engine.flash=function(t,n){var r=t.conf,i=t.video,s,o,a,f={pick:function(t){if(flowplayer.support.flashVideo){var n=e.grep(t,function(e){return e.type=="flash"})[0];if(n)return n;for(var r=0,i;r<t.length;r++){i=t[r];if(/mp4|flv/.test(i.type))return i}}},load:function(i){function f(e){return e.replace(/&amp;/g,"%26").replace(/&/g,"%26").replace(/=/g,"%3D")}var l=e("video",n),c=f(i.src);is_absolute=/^https?:/.test(c);try{l.length>0&&flowplayer.support.video&&l[0].pause()}catch(h){}var p=function(){l.remove()},d=function(t){return e.grep(t,function(e){return!!l[0].canPlayType("video/"+e.type)}).length>0};flowplayer.support.video&&l.prop("autoplay")&&d(i.sources)?l.one("timeupdate",p):p(),!is_absolute&&!r.rtmp&&(c=e("<img/>").attr("src",c)[0].src);if(a)a.__play(c);else{s="fp"+(""+Math.random()).slice(3,15);var v={hostname:r.embedded?r.hostname:location.hostname,url:c,callback:"jQuery."+s};n.data("origin")&&(v.origin=n.data("origin")),is_absolute&&delete r.rtmp,e.each(["key","autoplay","preload","rtmp","loop","debug","preload","splash","bufferTime"],function(e,t){r[t]&&(v[t]=r[t])}),v.rtmp&&(v.rtmp=f(v.rtmp)),o=u(r.swf,v),o.prependTo(n),a=o[0],setTimeout(function(){try{if(!a.PercentLoaded())return n.trigger("error",[t,{code:7,url:r.swf}])}catch(e){}},5e3),setTimeout(function(){typeof a.PercentLoaded=="undefined"&&n.trigger("flashdisabled",[t])},1e3),e[s]=function(n,s){r.debug&&n!="status"&&console.log("--",n,s);var o=e.Event(n);switch(n){case"ready":s=e.extend(i,s);break;case"click":o.flash=!0;break;case"keydown":o.which=s;break;case"seek":i.time=s;break;case"status":t.trigger("progress",s.time),s.buffer<i.bytes&&!i.buffered?(i.buffer=s.buffer/i.bytes*i.duration,t.trigger("buffer",i.buffer)):i.buffered||(i.buffered=!0,t.trigger("buffered"))}n!="buffered"&&setTimeout(function(){t.trigger(o,s)},1)}}},speed:e.noop,unload:function(){a&&a.__unload&&a.__unload(),delete e[s],e("object",n).remove(),a=0}};e.each("pause,resume,seek,volume".split(","),function(e,r){f[r]=function(e){try{t.ready&&(r=="seek"&&t.video.time&&!t.paused&&t.trigger("beforeseek"),e===undefined?a["__"+r]():a["__"+r](e))}catch(i){if(typeof a["__"+r]=="undefined")return n.trigger("flashdisabled",[t]);throw i}}});var l=e(window);return t.bind("ready fullscreen fullscreen-exit",function(r){var i=n.height(),s=n.width();if(t.conf.flashfit||/full/.test(r.type)){var o=t.isFullscreen,u=o&&L,a=!flowplayer.support.inlineBlock,f=o?u?screen.width:l.width():s,c=o?u?screen.height:l.height():i,h=0,p=0,d=a?s:"",v=a?i:"",m,g;if(t.conf.flashfit||r.type==="fullscreen")m=t.video.width/t.video.height,g=t.video.height/t.video.width,v=Math.max(g*f),d=Math.max(m*c),v=v>c?d*g:v,v=Math.min(Math.round(v),c),d=d>f?v*m:d,d=Math.min(Math.round(d),f),p=Math.max(Math.round((c+p-v)/2),0),h=Math.max(Math.round((f+h-d)/2),0);e("object",n).css({width:d,height:v,marginTop:p,marginLeft:h})}}),f};var a=e("<video/>")[0],f={ended:"finish",pause:"pause",play:"resume",progress:"buffer",timeupdate:"progress",volumechange:"volume",ratechange:"speed",seeked:"seek",loadeddata:"ready",error:"error",dataunavailable:"error"},d,v=function(t){return d?d.attr({type:c(t.type),src:t.src}):d=e("<video/>",{src:t.src,type:c(t.type),"class":"fp-engine",autoplay:"autoplay",preload:"none","x-webkit-airplay":"allow"})};flowplayer.engine.html5=function(t,n){function m(r,s,u){if(r.listeners&&r.listeners.hasOwnProperty(n.data("fp-player_id")))return;(r.listeners||(r.listeners={}))[n.data("fp-player_id")]=!0,s.bind("error",function(n){try{if(n.originalEvent&&e(n.originalEvent.originalTarget).is("img"))return n.preventDefault();h(e(n.target).attr("type"))&&t.trigger("error",{code:4})}catch(r){}}),e.each(f,function(s,f){r.addEventListener(s,function(c){f=="progress"&&c.srcElement&&c.srcElement.readyState===0&&setTimeout(function(){t.video.duration||(f="error",t.trigger(f,{code:4}))},1e4),o.debug&&!/progress/.test(f)&&console.log(s,"->",f,c);if(!t.ready&&!/ready|error/.test(f)||!f||!e("video",n).length)return;var h=e.Event(f),p;switch(f){case"ready":p=e.extend(u,{duration:r.duration,width:r.videoWidth,height:r.videoHeight,url:r.currentSrc,src:r.currentSrc});try{p.seekable=r.seekable&&r.seekable.end(null)}catch(d){}a=a||setInterval(function(){try{p.buffer=r.buffered.end(null)}catch(e){}p.buffer&&(l(p.buffer,1e3)<l(p.duration,1e3)&&!p.buffered?t.trigger("buffer",c):p.buffered||(p.buffered=!0,t.trigger("buffer",c).trigger("buffered",c),clearInterval(a),a=0))},250);if(!o.live&&!p.duration&&!i.hlsDuration&&s==="loadeddata"){var v=function(){p.duration=r.duration;try{p.seekable=r.seekable&&r.seekable.end(null)}catch(e){}t.trigger(h,p),r.removeEventListener("durationchange",v)};r.addEventListener("durationchange",v);return}break;case"progress":case"seek":var m=t.video.duration;if(r.currentTime>0){p=Math.max(r.currentTime,0);break}if(f=="progress")return;case"speed":p=l(r.playbackRate);break;case"volume":p=l(r.volume);break;case"error":try{p=(c.srcElement||c.originalTarget).error}catch(g){return}}t.trigger(h,p)},!1)})}var r=e("video",n),i=flowplayer.support,s=e("track",r),o=t.conf,u,a,c;return u={pick:function(e){if(i.video){if(o.videoTypePreference){var t=p(e,o.videoTypePreference);if(t)return t}for(var n=0,r;n<e.length;n++)if(h(e[n].type))return e[n]}},load:function(u){if(o.splash&&!c)r=v(u).prependTo(n),i.inlineVideo||r.css({position:"absolute",top:"-9999em"}),s.length&&r.append(s.attr("default","")),o.loop&&r.attr("loop","loop"),c=r[0];else{c=r[0];var a=r.find("source");!c.src&&a.length&&(c.src=u.src,a.remove());if(t.video.src&&u.src!=t.video.src)r.attr("autoplay","autoplay"),c.src=u.src;else if(o.preload=="none"||!i.dataload)i.zeropreload?t.trigger("ready",u).trigger("pause").one("ready",function(){n.trigger("resume",[t])}):t.one("ready",function(){n.trigger("pause",[t])})}m(c,e("source",r).add(r),u),(o.preload!="none"||!i.zeropreload||!i.dataload)&&c.load(),o.splash&&c.load()},pause:function(){c.pause()},resume:function(){c.play()},speed:function(e){c.playbackRate=e},seek:function(e){try{var n=t.paused;c.currentTime=e,n&&c.pause()}catch(r){}},volume:function(e){c.volume=e},unload:function(){e("video.fp-engine",n).remove(),i.cachedVideoTag||(d=null),a=clearInterval(a),c=0}}};var m=/\.(\w{3,4})(\?.*)?$/i;e.throttle=function(e,t){var n;return function(){n||(e.apply(this,arguments),n=1,setTimeout(function(){n=0},t))}},e.fn.slider2=function(t){var n=/iPad/.test(navigator.userAgent)&&!/CriOS/.test(navigator.userAgent);return this.each(function(){var r=e(this),i=e(document),s=r.children(":last"),o,u,a,f,l,c,h,p,d=!1,v=function(){u=r.offset(),a=r.width(),f=r.height(),c=l?f:a,p=b(h)},m=function(e){!o&&e!=w.value&&(!h||e<h)&&(r.trigger("slide",[e]),w.value=e)},g=function(e){var n=e.pageX;!n&&e.originalEvent&&e.originalEvent.touches&&e.originalEvent.touches.length&&(n=e.originalEvent.touches[0].pageX);var r=l?e.pageY-u.top:n-u.left;r=Math.max(0,Math.min(p||c,r));var i=r/c;return l&&(i=1-i),t&&(i=1-i),y(i,0,!0)},y=function(e,t){t===undefined&&(t=0),e>1&&(e=1);var r=Math.round(e*1e3)/10+"%";if(!h||e<=h)n||s.stop(),d?s.css("width",r):s.animate(l?{height:r}:{width:r},t,"linear");return e},b=function(e){return Math.max(0,Math.min(c,l?(1-e)*f:e*a))},w={max:function(e){h=e},disable:function(e){o=e},slide:function(e,t,n){v(),n&&m(e),y(e,t)},disableAnimation:function(e){d=e!==!1}};v(),r.data("api",w).bind("mousedown.sld touchstart",function(t){t.preventDefault();if(!o){var n=e.throttle(m,100);v(),w.dragging=!0,r.addClass("is-dragging"),m(g(t)),i.bind("mousemove.sld touchmove",function(e){e.preventDefault(),n(g(e))}).one("mouseup touchend",function(){w.dragging=!1,r.removeClass("is-dragging"),i.unbind("mousemove.sld touchmove")})}})})},flowplayer(function(t,n){function o(t){return e(".fp-"+t,n)}function T(t){if(n.css("width")==="0px"||n.css("height")==="0px"||t!==flowplayer.defaults.ratio)parseInt(v,10)||h.css("paddingTop",t*100+"%");i.inlineBlock||e("object",n).height(n.height())}function N(e){n.toggleClass("is-mouseover",e).toggleClass("is-mouseout",!e)}var r=t.conf,i=flowplayer.support,s;n.find(".fp-ratio,.fp-ui").remove(),n.addClass("flowplayer").append(' <div class="ratio"/> <div class="ui"> <div class="waiting"><em/><em/><em/></div> <a class="fullscreen"/> <a class="unload"/> <p class="speed"/> <div class="controls"> <a class="play"></a> <div class="timeline"> <div class="buffer"/> <div class="progress"/> </div> <div class="volume"> <a class="mute"></a> <div class="volumeslider"> <div class="volumelevel"/> </div> </div> </div> <div class="time"> <em class="elapsed">00:00</em> <em class="remaining"/> <em class="duration">00:00</em> </div> <div class="message"><h2/><p/></div> </div>'.replace(/class="/g,'class="fp-'));var u=o("progress"),a=o("buffer"),f=o("elapsed"),l=o("remaining"),c=o("waiting"),h=o("ratio"),p=o("speed"),d=o("duration"),v=h.css("paddingTop"),m=o("timeline").slider2(t.rtl),g=m.data("api"),y=o("volume"),b=o("fullscreen"),E=o("volumeslider").slider2(t.rtl),S=E.data("api"),x=n.is(".fixed-controls, .no-toggle");g.disableAnimation(n.hasClass("is-touch")),i.animation||c.html("<p>loading &hellip;</p>"),T(r.ratio);try{r.fullscreen||b.remove()}catch(C){b.remove()}t.bind("ready",function(){var e=t.video.duration;g.disable(t.disabled||!e),r.adaptiveRatio&&T(t.video.height/t.video.width),d.add(l).html(w(e)),e>=3600&&n.addClass("is-long")||n.removeClass("is-long"),S.slide(t.volumeLevel)}).bind("unload",function(){v||h.css("paddingTop","")}).bind("buffer",function(){var e=t.video,n=e.buffer/e.duration;!e.seekable&&i.seekable&&g.max(n),n<1?a.css("width",n*100+"%"):a.css({width:"100%"})}).bind("speed",function(e,t,n){p.text(n+"x").addClass("fp-hilite"),setTimeout(function(){p.removeClass("fp-hilite")},1e3)}).bind("buffered",function(){a.css({width:"100%"}),g.max(1)}).bind("progress",function(){var e=t.video.time,n=t.video.duration;g.dragging||g.slide(e/n,t.seeking?0:250),f.html(w(e)),l.html("-"+w(n-e))}).bind("finish resume seek",function(e){n.toggleClass("is-finished",e.type=="finish")}).bind("stop",function(){f.html(w(0)),g.slide(0,100)}).bind("finish",function(){f.html(w(t.video.duration)),g.slide(1,100),n.removeClass("is-seeking")}).bind("beforeseek",function(){u.stop()}).bind("volume",function(){S.slide(t.volumeLevel)}).bind("disable",function(){var e=t.disabled;g.disable(e),S.disable(e),n.toggleClass("is-disabled",t.disabled)}).bind("mute",function(e,t,r){n.toggleClass("is-muted",r)}).bind("error",function(t,i,s){n.removeClass("is-loading").addClass("is-error");if(s){s.message=r.errors[s.code],i.error=!0;var o=e(".fp-message",n);e("h2",o).text((i.engine||"html5")+": "+s.message),e("p",o).text(s.url||i.video.url||i.video.src||r.errorUrls[s.code]),n.unbind("mouseenter click").removeClass("is-mouseover")}}).bind("mouseenter mouseleave",function(e){if(x)return;var t=e.type=="mouseenter",r;N(t),t?(n.bind("pause.x mousemove.x volume.x",function(){N(!0),r=new Date}),s=setInterval(function(){new Date-r>2e3&&(N(!1),r=new Date)},100)):(n.unbind(".x"),clearInterval(s))}).bind("mouseleave",function(){(g.dragging||S.dragging)&&n.addClass("is-mouseover").removeClass("is-mouseout")}).bind("click.player",function(n){if(e(n.target).is(".fp-ui, .fp-engine")||n.flash)return n.preventDefault(),t.toggle()}).bind("contextmenu",function(t){t.preventDefault();var r=n.offset(),i=e(window),s=t.clientX-r.left,o=t.clientY-r.top+i.scrollTop(),u=n.find(".fp-context-menu").css({left:s+"px",top:o+"px",display:"block"}).on("click",function(e){e.stopPropagation()});e("html").on("click.outsidemenu",function(t){u.hide(),e("html").off("click.outsidemenu")})}).bind("flashdisabled",function(){n.addClass("is-flash-disabled").one("ready",function(){n.removeClass("is-flash-disabled").find(".fp-flash-disabled").remove()}).append('<div class="fp-flash-disabled">Adobe Flash is disabled for this page, click player area to enable.</div>')}),r.poster&&n.css("backgroundImage","url("+r.poster+")");var k=n.css("backgroundColor"),L=n.css("backgroundImage")!="none"||k&&k!="rgba(0, 0, 0, 0)"&&k!="transparent";L&&!r.splash&&!r.autoplay&&t.bind("ready stop",function(){n.addClass("is-poster").one("progress",function(){n.removeClass("is-poster")})}),!L&&t.forcedSplash&&n.css("backgroundColor","#555"),e(".fp-toggle, .fp-play",n).click(t.toggle),e.each(["mute","fullscreen","unload"],function(e,n){o(n).click(function(){t[n]()})}),m.bind("slide",function(e,n){t.seeking=!0,t.seek(n*t.video.duration)}),E.bind("slide",function(e,n){t.volume(n)}),o("time").click(function(t){e(this).toggleClass("is-inverted")}),N(x)});var E,S,x="is-help";e(document).bind("keydown.fp",function(t){var n=E,r=t.ctrlKey||t.metaKey||t.altKey,i=t.which,s=n&&n.conf;if(!n||!s.keyboard||n.disabled)return;if(e.inArray(i,[63,187,191])!=-1)return S.toggleClass(x),!1;if(i==27&&S.hasClass(x))return S.toggleClass(x),!1;if(!r&&n.ready){t.preventDefault();if(t.shiftKey){i==39?n.speed(!0):i==37&&n.speed(!1);return}if(i<58&&i>47)return n.seekTo(i-48);switch(i){case 38:case 75:n.volume(n.volumeLevel+.15);break;case 40:case 74:n.volume(n.volumeLevel-.15);break;case 39:case 76:n.seeking=!0,n.seek(!0);break;case 37:case 72:n.seeking=!0,n.seek(!1);break;case 190:n.seekTo();break;case 32:n.toggle();break;case 70:s.fullscreen&&n.fullscreen();break;case 77:n.mute();break;case 81:n.unload()}}}),flowplayer(function(t,n){if(!t.conf.keyboard)return;n.bind("mouseenter mouseleave",function(e){E=!t.disabled&&e.type=="mouseenter"?t:0,E&&(S=n)}),n.append(' <div class="fp-help"> <a class="fp-close"></a> <div class="fp-help-section fp-help-basics"> <p><em>space</em>play / pause</p> <p><em>q</em>unload | stop</p> <p><em>f</em>fullscreen</p> <p><em>shift</em> + <em>&#8592;</em><em>&#8594;</em>slower / faster <small>(latest Chrome and Safari)</small></p> </div> <div class="fp-help-section"> <p><em>&#8593;</em><em>&#8595;</em>volume</p> <p><em>m</em>mute</p> </div> <div class="fp-help-section"> <p><em>&#8592;</em><em>&#8594;</em>seek</p> <p><em>&nbsp;. </em>seek to previous </p><p><em>1</em><em>2</em>&hellip;<em>6</em> seek to 10%, 20%, &hellip;60% </p> </div> </div> '),t.conf.tooltip&&e(".fp-ui",n).attr("title","Hit ? for help").on("mouseout.tip",function(){e(this).removeAttr("title").off("mouseout.tip")}),e(".fp-close",n).click(function(){n.toggleClass(x)})});var T=e.browser.mozilla?"moz":"webkit",N="fullscreen",C="fullscreen-exit",k,L=flowplayer.support.fullscreen,A=typeof document.exitFullscreen=="function",O=navigator.userAgent.toLowerCase(),M=/(safari)[ \/]([\w.]+)/.exec(O)&&!/(chrome)[ \/]([\w.]+)/.exec(O);e(document).bind(A?"fullscreenchange":T+"fullscreenchange",function(t){var n=e(document.webkitCurrentFullScreenElement||document.mozFullScreenElement||document.fullscreenElement||t.target);n.length&&!k?k=n.trigger(N,[n]):(k.trigger(C,[k]),k=null)}),flowplayer(function(t,n){if(!t.conf.fullscreen)return;var r=e(window),i={index:0,pos:0,play:!1},s;t.isFullscreen=!1,t.fullscreen=function(o){if(t.disabled)return;return o===undefined&&(o=!t.isFullscreen),o&&(s=r.scrollTop()),(T=="webkit"||M)&&t.engine=="flash"&&(i.index=t.video.index,t.conf.rtmp&&e.extend(i,{pos:t.video.time,play:t.playing})),L?o?A?n[0].requestFullscreen():(n[0][T+"RequestFullScreen"](Element.ALLOW_KEYBOARD_INPUT),M&&!document.webkitCurrentFullScreenElement&&!document.mozFullScreenElement&&n[0][T+"RequestFullScreen"]()):A?document.exitFullscreen():document[T+"CancelFullScreen"]():t.trigger(o?N:C,[t]),t};var o;n.bind("mousedown.fs",function(){+(new Date)-o<150&&t.ready&&t.fullscreen(),o=+(new Date)}),t.bind(N,function(e){n.addClass("is-fullscreen"),t.isFullscreen=!0}).bind(C,function(e){var i;!L&&t.engine==="html5"&&(i=n.css("opacity")||"",n.css("opacity",0)),n.removeClass("is-fullscreen"),!L&&t.engine==="html5"&&setTimeout(function(){n.css("opacity",i)}),t.isFullscreen=!1,r.scrollTop(s)}).bind("ready",function(){if(i.index>0)t.play(i.index),i.index=0;else if(i.pos&&!isNaN(i.pos)){var n=function(){i.play||t.pause(),e.extend(i,{pos:0,play:!1})};t.conf.live?(t.resume(),n()):t.resume().seek(i.pos,n)}})}),flowplayer(function(t,n){function s(){return e(r.query,n)}function o(){return e(r.query+"."+i,n)}var r=e.extend({active:"is-active",advance:!0,query:".fp-playlist a"},t.conf),i=r.active;t.play=function(n){return n===undefined?t.resume():typeof n=="number"&&!t.conf.playlist[n]?t:(typeof n!="number"&&t.load.apply(null,arguments),t.unbind("resume.fromfirst"),t.video.index=n,t.load(typeof t.conf.playlist[n]=="string"?t.conf.playlist[n].toString():e.map(t.conf.playlist[n],function(t){return e.extend({},t)})),t)},t.next=function(e){e&&e.preventDefault();var n=t.video.index;return n!=-1&&(n=n===t.conf.playlist.length-1?0:n+1,t.play(n)),t},t.prev=function(e){e&&e.preventDefault();var n=t.video.index;return n!=-1&&(n=n===0?t.conf.playlist.length-1:n-1,t.play(n)),t},e(".fp-next",n).click(t.next),e(".fp-prev",n).click(t.prev),r.advance&&n.unbind("finish.pl").bind("finish.pl",function(e,t){var i=t.video.index+1;i<t.conf.playlist.length||r.loop?(i=i===t.conf.playlist.length?0:i,n.removeClass("is-finished"),setTimeout(function(){t.play(i)})):(n.addClass("is-playing"),t.conf.playlist.length>1&&t.one("resume.fromfirst",function(){return t.play(0),!1}))});var u=!1;if(t.conf.playlist.length){u=!0;var a=n.find(".fp-playlist");if(!a.length){a=e('<div class="fp-playlist"></div>');var f=e(".fp-next,.fp-prev",n);f.length?f.eq(0).before(a):e("video",n).after(a)}a.empty(),e.each(t.conf.playlist,function(t,n){var r;if(typeof n=="string")r=n;else for(var i in n[0])if(n[0].hasOwnProperty(i)){r=n[0][i];break}a.append(e("<a />").attr({href:r,"data-index":t}))})}if(s().length){u||(t.conf.playlist=[],s().each(function(){var n=e(this).attr("href");e(this).attr("data-index",t.conf.playlist.length),t.conf.playlist.push(n)})),n.on("click",r.query,function(n){n.preventDefault();var i=e(n.target).closest(r.query),s=Number(i.attr("data-index"));s!=-1&&t.play(s)});var l=s().filter("[data-cuepoints]").length;t.bind("load",function(r,s,u){var a=o().removeClass(i),f=a.attr("data-index"),c=u.index=t.video.index||0,h=e('a[data-index="'+c+'"]',n).addClass(i),p=c==t.conf.playlist.length-1;n.removeClass("video"+f).addClass("video"+c).toggleClass("last-video",p),u.index=s.video.index=c,u.is_last=s.video.is_last=p,l&&(t.cuepoints=h.data("cuepoints"))}).bind("unload.pl",function(){o().toggleClass(i)})}t.conf.playlist.length&&(t.conf.loop=!1)});var _=/ ?cue\d+ ?/;flowplayer(function(t,n){function i(e){n[0].className=n[0].className.replace(_," "),e>=0&&n.addClass("cue"+e)}var r=0;t.cuepoints=t.conf.cuepoints||[],t.bind("progress",function(e,s,o){if(r&&o-r<.015)return r=o;r=o;var u=t.cuepoints||[];for(var a=0,f;a<u.length;a++)f=u[a],isNaN(f)||(f={time:f}),f.time<0&&(f.time=t.video.duration+f.time),f.index=a,Math.abs(f.time-o)<.125*t.currentSpeed&&(i(a),n.trigger("cuepoint",[t,f]))}).bind("unload seek",i),t.conf.generate_cuepoints&&t.bind("load",function(){e(".fp-cuepoint",n).remove()}).bind("ready",function(){var r=t.cuepoints||[],i=t.video.duration,s=e(".fp-timeline",n).css("overflow","visible");e.each(r,function(n,r){var o=r.time||r;o<0&&(o=i+r);var u=e("<a/>").addClass("fp-cuepoint fp-cuepoint"+n).css("left",o/i*100+"%");u.appendTo(s).mousedown(function(){return t.seek(o),!1})})})}),flowplayer(function(t,n,r){function u(e){var t=e.split(":");return t.length==2&&t.unshift(0),t[0]*60*60+t[1]*60+parseFloat(t[2].replace(",","."))}var i=e("track",n),s=t.conf;if(flowplayer.support.subtitles){t.subtitles=i.length&&i[0].track;if(s.nativesubtitles&&s.engine=="html5")return}i.remove();var o=/^(([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3}) --\> (([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3})(.*)/;t.subtitles=[];var a=i.attr("src");if(!a)return;setTimeout(function(){e.get(a,function(n){for(var r=0,i=n.split("\n"),s=i.length,a={},f,l,c,h;r<s;r++){l=o.exec(i[r]);if(l){f=i[r-1],c="<p>"+i[++r]+"</p><br/>";while(e.trim(i[++r])&&r<i.length)c+="<p>"+i[r]+"</p><br/>";a={title:f,startTime:u(l[1]),endTime:u(l[2]||l[3]),text:c},h={time:a.startTime,subtitle:a},t.subtitles.push(a),t.cuepoints.push(h),t.cuepoints.push({time:a.endTime,subtitleEnd:f}),a.startTime===0&&t.trigger("cuepoint",h)}}}).fail(function(){return t.trigger("error",{code:8,url:a}),!1})});var f=e("<div class='fp-subtitle'/>").appendTo(n),l;t.bind("cuepoint",function(e,t,n){n.subtitle?(l=n.index,f.html(n.subtitle.text).addClass("fp-active")):n.subtitleEnd&&(f.removeClass("fp-active"),l=n.index)}).bind("seek",function(n,r,i){l&&t.cuepoints[l]&&t.cuepoints[l].time>i&&(f.removeClass("fp-active"),l=null),e.each(t.cuepoints||[],function(e,n){var r=n.subtitle;r&&l!=n.index?i>=n.time&&(!r.endTime||i<=r.endTime)&&t.trigger("cuepoint",n):n.subtitleEnd&&i>=n.time&&n.index==l+1&&t.trigger("cuepoint",n)})})}),flowplayer(function(t,n){var r=t.conf.analytics,i=0,s=0;if(r){typeof _gat=="undefined"&&e.getScript("//google-analytics.com/ga.js");function o(e){if(i&&typeof _gat!="undefined"){var s=_gat._getTracker(r),o=t.video;s._setAllowLinker(!0),s._trackEvent("Video / Seconds played",t.engine+"/"+o.type,n.attr("title")||o.src.split("/").slice(-1)[0].replace(m,""),Math.round(i/1e3)),i=0}}t.bind("load unload",o).bind("progress",function(){t.seeking||(i+=s?+(new Date)-s:0,s=+(new Date))}).bind("pause",function(){s=0}),e(window).unload(o)}});var D=/IEMobile/.test(r);(flowplayer.support.touch||D)&&flowplayer(function(t,n){var i=/Android/.test(r)&&!/Firefox/.test(r)&&!/Opera/.test(r),s=/Silk/.test(r),o=i?parseFloat(/Android\ (\d\.\d)/.exec(r)[1],10):0;if(i){t.conf.videoTypePreference="mp4";if(!/Chrome/.test(r)&&o<4){var u=t.load;t.load=function(e,n){var r=u.apply(t,arguments);return t.trigger("ready",[t,t.video]),r}}}flowplayer.support.volume||n.addClass("no-volume no-mute"),n.addClass("is-touch"),n.find(".fp-timeline").data("api").disableAnimation();var a=!1;n.bind("touchmove",function(){a=!0}).bind("touchend click",function(r){if(a){a=!1;return}if(t.playing&&!n.hasClass("is-mouseover"))
6
- return n.addClass("is-mouseover").removeClass("is-mouseout"),!1;t.paused&&n.hasClass("is-mouseout")&&!t.splash&&t.toggle(),t.paused&&D&&e("video.fp-engine",n)[0].play()}),t.conf.native_fullscreen&&typeof e("<video />")[0].webkitEnterFullScreen=="function"&&(t.fullscreen=function(){var t=e("video.fp-engine",n);t[0].webkitEnterFullScreen(),t.one("webkitendfullscreen",function(){t.prop("controls",!0).prop("controls",!1)})}),(i||s)&&t.bind("ready",function(){var r=e("video.fp-engine",n);r.one("canplay",function(){r[0].play()}),r[0].play(),t.bind("progress.dur",function(){var i=r[0].duration;i!==1&&(t.video.duration=i,e(".fp-duration",n).html(w(i)),t.unbind("progress.dur"))})})}),flowplayer(function(t,n){if(t.conf.embed===!1)return;var r=t.conf,i=e(".fp-ui",n),s=e("<a/>",{"class":"fp-embed",title:"Copy to your site"}).appendTo(i),o=e("<div/>",{"class":"fp-embed-code"}).append("<label>Right click to copy and paste this HTML code on your site to embed.</label><textarea/>").appendTo(i),u=e("textarea",o);t.embedCode=function(){var i=t.video,s=i.width||n.width(),o=i.height||n.height(),u=e("<div/>",{"class":"flowplayer",css:{width:s,height:o}}),a=e("<video/>").appendTo(u);e.each(["origin","analytics","key","rtmp"],function(e,t){r[t]&&u.attr("data-"+t,r[t])}),r.logo&&u.attr("data-logo",e("<img />").attr("src",r.logo)[0].src),e.each(i.sources,function(t,n){var i=n.src;if(!/^https?:/.test(n.src)&&n.type!=="flash"||!r.rtmp)i=e("<img/>").attr("src",n.src)[0].src;a.append(e("<source/>",{type:"video/"+n.type,src:i}))});var f={src:"//foliovision.com/flowplayer/5.4.6/embed.min.js"};e.isPlainObject(r.embed)&&(f["data-swf"]=r.embed.swf,f["data-library"]=r.embed.library,f.src=r.embed.script||f.src,r.embed.skin&&(f["data-skin"]=r.embed.skin));var l=e("<foo/>",f).append(u);return e("<p/>").append(l).html().replace(/<(\/?)foo/g,"<$1script")},n.fptip(".fp-embed","is-embedding"),u.click(function(){this.select()}),s.click(function(){u.text(t.embedCode()),u[0].focus(),u[0].select()})}),e.fn.fptip=function(t,n){return this.each(function(){function i(){r.removeClass(n),e(document).unbind(".st")}var r=e(this);e(t||"a",this).click(function(t){t.preventDefault(),r.toggleClass(n),r.hasClass(n)&&e(document).bind("keydown.st",function(e){e.which==27&&i()}).bind("click.st",function(t){e(t.target).parents("."+n).length||i()})})})}}(jQuery),flowplayer(function(e,t){function n(e){var t=s("<a/>")[0];return t.href=e,t.hostname}function r(e){var t="ab.ca,ac.ac,ac.at,ac.be,ac.cn,ac.il,ac.in,ac.jp,ac.kr,ac.th,ac.uk,adm.br,adv.br,ah.cn,am.br,arq.br,art.br,arts.ro,asn.au,asso.fr,asso.mc,bc.ca,bio.br,biz.pl,biz.tr,bj.cn,br.com,cn.com,cng.br,cnt.br,co.ac,co.at,co.gl,co.id,co.il,co.in,co.jp,co.kr,co.mg,co.ms,co.nz,co.th,co.uk,co.ve,co.vi,co.za,com.ag,com.ai,com.ar,com.au,com.br,com.cn,com.cy,com.de,com.do,com.ec,com.es,com.fj,com.fr,com.gl,com.gt,com.hk,com.hr,com.hu,com.kg,com.ki,com.lc,com.mg,com.mm,com.ms,com.mt,com.mu,com.mx,com.my,com.nf,com.ng,com.ni,com.pa,com.ph,com.pl,com.pt,com.qa,com.ro,com.ru,com.sb,com.sc,com.sg,com.sv,com.tr,com.tw,com.ua,com.uy,com.ve,com.vn,cq.cn,de.com,de.org,ecn.br,edu.au,edu.cn,edu.hk,edu.mm,edu.my,edu.pt,edu.qa,edu.tr,eng.br,ernet.in,esp.br,etc.br,eti.br,eu.com,eu.int,eu.lv,firm.in,firm.ro,fm.br,fot.br,fst.br,g12.br,gb.com,gb.net,gd.cn,gen.in,go.jp,go.kr,go.th,gov.au,gov.az,gov.br,gov.cn,gov.il,gov.in,gov.mm,gov.my,gov.qa,gov.sg,gov.tr,gov.tw,gs.cn,gv.ac,gv.at,gx.cn,gz.cn,he.cn,hi.cn,hk.cn,hl.cn,hu.com,id.au,idv.tw,in.ua,ind.br,ind.in,inf.br,info.pl,info.ro,info.tr,info.ve,iwi.nz,jl.cn,jor.br,js.cn,k12.il,k12.tr,kr.com,lel.br,ln.cn,ltd.uk,maori.nz,mb.ca,me.uk,med.br,mi.th,mil.br,mo.cn,muni.il,nb.ca,ne.jp,ne.kr,net.ag,net.ai,net.au,net.br,net.cn,net.do,net.gl,net.hk,net.il,net.in,net.kg,net.ki,net.lc,net.mg,net.mm,net.mu,net.ni,net.nz,net.pl,net.ru,net.sb,net.sc,net.sg,net.th,net.tr,net.tw,net.uk,net.ve,nf.ca,nm.cn,nm.kr,no.com,nom.br,nom.ni,nom.ro,ns.ca,nt.ca,nt.ro,ntr.br,nx.cn,odo.br,off.ai,on.ca,or.ac,or.at,or.jp,or.kr,or.th,org.ag,org.ai,org.au,org.br,org.cn,org.do,org.es,org.gl,org.hk,org.in,org.kg,org.ki,org.lc,org.mg,org.mm,org.ms,org.nf,org.ni,org.nz,org.pl,org.ro,org.ru,org.sb,org.sc,org.sg,org.tr,org.tw,org.uk,org.ve,pe.ca,plc.uk,ppg.br,presse.fr,pro.br,psc.br,psi.br,qc.ca,qc.com,qh.cn,rec.br,rec.ro,res.in,sa.com,sc.cn,sch.ul,se.com,se.net,sh.cn,sk.ca,slg.br,sn.cn,store.ro,tj.cn,tm.fr,tm.mc,tm.ro,tmp.br,tur.br,tv.br,tv.tr,tw.cn,uk.com,uk.net,us.com,uy.com,vet.br,waw.pl,web.ve,www.ro,xj.cn,xz.cn,yk.ca,yn.cn,zj.cn,zlg.br".split(",");e=e.toLowerCase();var n=e.split("."),r=n.length;if(r<2)return e;var i=n.slice(-2).join(".");return r>=3&&s.inArray(i,t)>=0?n.slice(-3).join("."):i}function i(e,t){t!="localhost"&&!parseInt(t.split(".").slice(-1))&&(t=r(t));var n=0;for(var i=t.length-1;i>=0;i--)n+=t.charCodeAt(i)*28939532831;n=(""+n).substring(0,7);for(i=0;i<e.length;i++)if(n===e[i].substring(1,8))return 1}var s=jQuery,o=e.conf,u=o.swf.indexOf("flowplayer.org")&&o.e&&t.data("origin"),a=u?n(u):location.hostname,f=o.key;location.protocol=="file:"&&(a="localhost"),e.load.ed=1,o.hostname=a,o.origin=u||location.href,u&&t.addClass("is-embedded"),typeof f=="string"&&(f=f.split(/,\s*/));if(f&&typeof i=="function"&&i(f,a))o.logo&&t.append(s("<a>",{"class":"fp-logo",href:u}).append(s("<img/>",{src:o.logo})));else{var l=s("<a/>").attr("onclick","").appendTo(t),c=s(".fp-controls",t),h=s('<div class="fp-context-menu"><ul><li class="copyright">&copy; 2013</li><li><a href="http://flowplayer.org">About Flowplayer</a></li><li><a href="http://flowplayer.org/license">GPL based license</a></li></ul></div>').appendTo(t);e.bind("pause resume finish unload",function(e,n){var r=-1;n.video.src&&s.each([["org","flowplayer","drive"],["org","flowplayer","my"]],function(e,t){return r=n.video.src.indexOf("://"+t.reverse().join(".")),r===-1}),/pause|resume/.test(e.type)&&n.engine!="flash"&&r!=4&&r!=5?(l.show().css({position:"absolute",left:16,bottom:36,zIndex:99999,width:120,height:27,backgroundImage:"url("+[".png","fplogo","/",".com","foliovision","//"].reverse().join("")+")"}),n.load.ed=l.is(":visible")&&s.contains(t[0],h[0]),n.load.ed||n.pause()):l.hide()})}});
7
 
8
  /*
9
  FV Flowplayer additions!
10
  */
 
 
 
 
11
  if( typeof(fv_flowplayer_admin_input) != "undefined" && fv_flowplayer_admin_input ) {
12
  jQuery(document).keyup(function(e) {
13
  if (e.keyCode == 27) { fv_wp_flowplayer_admin_show_notice(); } // esc
@@ -33,9 +37,9 @@ function fv_wp_flowplayer_admin_show_notice( id, link ) {
33
  jQuery('#fv_wp_fp_notice_'+id).toggle();
34
 
35
  var api = flowplayer(), currentPos;
36
- if( jQuery('#fv_wp_fp_notice_'+id).parent().hasClass("fv-wp-flowplayer-notice") ) {
37
  api.disable(false);
38
- } else {
39
  api.disable(true);
40
  }
41
 
@@ -79,28 +83,108 @@ function fv_wp_flowplayer_admin_support_mail( hash, button ) {
79
  }
80
  );
81
  }
82
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  function fv_flowplayer_admin_test_media( hash, media ) {
84
- jQuery(document).ready( function() {
85
- jQuery('#wpfp_'+hash).append('<div id="wpfp_notice_'+hash+'" class="fv-wp-flowplayer-notice-small" title="This note is visible to logged-in admins only."><small>Admin note: Checking the video file...</small></div>');
86
- jQuery.post( fv_fp_ajaxurl, { action: 'fv_wp_flowplayer_check_mimetype', media: media, hash: hash }, function( response ) {
 
 
87
  var obj;
88
  try {
 
 
89
  obj = jQuery.parseJSON( response );
90
-
91
- var extra_class = ( obj[1] > 0 ) ? ' fv-wp-flowplayer-error' : ' fv-wp-flowplayer-ok';
92
- if( obj[1] == 0 && obj[2] > 0 ) {
93
- extra_class = '';
94
- }
95
- jQuery('#wpfp_notice_'+hash).remove();
96
- jQuery('#wpfp_'+hash).append('<div id="wpfp_notice_'+hash+'" class="fv-wp-flowplayer-notice-small'+extra_class+'" title="This note is visible to logged-in admins only.">'+obj[0]+'</div>');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  } catch(e) {
 
98
  jQuery('#wpfp_notice_'+hash).html('<p>Admin: Error parsing JSON</p>');
99
  return;
100
  }
101
 
102
- } );
103
- } );
 
104
  }
105
 
106
  function fv_flowplayer_autoplay( hash ) {
@@ -161,7 +245,7 @@ function fv_flowplayer_browser_ff_m4v( hash ) {
161
  }
162
 
163
  function fv_flowplayer_browser_ie( hash ) {
164
- if( jQuery.browser.msie && parseInt(jQuery.browser.version, 10) >= 9 ) {
165
  jQuery('#wpfp_'+hash).attr('data-engine','flash');
166
  }
167
  }
@@ -180,6 +264,15 @@ jQuery(document).ready( function() {
180
  if( (navigator.platform.indexOf("iPhone") != -1) || (navigator.platform.indexOf("iPod") != -1) || (navigator.platform.indexOf("iPad") != -1) ) {
181
  jQuery(window).trigger('load');
182
  }
 
 
 
 
 
 
 
 
 
183
  } );
184
 
185
  function fv_flowplayer_loop( hash ) {
@@ -219,7 +312,12 @@ function fv_flowplayer_preroll_post( hash, media ) {
219
  }
220
 
221
  function fv_flowplayer_redirect( hash, url ) {
222
- jQuery('#wpfp_'+hash).bind('finish', function() { window.open(url, '_blank'); } );
 
 
 
 
 
223
  }
224
 
225
  var fv_flowplayer_safety_resize_arr = Array();
@@ -228,6 +326,8 @@ function fv_flowplayer_safety_resize() {
228
  var fv_flowplayer_safety_resize_init = false;
229
 
230
  jQuery('.flowplayer').each( function() {
 
 
231
  if( jQuery(this).width() < 30 || jQuery(this).height() < 20 ) {
232
  fv_flowplayer_safety_resize_init = true
233
  var el = jQuery(this);
@@ -244,6 +344,8 @@ function fv_flowplayer_safety_resize() {
244
  if( fv_flowplayer_safety_resize_init ) {
245
  jQuery(window).resize(function() {
246
  jQuery('.flowplayer').each( function() {
 
 
247
  if( fv_flowplayer_safety_resize_arr[jQuery(this).attr('id')] ) {
248
  jQuery(this).width( fv_flowplayer_safety_resize_arr[jQuery(this).attr('id')].width() );
249
  jQuery(this).height( parseInt(jQuery(this).width() * jQuery(this).attr('data-ratio')) );
@@ -253,37 +355,38 @@ function fv_flowplayer_safety_resize() {
253
  }
254
  }
255
 
256
- if( typeof(fv_flowplayer_safety_resize_do) != "undefined" && fv_flowplayer_safety_resize_do ) {
257
  jQuery(document).ready(function() { fv_flowplayer_safety_resize(); } );
258
  }
259
 
260
  jQuery(document).ready( function() {
261
- if( jQuery('.fp-playlist-external').length == 0 ) return;
262
- jQuery('.fp-playlist-external' ).each( function(i,el) {
263
- if( jQuery('a',el).length == 0 ) return;
264
- jQuery('a',el).click( function(e) {
265
- jQuery('a',el).removeClass('is-active');
266
- jQuery( '#' + jQuery(this).parent().attr('rel') ).flowplayer().play( jQuery('a',el).index(this) );
267
- jQuery(this).addClass('is-active');
268
- } );
269
- } );
270
-
271
- jQuery('.flowplayer').bind('load', function(e,api,video) {
272
- if( video.index > -1 ) {
273
- var playlist_external = jQuery('[rel='+jQuery(this).attr('id')+'] a');
274
- if( playlist_external.length > 0 ) {
275
- jQuery(playlist_external).removeClass('is-active');
276
- jQuery(playlist_external).eq(video.index).addClass('is-active');
277
- }
278
- }
279
- } );
 
 
 
 
 
280
  } );
281
 
282
- jQuery( function() { if( typeof(flowplayer.conf.key) == "undefined" ) { jQuery('.flowplayer').unbind('contextmenu'); } } );
283
-
284
- if( typeof(fv_flowplayer_conf) != "undefined" ) {
285
- flowplayer.conf = fv_flowplayer_conf
286
- }
287
 
288
  if( typeof(fv_flowplayer_playlists) != "undefined" ) {
289
  for( var i in fv_flowplayer_playlists ) {
@@ -299,11 +402,6 @@ if( typeof(fv_flowplayer_autoplay_array) != "undefined" ) {
299
  fv_flowplayer_autoplay( i );
300
  }
301
  }
302
- if( typeof(fv_flowplayer_admin_test_media_array) != "undefined" ) {
303
- for( var i in fv_flowplayer_admin_test_media_array ) {
304
- fv_flowplayer_admin_test_media( i, fv_flowplayer_admin_test_media_array[i] );
305
- }
306
- }
307
  if( typeof(fv_flowplayer_browser_ff_m4v_array) != "undefined" ) {
308
  for( var i in fv_flowplayer_browser_ff_m4v_array ) {
309
  fv_flowplayer_browser_ff_m4v( i );
@@ -338,4 +436,26 @@ if( typeof(fv_flowplayer_redirect_array) != "undefined" ) {
338
  for( var i in fv_flowplayer_redirect_array ) {
339
  fv_flowplayer_redirect( i, fv_flowplayer_redirect_array[i] );
340
  }
341
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  Flowplayer Unlimited v5.4.6 (2013-12-17) | flowplayer.org/license
4
 
5
  */!function(e){function u(t,n){var r="obj"+(""+Math.random()).slice(2,15),i='<object class="fp-engine" id="'+r+'" name="'+r+'" ';i+=e.browser.msie?'classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">':' data="'+t+'" type="application/x-shockwave-flash">';var s={width:"100%",height:"100%",allowscriptaccess:"always",wmode:"transparent",quality:"high",flashvars:"",movie:t+(e.browser.msie?"?"+r:""),name:r};return e.each(n,function(e,t){s.flashvars+=e+"="+t+"&"}),e.each(s,function(e,t){i+='<param name="'+e+'" value="'+t+'"/>'}),i+="</object>",e(i)}function l(e,t){return t=t||100,Math.round(e*t)/t}function c(e){return/mpegurl/i.test(e)?"application/x-mpegurl":"video/"+e}function h(e){return/^(video|application)/.test(e)||(e=c(e)),!!a.canPlayType(e).replace("no","")}function p(t,n){var r=e.grep(t,function(e){return e.type===n});return r.length?r[0]:null}function g(e){var t=e.attr("src"),n=e.attr("type")||"",r=t.split(m)[1];return n=/mpegurl/.test(n)?"mpegurl":n.replace("video/",""),{src:t,suffix:r||n,type:n||r}}function y(t){var n=this,r=[];e("source",t).each(function(){r.push(g(e(this)))}),r.length||r.push(g(t)),n.initialSources=r,n.resolve=function(t){return t?(e.isArray(t)?t={sources:e.map(t,function(t){var n,r=e.extend({},t);return e.each(t,function(e,t){n=e}),r.type=n,r.src=t[n],delete r[n],r})}:typeof t=="string"&&(t={src:t,sources:[]},e.each(r,function(e,n){n.type!="flash"&&t.sources.push({type:n.type,src:t.src.replace(m,"."+n.suffix+"$2")})})),t):{sources:r}}}function b(e){return e=parseInt(e,10),e>=10?e:"0"+e}function w(e){e=e||0;var t=Math.floor(e/3600),n=Math.floor(e/60);return e-=n*60,t>=1?(n-=t*60,t+":"+b(n)+":"+b(e)):b(n)+":"+b(e)}!function(e){if(!e.browser){var t=e.browser={},n=navigator.userAgent.toLowerCase(),r=/(chrome)[ \/]([\w.]+)/.exec(n)||/(safari)[ \/]([\w.]+)/.exec(n)||/(webkit)[ \/]([\w.]+)/.exec(n)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(n)||/(msie) ([\w.]+)/.exec(n)||n.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(n)||[];r[1]&&(t[r[1]]=!0,t.version=r[2]||"0")}}(jQuery),e(function(){typeof e.fn.flowplayer=="function"&&e("video").parent(".flowplayer").flowplayer()});var t=[],n=[],r=window.navigator.userAgent;window.flowplayer=function(r){return e.isFunction(r)?n.push(r):typeof r=="number"||r===undefined?t[r||0]:e(r).data("flowplayer")},e(window).on("beforeunload",function(){e.each(t,function(t,n){n.conf.splash?n.unload():n.bind("error",function(){e(".flowplayer.is-error .fp-message").remove()})})});var i=!1;try{typeof window.localStorage=="object"&&(window.localStorage.flowplayerTestStorage="test",i=!0)}catch(s){}e.extend(flowplayer,{version:"5.4.6",engine:{},conf:{},support:{},defaults:{debug:!1,disabled:!1,engine:"html5",fullscreen:window==window.top,keyboard:!0,ratio:9/16,adaptiveRatio:!1,flashfit:!1,rtmp:0,splash:!1,live:!1,swf:"//releases.flowplayer.org/5.4.6/commercial/flowplayer.swf",speeds:[.25,.5,1,1.5,2],tooltip:!0,volume:i?localStorage.muted=="true"?0:isNaN(localStorage.volume)?1:localStorage.volume||1:1,errors:["","Video loading aborted","Network error","Video not properly encoded","Video file not found","Unsupported video","Skin not found","SWF file not found","Subtitles not found","Invalid RTMP URL","Unsupported video format. Try installing Adobe Flash."],errorUrls:["","","","","","","","","","","http://get.adobe.com/flashplayer/"],playlist:[]}});var o=1;e.fn.flowplayer=function(r,i){return typeof r=="string"&&(r={swf:r}),e.isFunction(r)&&(i=r,r={}),!r&&this.data("flowplayer")||this.each(function(){var s=e(this).addClass("is-loading"),u=e.extend({},flowplayer.defaults,flowplayer.conf,r,s.data()),a=e("video",s).addClass("fp-engine").removeAttr("controls"),f=a.length?new y(a):null,l={},c,h;if(u.playlist.length){var p=a.attr("preload"),d;a.length&&a.replaceWith(d=e("<p />")),a=e("<video />").addClass("fp-engine"),d?d.replaceWith(a):s.prepend(a),flowplayer.support.video&&a.attr("preload",p),typeof u.playlist[0]=="string"?a.attr("src",u.playlist[0]):e.each(u.playlist[0],function(t,n){for(var r in n)n.hasOwnProperty(r)&&a.append(e("<source />").attr({type:"video/"+r,src:n[r]}))}),f=new y(a)}var v=s.data("flowplayer");v&&v.unload(),s.data("fp-player_id",s.data("fp-player_id")||o++);try{l=window.localStorage||l}catch(m){}var g=this.currentStyle&&this.currentStyle.direction==="rtl"||window.getComputedStyle&&window.getComputedStyle(this,null).getPropertyValue("direction")==="rtl";g&&s.addClass("is-rtl");var b=v||{conf:u,currentSpeed:1,volumeLevel:typeof u.volume=="undefined"?l.volume*1:u.volume,video:{},disabled:!1,finished:!1,loading:!1,muted:l.muted=="true"||u.muted,paused:!1,playing:!1,ready:!1,splash:!1,rtl:g,load:function(t,n){if(b.error||b.loading||b.disabled)return;t=f.resolve(t),e.extend(t,h.pick(t.sources));if(t.src){var r=e.Event("load");s.trigger(r,[b,t,h]),r.isDefaultPrevented()?b.loading=!1:(h.load(t),e.isFunction(t)&&(n=t),n&&s.one("ready",n))}return b},pause:function(e){return b.ready&&!b.seeking&&!b.disabled&&!b.loading&&(h.pause(),b.one("pause",e)),b},resume:function(){return b.ready&&b.paused&&!b.disabled&&(h.resume(),b.finished&&(b.trigger("resume",[b]),b.finished=!1)),b},toggle:function(){return b.ready?b.paused?b.resume():b.pause():b.load()},seek:function(t,n){if(b.ready){if(typeof t=="boolean"){var r=b.video.duration*.1;t=b.video.time+(t?r:-r)}t=c=Math.min(Math.max(t,0),b.video.duration).toFixed(1);var i=e.Event("beforeseek");s.trigger(i,[b,t]),i.isDefaultPrevented()?(b.seeking=!1,s.toggleClass("is-seeking",b.seeking)):(h.seek(t),e.isFunction(n)&&s.one("seek",n))}return b},seekTo:function(e,t){var n=e===undefined?c:b.video.duration*.1*e;return b.seek(n,t)},mute:function(e){return e===undefined&&(e=!b.muted),l.muted=b.muted=e,l.volume=isNaN(l.volume)?u.volume:l.volume,b.volume(e?0:l.volume,!0),b.trigger("mute",e),b},volume:function(e,t){return b.ready&&(e=Math.min(Math.max(e,0),1),t||(l.volume=e),h.volume(e)),b},speed:function(t,n){return b.ready&&(typeof t=="boolean"&&(t=u.speeds[e.inArray(b.currentSpeed,u.speeds)+(t?1:-1)]||b.currentSpeed),h.speed(t),n&&s.one("speed",n)),b},stop:function(){return b.ready&&(b.pause(),b.seek(0,function(){s.trigger("stop")})),b},unload:function(){return s.hasClass("is-embedding")||(u.splash?(b.trigger("unload"),h.unload()):b.stop()),b},disable:function(e){return e===undefined&&(e=!b.disabled),e!=b.disabled&&(b.disabled=e,b.trigger("disable",e)),b}};b.conf=e.extend(b.conf,u),e.each(["bind","one","unbind"],function(e,t){b[t]=function(e,n){return s[t](e,n),b}}),b.trigger=function(e,t){return s.trigger(e,[b,t]),b},s.data("flowplayer")||s.bind("boot",function(){e.each(["autoplay","loop","preload","poster"],function(e,t){var n=a.attr(t);n!==undefined&&(u[t]=n?n:!0)});if(u.splash||s.hasClass("is-splash")||!flowplayer.support.firstframe)b.forcedSplash=!u.splash&&!s.hasClass("is-splash"),b.splash=u.splash=u.autoplay=!0,s.addClass("is-splash"),flowplayer.support.video&&a.attr("preload","none");if(u.live||s.hasClass("is-live"))b.live=u.live=!0,s.addClass("is-live");e.each(n,function(e){this(b,s)}),h=flowplayer.engine[u.engine],h&&(h=h(b,s)),h.pick(f.initialSources)?b.engine=u.engine:e.each(flowplayer.engine,function(e,t){if(e!=u.engine)return h=this(b,s),h.pick(f.initialSources)&&(b.engine=e),!1}),t.push(b);if(!b.engine)return b.trigger("error",{code:flowplayer.support.flashVideo?5:10});u.splash?b.unload():b.load(),u.disabled&&b.disable(),h.volume(b.volumeLevel),s.one("ready",i)}).bind("load",function(t,n,r){u.splash&&e(".flowplayer").filter(".is-ready, .is-loading").not(s).each(function(){var t=e(this).data("flowplayer");t.conf.splash&&t.unload()}),s.addClass("is-loading"),n.loading=!0}).bind("ready",function(e,t,n){function r(){s.removeClass("is-loading"),t.loading=!1}n.time=0,t.video=n,u.splash?s.one("progress",r):r(),t.muted?t.mute(!0):t.volume(t.volumeLevel)}).bind("unload",function(e){u.splash&&a.remove(),s.removeClass("is-loading"),b.loading=!1}).bind("ready unload",function(e){var t=e.type=="ready";s.toggleClass("is-splash",!t).toggleClass("is-ready",t),b.ready=t,b.splash=!t}).bind("progress",function(e,t,n){t.video.time=n}).bind("speed",function(e,t,n){t.currentSpeed=n}).bind("volume",function(e,t,n){t.volumeLevel=Math.round(n*100)/100,t.muted?n&&t.mute(!1):l.volume=n}).bind("beforeseek seek",function(e){b.seeking=e.type=="beforeseek",s.toggleClass("is-seeking",b.seeking)}).bind("ready pause resume unload finish stop",function(e,t,n){b.paused=/pause|finish|unload|stop/.test(e.type),e.type=="ready"&&(b.paused=u.preload=="none",n&&(b.paused=!n.duration||!u.autoplay&&u.preload!="none")),b.playing=!b.paused,s.toggleClass("is-paused",b.paused).toggleClass("is-playing",b.playing),b.load.ed||b.pause()}).bind("finish",function(e){b.finished=!0}).bind("error",function(){a.remove()}),s.trigger("boot",[b,s]).data("flowplayer",b)})},!function(){var t=function(e){var t=/Version\/(\d\.\d)/.exec(e);return t&&t.length>1?parseFloat(t[1],10):0},n=flowplayer.support,r=e.browser,i=e("<video loop autoplay preload/>")[0],s=r.msie,o=navigator.userAgent,u=/iPad|MeeGo/.test(o)&&!/CriOS/.test(o),a=/iPad/.test(o)&&/CriOS/.test(o),f=/iP(hone|od)/i.test(o)&&!/iPad/.test(o),l=/Android/.test(o)&&!/Firefox/.test(o),c=/Android/.test(o)&&/Firefox/.test(o),h=/Silk/.test(o),p=/IEMobile/.test(o),d=u?t(o):0,v=l?parseFloat(/Android\ (\d\.\d)/.exec(o)[1],10):0;e.extend(n,{subtitles:!!i.addTextTrack,fullscreen:!l&&(typeof document.webkitCancelFullScreen=="function"&&!/Mac OS X 10_5.+Version\/5\.0\.\d Safari/.test(o)||document.mozFullScreenEnabled||typeof document.exitFullscreen=="function"),inlineBlock:!(s&&r.version<8),touch:"ontouchstart"in window,dataload:!u&&!f&&!p,zeropreload:!s&&!l,volume:!u&&!l&&!f&&!h&&!a,cachedVideoTag:!u&&!f&&!a&&!p,firstframe:!f&&!u&&!l&&!h&&!a&&!p&&!c,inlineVideo:!f&&!p&&(!l||v>=3),hlsDuration:!r.safari||u||f||a,seekable:!u&&!a});try{var m=navigator.plugins["Shockwave Flash"],g=s?(new ActiveXObject("ShockwaveFlash.ShockwaveFlash")).GetVariable("$version"):m.description;!s&&!m[0].enabledPlugin?n.flashVideo=!1:(g=g.split(/\D+/),g.length&&!g[0]&&(g=g.slice(1)),n.flashVideo=g[0]>9||g[0]==9&&g[3]>=115)}catch(y){}try{n.video=!!i.canPlayType,n.video&&i.canPlayType("video/mp4")}catch(b){n.video=!1}n.animation=function(){var t=["","Webkit","Moz","O","ms","Khtml"],n=e("<p/>")[0];for(var r=0;r<t.length;r++)if(n.style[t[r]+"AnimationName"]!=="undefined")return!0}()}(),window.attachEvent&&window.attachEvent("onbeforeunload",function(){__flash_savedUnloadHandler=__flash_unloadHandler=function(){}}),flowplayer.engine.flash=function(t,n){var r=t.conf,i=t.video,s,o,a,f={pick:function(t){if(flowplayer.support.flashVideo){var n=e.grep(t,function(e){return e.type=="flash"})[0];if(n)return n;for(var r=0,i;r<t.length;r++){i=t[r];if(/mp4|flv/.test(i.type))return i}}},load:function(i){function f(e){return e.replace(/&amp;/g,"%26").replace(/&/g,"%26").replace(/=/g,"%3D")}var l=e("video",n),c=f(i.src);is_absolute=/^https?:/.test(c);try{l.length>0&&flowplayer.support.video&&l[0].pause()}catch(h){}var p=function(){l.remove()},d=function(t){return e.grep(t,function(e){return!!l[0].canPlayType("video/"+e.type)}).length>0};flowplayer.support.video&&l.prop("autoplay")&&d(i.sources)?l.one("timeupdate",p):p(),!is_absolute&&!r.rtmp&&(c=e("<img/>").attr("src",c)[0].src);if(a)a.__play(c);else{s="fp"+(""+Math.random()).slice(3,15);var v={hostname:r.embedded?r.hostname:location.hostname,url:c,callback:"jQuery."+s};n.data("origin")&&(v.origin=n.data("origin")),is_absolute&&delete r.rtmp,e.each(["key","autoplay","preload","rtmp","loop","debug","preload","splash","bufferTime"],function(e,t){r[t]&&(v[t]=r[t])}),v.rtmp&&(v.rtmp=f(v.rtmp)),o=u(r.swf,v),o.prependTo(n),a=o[0],setTimeout(function(){try{if(!a.PercentLoaded())return n.trigger("error",[t,{code:7,url:r.swf}])}catch(e){}},5e3),setTimeout(function(){typeof a.PercentLoaded=="undefined"&&n.trigger("flashdisabled",[t])},1e3),e[s]=function(n,s){r.debug&&n!="status"&&console.log("--",n,s);var o=e.Event(n);switch(n){case"ready":s=e.extend(i,s);break;case"click":o.flash=!0;break;case"keydown":o.which=s;break;case"seek":i.time=s;break;case"status":t.trigger("progress",s.time),s.buffer<i.bytes&&!i.buffered?(i.buffer=s.buffer/i.bytes*i.duration,t.trigger("buffer",i.buffer)):i.buffered||(i.buffered=!0,t.trigger("buffered"))}n!="buffered"&&setTimeout(function(){t.trigger(o,s)},1)}}},speed:e.noop,unload:function(){a&&a.__unload&&a.__unload(),delete e[s],e("object",n).remove(),a=0}};e.each("pause,resume,seek,volume".split(","),function(e,r){f[r]=function(e){try{t.ready&&(r=="seek"&&t.video.time&&!t.paused&&t.trigger("beforeseek"),e===undefined?a["__"+r]():a["__"+r](e))}catch(i){if(typeof a["__"+r]=="undefined")return n.trigger("flashdisabled",[t]);throw i}}});var l=e(window);return t.bind("ready fullscreen fullscreen-exit",function(r){var i=n.height(),s=n.width();if(t.conf.flashfit||/full/.test(r.type)){var o=t.isFullscreen,u=o&&L,a=!flowplayer.support.inlineBlock,f=o?u?screen.width:l.width():s,c=o?u?screen.height:l.height():i,h=0,p=0,d=a?s:"",v=a?i:"",m,g;if(t.conf.flashfit||r.type==="fullscreen")m=t.video.width/t.video.height,g=t.video.height/t.video.width,v=Math.max(g*f),d=Math.max(m*c),v=v>c?d*g:v,v=Math.min(Math.round(v),c),d=d>f?v*m:d,d=Math.min(Math.round(d),f),p=Math.max(Math.round((c+p-v)/2),0),h=Math.max(Math.round((f+h-d)/2),0);e("object",n).css({width:d,height:v,marginTop:p,marginLeft:h})}}),f};var a=e("<video/>")[0],f={ended:"finish",pause:"pause",play:"resume",progress:"buffer",timeupdate:"progress",volumechange:"volume",ratechange:"speed",seeked:"seek",loadeddata:"ready",error:"error",dataunavailable:"error"},d,v=function(t){return d?d.attr({type:c(t.type),src:t.src}):d=e("<video/>",{src:t.src,type:c(t.type),"class":"fp-engine",autoplay:"autoplay",preload:"none","x-webkit-airplay":"allow"})};flowplayer.engine.html5=function(t,n){function m(r,s,u){if(r.listeners&&r.listeners.hasOwnProperty(n.data("fp-player_id")))return;(r.listeners||(r.listeners={}))[n.data("fp-player_id")]=!0,s.bind("error",function(n){try{if(n.originalEvent&&e(n.originalEvent.originalTarget).is("img"))return n.preventDefault();h(e(n.target).attr("type"))&&t.trigger("error",{code:4})}catch(r){}}),e.each(f,function(s,f){r.addEventListener(s,function(c){f=="progress"&&c.srcElement&&c.srcElement.readyState===0&&setTimeout(function(){t.video.duration||(f="error",t.trigger(f,{code:4}))},1e4),o.debug&&!/progress/.test(f)&&console.log(s,"->",f,c);if(!t.ready&&!/ready|error/.test(f)||!f||!e("video",n).length)return;var h=e.Event(f),p;switch(f){case"ready":p=e.extend(u,{duration:r.duration,width:r.videoWidth,height:r.videoHeight,url:r.currentSrc,src:r.currentSrc});try{p.seekable=r.seekable&&r.seekable.end(null)}catch(d){}a=a||setInterval(function(){try{p.buffer=r.buffered.end(null)}catch(e){}p.buffer&&(l(p.buffer,1e3)<l(p.duration,1e3)&&!p.buffered?t.trigger("buffer",c):p.buffered||(p.buffered=!0,t.trigger("buffer",c).trigger("buffered",c),clearInterval(a),a=0))},250);if(!o.live&&!p.duration&&!i.hlsDuration&&s==="loadeddata"){var v=function(){p.duration=r.duration;try{p.seekable=r.seekable&&r.seekable.end(null)}catch(e){}t.trigger(h,p),r.removeEventListener("durationchange",v)};r.addEventListener("durationchange",v);return}break;case"progress":case"seek":var m=t.video.duration;if(r.currentTime>0){p=Math.max(r.currentTime,0);break}if(f=="progress")return;case"speed":p=l(r.playbackRate);break;case"volume":p=l(r.volume);break;case"error":try{p=(c.srcElement||c.originalTarget).error}catch(g){return}}t.trigger(h,p)},!1)})}var r=e("video",n),i=flowplayer.support,s=e("track",r),o=t.conf,u,a,c;return u={pick:function(e){if(i.video){if(o.videoTypePreference){var t=p(e,o.videoTypePreference);if(t)return t}for(var n=0,r;n<e.length;n++)if(h(e[n].type))return e[n]}},load:function(u){if(o.splash&&!c)r=v(u).prependTo(n),i.inlineVideo||r.css({position:"absolute",top:"-9999em"}),s.length&&r.append(s.attr("default","")),o.loop&&r.attr("loop","loop"),c=r[0];else{c=r[0];var a=r.find("source");!c.src&&a.length&&(c.src=u.src,a.remove());if(t.video.src&&u.src!=t.video.src)r.attr("autoplay","autoplay"),c.src=u.src;else if(o.preload=="none"||!i.dataload)i.zeropreload?t.trigger("ready",u).trigger("pause").one("ready",function(){n.trigger("resume",[t])}):t.one("ready",function(){n.trigger("pause",[t])})}m(c,e("source",r).add(r),u),(o.preload!="none"||!i.zeropreload||!i.dataload)&&c.load(),o.splash&&c.load()},pause:function(){c.pause()},resume:function(){c.play()},speed:function(e){c.playbackRate=e},seek:function(e){try{var n=t.paused;c.currentTime=e,n&&c.pause()}catch(r){}},volume:function(e){c.volume=e},unload:function(){e("video.fp-engine",n).remove(),i.cachedVideoTag||(d=null),a=clearInterval(a),c=0}}};var m=/\.(\w{3,4})(\?.*)?$/i;e.throttle=function(e,t){var n;return function(){n||(e.apply(this,arguments),n=1,setTimeout(function(){n=0},t))}},e.fn.slider2=function(t){var n=/iPad/.test(navigator.userAgent)&&!/CriOS/.test(navigator.userAgent);return this.each(function(){var r=e(this),i=e(document),s=r.children(":last"),o,u,a,f,l,c,h,p,d=!1,v=function(){u=r.offset(),a=r.width(),f=r.height(),c=l?f:a,p=b(h)},m=function(e){!o&&e!=w.value&&(!h||e<h)&&(r.trigger("slide",[e]),w.value=e)},g=function(e){var n=e.pageX;!n&&e.originalEvent&&e.originalEvent.touches&&e.originalEvent.touches.length&&(n=e.originalEvent.touches[0].pageX);var r=l?e.pageY-u.top:n-u.left;r=Math.max(0,Math.min(p||c,r));var i=r/c;return l&&(i=1-i),t&&(i=1-i),y(i,0,!0)},y=function(e,t){t===undefined&&(t=0),e>1&&(e=1);var r=Math.round(e*1e3)/10+"%";if(!h||e<=h)n||s.stop(),d?s.css("width",r):s.animate(l?{height:r}:{width:r},t,"linear");return e},b=function(e){return Math.max(0,Math.min(c,l?(1-e)*f:e*a))},w={max:function(e){h=e},disable:function(e){o=e},slide:function(e,t,n){v(),n&&m(e),y(e,t)},disableAnimation:function(e){d=e!==!1}};v(),r.data("api",w).bind("mousedown.sld touchstart",function(t){t.preventDefault();if(!o){var n=e.throttle(m,100);v(),w.dragging=!0,r.addClass("is-dragging"),m(g(t)),i.bind("mousemove.sld touchmove",function(e){e.preventDefault(),n(g(e))}).one("mouseup touchend",function(){w.dragging=!1,r.removeClass("is-dragging"),i.unbind("mousemove.sld touchmove")})}})})},flowplayer(function(t,n){function o(t){return e(".fp-"+t,n)}function T(t){if(n.css("width")==="0px"||n.css("height")==="0px"||t!==flowplayer.defaults.ratio)parseInt(v,10)||h.css("paddingTop",t*100+"%");i.inlineBlock||e("object",n).height(n.height())}function N(e){n.toggleClass("is-mouseover",e).toggleClass("is-mouseout",!e)}var r=t.conf,i=flowplayer.support,s;n.find(".fp-ratio,.fp-ui").remove(),n.addClass("flowplayer").append(' <div class="ratio"/> <div class="ui"> <div class="waiting"><em/><em/><em/></div> <a class="fullscreen"/> <a class="unload"/> <p class="speed"/> <div class="controls"> <a class="play"></a> <div class="timeline"> <div class="buffer"/> <div class="progress"/> </div> <div class="volume"> <a class="mute"></a> <div class="volumeslider"> <div class="volumelevel"/> </div> </div> </div> <div class="time"> <em class="elapsed">00:00</em> <em class="remaining"/> <em class="duration">00:00</em> </div> <div class="message"><h2/><p/></div> </div>'.replace(/class="/g,'class="fp-'));var u=o("progress"),a=o("buffer"),f=o("elapsed"),l=o("remaining"),c=o("waiting"),h=o("ratio"),p=o("speed"),d=o("duration"),v=h.css("paddingTop"),m=o("timeline").slider2(t.rtl),g=m.data("api"),y=o("volume"),b=o("fullscreen"),E=o("volumeslider").slider2(t.rtl),S=E.data("api"),x=n.is(".fixed-controls, .no-toggle");g.disableAnimation(n.hasClass("is-touch")),i.animation||c.html("<p>loading &hellip;</p>"),T(r.ratio);try{r.fullscreen||b.remove()}catch(C){b.remove()}t.bind("ready",function(){var e=t.video.duration;g.disable(t.disabled||!e),r.adaptiveRatio&&T(t.video.height/t.video.width),d.add(l).html(w(e)),e>=3600&&n.addClass("is-long")||n.removeClass("is-long"),S.slide(t.volumeLevel)}).bind("unload",function(){v||h.css("paddingTop","")}).bind("buffer",function(){var e=t.video,n=e.buffer/e.duration;!e.seekable&&i.seekable&&g.max(n),n<1?a.css("width",n*100+"%"):a.css({width:"100%"})}).bind("speed",function(e,t,n){p.text(n+"x").addClass("fp-hilite"),setTimeout(function(){p.removeClass("fp-hilite")},1e3)}).bind("buffered",function(){a.css({width:"100%"}),g.max(1)}).bind("progress",function(){var e=t.video.time,n=t.video.duration;g.dragging||g.slide(e/n,t.seeking?0:250),f.html(w(e)),l.html("-"+w(n-e))}).bind("finish resume seek",function(e){n.toggleClass("is-finished",e.type=="finish")}).bind("stop",function(){f.html(w(0)),g.slide(0,100)}).bind("finish",function(){f.html(w(t.video.duration)),g.slide(1,100),n.removeClass("is-seeking")}).bind("beforeseek",function(){u.stop()}).bind("volume",function(){S.slide(t.volumeLevel)}).bind("disable",function(){var e=t.disabled;g.disable(e),S.disable(e),n.toggleClass("is-disabled",t.disabled)}).bind("mute",function(e,t,r){n.toggleClass("is-muted",r)}).bind("error",function(t,i,s){n.removeClass("is-loading").addClass("is-error");if(s){s.message=r.errors[s.code],i.error=!0;var o=e(".fp-message",n);e("h2",o).text((i.engine||"html5")+": "+s.message),e("p",o).text(s.url||i.video.url||i.video.src||r.errorUrls[s.code]),n.unbind("mouseenter click").removeClass("is-mouseover")}}).bind("mouseenter mouseleave",function(e){if(x)return;var t=e.type=="mouseenter",r;N(t),t?(n.bind("pause.x mousemove.x volume.x",function(){N(!0),r=new Date}),s=setInterval(function(){new Date-r>2e3&&(N(!1),r=new Date)},100)):(n.unbind(".x"),clearInterval(s))}).bind("mouseleave",function(){(g.dragging||S.dragging)&&n.addClass("is-mouseover").removeClass("is-mouseout")}).bind("click.player",function(n){if(e(n.target).is(".fp-ui, .fp-engine")||n.flash)return n.preventDefault(),t.toggle()}).bind("contextmenu",function(t){t.preventDefault();var r=n.offset(),i=e(window),s=t.clientX-r.left,o=t.clientY-r.top+i.scrollTop(),u=n.find(".fp-context-menu").css({left:s+"px",top:o+"px",display:"block"}).on("click",function(e){e.stopPropagation()});e("html").on("click.outsidemenu",function(t){u.hide(),e("html").off("click.outsidemenu")})}).bind("flashdisabled",function(){n.addClass("is-flash-disabled").one("ready",function(){n.removeClass("is-flash-disabled").find(".fp-flash-disabled").remove()}).append('<div class="fp-flash-disabled">Adobe Flash is disabled for this page, click player area to enable.</div>')}),r.poster&&n.css("backgroundImage","url("+r.poster+")");var k=n.css("backgroundColor"),L=n.css("backgroundImage")!="none"||k&&k!="rgba(0, 0, 0, 0)"&&k!="transparent";L&&!r.splash&&!r.autoplay&&t.bind("ready stop",function(){n.addClass("is-poster").one("progress",function(){n.removeClass("is-poster")})}),!L&&t.forcedSplash&&n.css("backgroundColor","#555"),e(".fp-toggle, .fp-play",n).click(t.toggle),e.each(["mute","fullscreen","unload"],function(e,n){o(n).click(function(){t[n]()})}),m.bind("slide",function(e,n){t.seeking=!0,t.seek(n*t.video.duration)}),E.bind("slide",function(e,n){t.volume(n)}),o("time").click(function(t){e(this).toggleClass("is-inverted")}),N(x)});var E,S,x="is-help";e(document).bind("keydown.fp",function(t){var n=E,r=t.ctrlKey||t.metaKey||t.altKey,i=t.which,s=n&&n.conf;if(!n||!s.keyboard||n.disabled)return;if(e.inArray(i,[63,187,191])!=-1)return S.toggleClass(x),!1;if(i==27&&S.hasClass(x))return S.toggleClass(x),!1;if(!r&&n.ready){t.preventDefault();if(t.shiftKey){i==39?n.speed(!0):i==37&&n.speed(!1);return}if(i<58&&i>47)return n.seekTo(i-48);switch(i){case 38:case 75:n.volume(n.volumeLevel+.15);break;case 40:case 74:n.volume(n.volumeLevel-.15);break;case 39:case 76:n.seeking=!0,n.seek(!0);break;case 37:case 72:n.seeking=!0,n.seek(!1);break;case 190:n.seekTo();break;case 32:n.toggle();break;case 70:s.fullscreen&&n.fullscreen();break;case 77:n.mute();break;case 81:n.unload()}}}),flowplayer(function(t,n){if(!t.conf.keyboard)return;n.bind("mouseenter mouseleave",function(e){E=!t.disabled&&e.type=="mouseenter"?t:0,E&&(S=n)}),n.append(' <div class="fp-help"> <a class="fp-close"></a> <div class="fp-help-section fp-help-basics"> <p><em>space</em>play / pause</p> <p><em>q</em>unload | stop</p> <p><em>f</em>fullscreen</p> <p><em>shift</em> + <em>&#8592;</em><em>&#8594;</em>slower / faster <small>(latest Chrome and Safari)</small></p> </div> <div class="fp-help-section"> <p><em>&#8593;</em><em>&#8595;</em>volume</p> <p><em>m</em>mute</p> </div> <div class="fp-help-section"> <p><em>&#8592;</em><em>&#8594;</em>seek</p> <p><em>&nbsp;. </em>seek to previous </p><p><em>1</em><em>2</em>&hellip;<em>6</em> seek to 10%, 20%, &hellip;60% </p> </div> </div> '),t.conf.tooltip&&e(".fp-ui",n).attr("title","Hit ? for help").on("mouseout.tip",function(){e(this).removeAttr("title").off("mouseout.tip")}),e(".fp-close",n).click(function(){n.toggleClass(x)})});var T=e.browser.mozilla?"moz":"webkit",N="fullscreen",C="fullscreen-exit",k,L=flowplayer.support.fullscreen,A=typeof document.exitFullscreen=="function",O=navigator.userAgent.toLowerCase(),M=/(safari)[ \/]([\w.]+)/.exec(O)&&!/(chrome)[ \/]([\w.]+)/.exec(O);e(document).bind(A?"fullscreenchange":T+"fullscreenchange",function(t){var n=e(document.webkitCurrentFullScreenElement||document.mozFullScreenElement||document.fullscreenElement||t.target);n.length&&!k?k=n.trigger(N,[n]):(k.trigger(C,[k]),k=null)}),flowplayer(function(t,n){if(!t.conf.fullscreen)return;var r=e(window),i={index:0,pos:0,play:!1},s;t.isFullscreen=!1,t.fullscreen=function(o){if(t.disabled)return;return o===undefined&&(o=!t.isFullscreen),o&&(s=r.scrollTop()),(T=="webkit"||M)&&t.engine=="flash"&&(i.index=t.video.index,t.conf.rtmp&&e.extend(i,{pos:t.video.time,play:t.playing})),L?o?A?n[0].requestFullscreen():(n[0][T+"RequestFullScreen"](Element.ALLOW_KEYBOARD_INPUT),M&&!document.webkitCurrentFullScreenElement&&!document.mozFullScreenElement&&n[0][T+"RequestFullScreen"]()):A?document.exitFullscreen():document[T+"CancelFullScreen"]():t.trigger(o?N:C,[t]),t};var o;n.bind("mousedown.fs",function(){+(new Date)-o<150&&t.ready&&t.fullscreen(),o=+(new Date)}),t.bind(N,function(e){n.addClass("is-fullscreen"),t.isFullscreen=!0}).bind(C,function(e){var i;!L&&t.engine==="html5"&&(i=n.css("opacity")||"",n.css("opacity",0)),n.removeClass("is-fullscreen"),!L&&t.engine==="html5"&&setTimeout(function(){n.css("opacity",i)}),t.isFullscreen=!1,r.scrollTop(s)}).bind("ready",function(){if(i.index>0)t.play(i.index),i.index=0;else if(i.pos&&!isNaN(i.pos)){var n=function(){i.play||t.pause(),e.extend(i,{pos:0,play:!1})};t.conf.live?(t.resume(),n()):t.resume().seek(i.pos,n)}})}),flowplayer(function(t,n){function s(){return e(r.query,n)}function o(){return e(r.query+"."+i,n)}var r=e.extend({active:"is-active",advance:!0,query:".fp-playlist a"},t.conf),i=r.active;t.play=function(n){return n===undefined?t.resume():typeof n=="number"&&!t.conf.playlist[n]?t:(typeof n!="number"&&t.load.apply(null,arguments),t.unbind("resume.fromfirst"),t.video.index=n,t.load(typeof t.conf.playlist[n]=="string"?t.conf.playlist[n].toString():e.map(t.conf.playlist[n],function(t){return e.extend({},t)})),t)},t.next=function(e){e&&e.preventDefault();var n=t.video.index;return n!=-1&&(n=n===t.conf.playlist.length-1?0:n+1,t.play(n)),t},t.prev=function(e){e&&e.preventDefault();var n=t.video.index;return n!=-1&&(n=n===0?t.conf.playlist.length-1:n-1,t.play(n)),t},e(".fp-next",n).click(t.next),e(".fp-prev",n).click(t.prev),r.advance&&n.unbind("finish.pl").bind("finish.pl",function(e,t){var i=t.video.index+1;i<t.conf.playlist.length||r.loop?(i=i===t.conf.playlist.length?0:i,n.removeClass("is-finished"),setTimeout(function(){t.play(i)})):(n.addClass("is-playing"),t.conf.playlist.length>1&&t.one("resume.fromfirst",function(){return t.play(0),!1}))});var u=!1;if(t.conf.playlist.length){u=!0;var a=n.find(".fp-playlist");if(!a.length){a=e('<div class="fp-playlist"></div>');var f=e(".fp-next,.fp-prev",n);f.length?f.eq(0).before(a):e("video",n).after(a)}a.empty(),e.each(t.conf.playlist,function(t,n){var r;if(typeof n=="string")r=n;else for(var i in n[0])if(n[0].hasOwnProperty(i)){r=n[0][i];break}a.append(e("<a />").attr({href:r,"data-index":t}))})}if(s().length){u||(t.conf.playlist=[],s().each(function(){var n=e(this).attr("href");e(this).attr("data-index",t.conf.playlist.length),t.conf.playlist.push(n)})),n.on("click",r.query,function(n){n.preventDefault();var i=e(n.target).closest(r.query),s=Number(i.attr("data-index"));s!=-1&&t.play(s)});var l=s().filter("[data-cuepoints]").length;t.bind("load",function(r,s,u){var a=o().removeClass(i),f=a.attr("data-index"),c=u.index=t.video.index||0,h=e('a[data-index="'+c+'"]',n).addClass(i),p=c==t.conf.playlist.length-1;n.removeClass("video"+f).addClass("video"+c).toggleClass("last-video",p),u.index=s.video.index=c,u.is_last=s.video.is_last=p,l&&(t.cuepoints=h.data("cuepoints"))}).bind("unload.pl",function(){o().toggleClass(i)})}t.conf.playlist.length&&(t.conf.loop=!1)});var _=/ ?cue\d+ ?/;flowplayer(function(t,n){function i(e){n[0].className=n[0].className.replace(_," "),e>=0&&n.addClass("cue"+e)}var r=0;t.cuepoints=t.conf.cuepoints||[],t.bind("progress",function(e,s,o){if(r&&o-r<.015)return r=o;r=o;var u=t.cuepoints||[];for(var a=0,f;a<u.length;a++)f=u[a],isNaN(f)||(f={time:f}),f.time<0&&(f.time=t.video.duration+f.time),f.index=a,Math.abs(f.time-o)<.125*t.currentSpeed&&(i(a),n.trigger("cuepoint",[t,f]))}).bind("unload seek",i),t.conf.generate_cuepoints&&t.bind("load",function(){e(".fp-cuepoint",n).remove()}).bind("ready",function(){var r=t.cuepoints||[],i=t.video.duration,s=e(".fp-timeline",n).css("overflow","visible");e.each(r,function(n,r){var o=r.time||r;o<0&&(o=i+r);var u=e("<a/>").addClass("fp-cuepoint fp-cuepoint"+n).css("left",o/i*100+"%");u.appendTo(s).mousedown(function(){return t.seek(o),!1})})})}),flowplayer(function(t,n,r){function u(e){var t=e.split(":");return t.length==2&&t.unshift(0),t[0]*60*60+t[1]*60+parseFloat(t[2].replace(",","."))}var i=e("track",n),s=t.conf;if(flowplayer.support.subtitles){t.subtitles=i.length&&i[0].track;if(s.nativesubtitles&&s.engine=="html5")return}i.remove();var o=/^(([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3}) --\> (([0-9]{2}:)?[0-9]{2}:[0-9]{2}[,.]{1}[0-9]{3})(.*)/;t.subtitles=[];var a=i.attr("src");if(!a)return;setTimeout(function(){e.get(a,function(n){for(var r=0,i=n.split("\n"),s=i.length,a={},f,l,c,h;r<s;r++){l=o.exec(i[r]);if(l){f=i[r-1],c="<p>"+i[++r]+"</p><br/>";while(e.trim(i[++r])&&r<i.length)c+="<p>"+i[r]+"</p><br/>";a={title:f,startTime:u(l[1]),endTime:u(l[2]||l[3]),text:c},h={time:a.startTime,subtitle:a},t.subtitles.push(a),t.cuepoints.push(h),t.cuepoints.push({time:a.endTime,subtitleEnd:f}),a.startTime===0&&t.trigger("cuepoint",h)}}}).fail(function(){return t.trigger("error",{code:8,url:a}),!1})});var f=e("<div class='fp-subtitle'/>").appendTo(n),l;t.bind("cuepoint",function(e,t,n){n.subtitle?(l=n.index,f.html(n.subtitle.text).addClass("fp-active")):n.subtitleEnd&&(f.removeClass("fp-active"),l=n.index)}).bind("seek",function(n,r,i){l&&t.cuepoints[l]&&t.cuepoints[l].time>i&&(f.removeClass("fp-active"),l=null),e.each(t.cuepoints||[],function(e,n){var r=n.subtitle;r&&l!=n.index?i>=n.time&&(!r.endTime||i<=r.endTime)&&t.trigger("cuepoint",n):n.subtitleEnd&&i>=n.time&&n.index==l+1&&t.trigger("cuepoint",n)})})}),flowplayer(function(t,n){var r=t.conf.analytics,i=0,s=0;if(r){typeof _gat=="undefined"&&e.getScript("//google-analytics.com/ga.js");function o(e){if(i&&typeof _gat!="undefined"){var s=_gat._getTracker(r),o=t.video;s._setAllowLinker(!0),s._trackEvent("Video / Seconds played",t.engine+"/"+o.type,n.attr("title")||o.src.split("/").slice(-1)[0].replace(m,""),Math.round(i/1e3)),i=0}}t.bind("load unload",o).bind("progress",function(){t.seeking||(i+=s?+(new Date)-s:0,s=+(new Date))}).bind("pause",function(){s=0}),e(window).unload(o)}});var D=/IEMobile/.test(r);(flowplayer.support.touch||D)&&flowplayer(function(t,n){var i=/Android/.test(r)&&!/Firefox/.test(r)&&!/Opera/.test(r),s=/Silk/.test(r),o=i?parseFloat(/Android\ (\d\.\d)/.exec(r)[1],10):0;if(i){t.conf.videoTypePreference="mp4";if(!/Chrome/.test(r)&&o<4){var u=t.load;t.load=function(e,n){var r=u.apply(t,arguments);return t.trigger("ready",[t,t.video]),r}}}flowplayer.support.volume||n.addClass("no-volume no-mute"),n.addClass("is-touch"),n.find(".fp-timeline").data("api").disableAnimation();var a=!1;n.bind("touchmove",function(){a=!0}).bind("touchend click",function(r){if(a){a=!1;return}if(t.playing&&!n.hasClass("is-mouseover"))
6
+ return n.addClass("is-mouseover").removeClass("is-mouseout"),!1;t.paused&&n.hasClass("is-mouseout")&&!t.splash&&t.toggle(),t.paused&&D&&e("video.fp-engine",n)[0].play()}),t.conf.native_fullscreen&&typeof e("<video />")[0].webkitEnterFullScreen=="function"&&(t.fullscreen=function(){var t=e("video.fp-engine",n);t[0].webkitEnterFullScreen(),t.one("webkitendfullscreen",function(){t.prop("controls",!0).prop("controls",!1)})}),(i||s)&&t.bind("ready",function(){var r=e("video.fp-engine",n);r.one("canplay",function(){r[0].play()}),r[0].play(),t.bind("progress.dur",function(){var i=r[0].duration;i!==1&&(t.video.duration=i,e(".fp-duration",n).html(w(i)),t.unbind("progress.dur"))})})}),flowplayer(function(t,n){if(t.conf.embed===!1)return;var r=t.conf,i=e(".fp-ui",n),s=e("<a/>",{"class":"fp-embed",title:"Copy to your site"}).appendTo(i),o=e("<div/>",{"class":"fp-embed-code"}).append("<label>Copy and paste this HTML code into your webpage to embed.</label><textarea/>").appendTo(i),u=e("textarea",o);t.embedCode=function(){var i=t.video,s=i.width||n.width(),o=i.height||n.height(),u=e("<div/>",{"class":"flowplayer",css:{width:s,height:o}}),a=e("<video/>").appendTo(u);e.each(["origin","analytics","key","rtmp"],function(e,t){r[t]&&u.attr("data-"+t,r[t])}),r.logo&&u.attr("data-logo",e("<img />").attr("src",r.logo)[0].src),e.each(i.sources,function(t,n){var i=n.src;if(!/^https?:/.test(n.src)&&n.type!=="flash"||!r.rtmp)i=e("<img/>").attr("src",n.src)[0].src;a.append(e("<source/>",{type:"video/"+n.type,src:i}))});var f={src:"//foliovision.com/flowplayer/5.4.6/embed.min.js"};e.isPlainObject(r.embed)&&(f["data-swf"]=r.embed.swf,f["data-library"]=r.embed.library,f.src=r.embed.script||f.src,r.embed.skin&&(f["data-skin"]=r.embed.skin));var l=e("<foo/>",f).append(u);return e("<p/>").append(l).html().replace(/<(\/?)foo/g,"<$1script")},n.fptip(".fp-embed","is-embedding"),u.click(function(){this.select()}),s.click(function(){u.text(t.embedCode()),u[0].focus(),u[0].select()})}),e.fn.fptip=function(t,n){return this.each(function(){function i(){r.removeClass(n),e(document).unbind(".st")}var r=e(this);e(t||"a",this).click(function(t){t.preventDefault(),r.toggleClass(n),r.hasClass(n)&&e(document).bind("keydown.st",function(e){e.which==27&&i()}).bind("click.st",function(t){e(t.target).parents("."+n).length||i()})})})}}(jQuery),flowplayer(function(e,t){function n(e){var t=s("<a/>")[0];return t.href=e,t.hostname}function r(e){var t="ab.ca,ac.ac,ac.at,ac.be,ac.cn,ac.il,ac.in,ac.jp,ac.kr,ac.th,ac.uk,adm.br,adv.br,ah.cn,am.br,arq.br,art.br,arts.ro,asn.au,asso.fr,asso.mc,bc.ca,bio.br,biz.pl,biz.tr,bj.cn,br.com,cn.com,cng.br,cnt.br,co.ac,co.at,co.gl,co.id,co.il,co.in,co.jp,co.kr,co.mg,co.ms,co.nz,co.th,co.uk,co.ve,co.vi,co.za,com.ag,com.ai,com.ar,com.au,com.br,com.cn,com.cy,com.de,com.do,com.ec,com.es,com.fj,com.fr,com.gl,com.gt,com.hk,com.hr,com.hu,com.kg,com.ki,com.lc,com.mg,com.mm,com.ms,com.mt,com.mu,com.mx,com.my,com.nf,com.ng,com.ni,com.pa,com.ph,com.pl,com.pt,com.qa,com.ro,com.ru,com.sb,com.sc,com.sg,com.sv,com.tr,com.tw,com.ua,com.uy,com.ve,com.vn,cq.cn,de.com,de.org,ecn.br,edu.au,edu.cn,edu.hk,edu.mm,edu.my,edu.pt,edu.qa,edu.tr,eng.br,ernet.in,esp.br,etc.br,eti.br,eu.com,eu.int,eu.lv,firm.in,firm.ro,fm.br,fot.br,fst.br,g12.br,gb.com,gb.net,gd.cn,gen.in,go.jp,go.kr,go.th,gov.au,gov.az,gov.br,gov.cn,gov.il,gov.in,gov.mm,gov.my,gov.qa,gov.sg,gov.tr,gov.tw,gs.cn,gv.ac,gv.at,gx.cn,gz.cn,he.cn,hi.cn,hk.cn,hl.cn,hu.com,id.au,idv.tw,in.ua,ind.br,ind.in,inf.br,info.pl,info.ro,info.tr,info.ve,iwi.nz,jl.cn,jor.br,js.cn,k12.il,k12.tr,kr.com,lel.br,ln.cn,ltd.uk,maori.nz,mb.ca,me.uk,med.br,mi.th,mil.br,mo.cn,muni.il,nb.ca,ne.jp,ne.kr,net.ag,net.ai,net.au,net.br,net.cn,net.do,net.gl,net.hk,net.il,net.in,net.kg,net.ki,net.lc,net.mg,net.mm,net.mu,net.ni,net.nz,net.pl,net.ru,net.sb,net.sc,net.sg,net.th,net.tr,net.tw,net.uk,net.ve,nf.ca,nm.cn,nm.kr,no.com,nom.br,nom.ni,nom.ro,ns.ca,nt.ca,nt.ro,ntr.br,nx.cn,odo.br,off.ai,on.ca,or.ac,or.at,or.jp,or.kr,or.th,org.ag,org.ai,org.au,org.br,org.cn,org.do,org.es,org.gl,org.hk,org.in,org.kg,org.ki,org.lc,org.mg,org.mm,org.ms,org.nf,org.ni,org.nz,org.pl,org.ro,org.ru,org.sb,org.sc,org.sg,org.tr,org.tw,org.uk,org.ve,pe.ca,plc.uk,ppg.br,presse.fr,pro.br,psc.br,psi.br,qc.ca,qc.com,qh.cn,rec.br,rec.ro,res.in,sa.com,sc.cn,sch.ul,se.com,se.net,sh.cn,sk.ca,slg.br,sn.cn,store.ro,tj.cn,tm.fr,tm.mc,tm.ro,tmp.br,tur.br,tv.br,tv.tr,tw.cn,uk.com,uk.net,us.com,uy.com,vet.br,waw.pl,web.ve,www.ro,xj.cn,xz.cn,yk.ca,yn.cn,zj.cn,zlg.br".split(",");e=e.toLowerCase();var n=e.split("."),r=n.length;if(r<2)return e;var i=n.slice(-2).join(".");return r>=3&&s.inArray(i,t)>=0?n.slice(-3).join("."):i}function i(e,t){t!="localhost"&&!parseInt(t.split(".").slice(-1))&&(t=r(t));var n=0;for(var i=t.length-1;i>=0;i--)n+=t.charCodeAt(i)*28939532831;n=(""+n).substring(0,7);for(i=0;i<e.length;i++)if(n===e[i].substring(1,8))return 1}var s=jQuery,o=e.conf,u=o.swf.indexOf("flowplayer.org")&&o.e&&t.data("origin"),a=u?n(u):location.hostname,f=o.key;location.protocol=="file:"&&(a="localhost"),e.load.ed=1,o.hostname=a,o.origin=u||location.href,u&&t.addClass("is-embedded"),typeof f=="string"&&(f=f.split(/,\s*/));if(f&&typeof i=="function"&&i(f,a))o.logo&&t.append(s("<a>",{"class":"fp-logo",href:u}).append(s("<img/>",{src:o.logo})));else{var l=s("<a/>").attr("onclick","").appendTo(t),c=s(".fp-controls",t),h=s('<div class="fp-context-menu"><ul><li class="copyright">&copy; 2013</li><li><a href="http://flowplayer.org">About Flowplayer</a></li><li><a href="http://flowplayer.org/license">GPL based license</a></li></ul></div>').appendTo(t);e.bind("pause resume finish unload",function(e,n){var r=-1;n.video.src&&s.each([["org","flowplayer","drive"],["org","flowplayer","my"]],function(e,t){return r=n.video.src.indexOf("://"+t.reverse().join(".")),r===-1}),/pause|resume/.test(e.type)&&n.engine!="flash"&&r!=4&&r!=5?(l.show().css({position:"absolute",left:16,bottom:36,zIndex:99999,width:120,height:27,backgroundImage:"url("+[".png","fplogo","/",".com","foliovision","//"].reverse().join("")+")"}),n.load.ed=l.is(":visible")&&s.contains(t[0],h[0]),n.load.ed||n.pause()):l.hide()})}});
7
 
8
  /*
9
  FV Flowplayer additions!
10
  */
11
+ if( typeof(fv_flowplayer_conf) != "undefined" ) {
12
+ flowplayer.conf = fv_flowplayer_conf
13
+ }
14
+
15
  if( typeof(fv_flowplayer_admin_input) != "undefined" && fv_flowplayer_admin_input ) {
16
  jQuery(document).keyup(function(e) {
17
  if (e.keyCode == 27) { fv_wp_flowplayer_admin_show_notice(); } // esc
37
  jQuery('#fv_wp_fp_notice_'+id).toggle();
38
 
39
  var api = flowplayer(), currentPos;
40
+ if( typeof(api) != "undefined" && jQuery('#fv_wp_fp_notice_'+id).parent().hasClass("fv-wp-flowplayer-notice") ) {
41
  api.disable(false);
42
+ } else if( typeof(api) != "undefined" ) {
43
  api.disable(true);
44
  }
45
 
83
  }
84
  );
85
  }
86
+
87
+ function fv_flowplayer_admin_message_parse_group(aInfo) {
88
+ var sOutput = '';
89
+ if( typeof(aInfo) != "undefined" && Object.keys(aInfo).length > 0 ) {
90
+ sOutput += '<p>';
91
+ for( var j in aInfo ) {
92
+ if( j == parseInt(j) ){
93
+ sOutput += aInfo[j]+'<br />';
94
+ } else {
95
+ sOutput += j+': <tt>'+aInfo[j]+'</tt><br />';
96
+ }
97
+ }
98
+ sOutput += '</p>';
99
+ }
100
+ return sOutput;
101
+ }
102
+
103
+
104
+
105
+ if( typeof(fv_flowplayer_admin_test_media_array) != "undefined" ) {
106
+ var fv_flowplayer_scroll_video_checker = false;
107
+ var fv_flowplayer_scroll_video_checker_status = [];
108
+ jQuery(document).ready( function() { fv_flowplayer_scroll_video_checker = true; } );
109
+ jQuery(document).scroll( function() { fv_flowplayer_scroll_video_checker = true; } );
110
+
111
+ setInterval( function() {
112
+ if( !fv_flowplayer_scroll_video_checker ) return;
113
+
114
+ var iMin = jQuery(window).scrollTop();
115
+ var iMax = iMin + jQuery(window).height();
116
+ jQuery('.flowplayer').each( function() {
117
+ var iPlayer = jQuery(this).offset().top;
118
+ if( iPlayer > iMin && iPlayer < iMax ) {
119
+ if( typeof(fv_flowplayer_scroll_video_checker_status[jQuery(this).attr('id')]) == "undefined" ) { // todo: store this somewhere else!
120
+ fv_flowplayer_scroll_video_checker_status[jQuery(this).attr('id')] = true;
121
+ var sID = jQuery(this).attr('id').replace(/wpfp_/,'');
122
+ if( typeof(fv_flowplayer_admin_test_media_array[sID]) != "undefined" ) {
123
+ fv_flowplayer_admin_test_media( sID, fv_flowplayer_admin_test_media_array[sID] );
124
+ }
125
+ }
126
+ }
127
+ } );
128
+ fv_flowplayer_scroll_video_checker = false;
129
+ }, 500 );
130
+ }
131
+
132
+
133
  function fv_flowplayer_admin_test_media( hash, media ) {
134
+ var hVideoChecker = jQuery('#wpfp_notice_'+hash);
135
+ jQuery('#wpfp_notice_'+hash).parent().append(jQuery('#wpfp_notice_'+hash));
136
+ jQuery('#wpfp_notice_'+hash).show();
137
+
138
+ jQuery.post( 'https://foliovision.com/fv-services/', { action: 'vid_check', media: media, hash: hash, site: flowplayer.conf.video_checker_site }, function( response ) {
139
  var obj;
140
  try {
141
+ response = response.replace( /[\s\S]*<FVFLOWPLAYER>/, '' );
142
+ response = response.replace( /<\/FVFLOWPLAYER>[\s\S]*/, '' );
143
  obj = jQuery.parseJSON( response );
144
+
145
+ var sCheckerInfo = '';
146
+ var sCheckerDetails = '';
147
+ var sResponseClass = 'vid-ok';
148
+ var sResponseMsg = 'Video OK';
149
+
150
+ for( var i in obj ) {
151
+
152
+ if( i != "global" ) {
153
+ sCheckerInfo += '<p>Analysis of <a href="'+i+'">'+i+'</a></p>';
154
+ }
155
+ sCheckerInfo += fv_flowplayer_admin_message_parse_group(obj[i].info);
156
+
157
+ if( typeof(obj[i].warnings) != "undefined" && Object.keys(obj[i].warnings).length > 0 && sResponseClass != 'vid-issues' ) {
158
+ sResponseMsg = 'Video Warnings';
159
+ sResponseClass = 'vid-warning';
160
+ sCheckerInfo += fv_flowplayer_admin_message_parse_group(obj[i].warnings);
161
+ }
162
+
163
+ if( typeof(obj[i].errors) != "undefined" && Object.keys(obj[i].errors).length > 0 ) {
164
+ sResponseMsg = 'Video Issues';
165
+ sResponseClass = 'vid-issues';
166
+ sCheckerInfo += fv_flowplayer_admin_message_parse_group(obj[i].errors);
167
+ }
168
+
169
+ jQuery('#wpfp_notice_'+hash).find('.video-checker-result').addClass(sResponseClass).html(sResponseMsg);
170
+
171
+ sCheckerDetails += fv_flowplayer_admin_message_parse_group(obj[i].details);
172
+
173
+ }
174
+ jQuery('#wpfp_notice_'+hash).find('.video-checker-result').wrap('<a class="fv_wp_flowplayer_dialog_link"></a>');
175
+ jQuery('#wpfp_notice_'+hash).find('.fv_wp_flowplayer_dialog_link').click( function() { fv_wp_flowplayer_admin_show_notice( hash, this) } );
176
+ jQuery('#wpfp_notice_'+hash).find('.mail-content-notice').html('<p>'+sCheckerInfo+'</p>');
177
+ jQuery('#wpfp_notice_'+hash).find('.mail-content-details .fv-wp-flowplayer-notice-parsed').html(sCheckerDetails)
178
+
179
  } catch(e) {
180
+ console.log(e);
181
  jQuery('#wpfp_notice_'+hash).html('<p>Admin: Error parsing JSON</p>');
182
  return;
183
  }
184
 
185
+ } ).error(function() {
186
+ jQuery('#wpfp_notice_'+hash).html('<p>Admin: <abbr title="Check the PHP error log.">Check failed</abbr>.</p>');
187
+ });
188
  }
189
 
190
  function fv_flowplayer_autoplay( hash ) {
245
  }
246
 
247
  function fv_flowplayer_browser_ie( hash ) {
248
+ if( (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) >= 9) || !!navigator.userAgent.match(/Trident.*rv[ :]*11\./) ) {
249
  jQuery('#wpfp_'+hash).attr('data-engine','flash');
250
  }
251
  }
264
  if( (navigator.platform.indexOf("iPhone") != -1) || (navigator.platform.indexOf("iPod") != -1) || (navigator.platform.indexOf("iPad") != -1) ) {
265
  jQuery(window).trigger('load');
266
  }
267
+ jQuery('.flowplayer').mouseleave( function() {
268
+ jQuery(this).find('.fvp-share-bar').removeClass('visible');
269
+ jQuery(this).find('.embed-code').hide();
270
+ } );
271
+ jQuery('.flowplayer .embed-code-toggle').click( function() {
272
+ jQuery(this).parent().siblings('.embed-code').toggle();
273
+ jQuery(this).parent().parent().toggleClass('visible');
274
+ return false;
275
+ } );
276
  } );
277
 
278
  function fv_flowplayer_loop( hash ) {
312
  }
313
 
314
  function fv_flowplayer_redirect( hash, url ) {
315
+ jQuery('#wpfp_'+hash).bind('finish', function(e,api) {
316
+ var windowFVFlowplayerRedirect = window.open(url, '_blank');
317
+ if( ( typeof(windowFVFlowplayerRedirect) == "undefined" || windowFVFlowplayerRedirect == null ) && ( typeof(api.video.is_last) == "undefined" || api.video.is_last ) ) {
318
+ location.href= url;
319
+ }
320
+ } );
321
  }
322
 
323
  var fv_flowplayer_safety_resize_arr = Array();
326
  var fv_flowplayer_safety_resize_init = false;
327
 
328
  jQuery('.flowplayer').each( function() {
329
+ if( jQuery(this).hasClass('lightboxed') ) return;
330
+
331
  if( jQuery(this).width() < 30 || jQuery(this).height() < 20 ) {
332
  fv_flowplayer_safety_resize_init = true
333
  var el = jQuery(this);
344
  if( fv_flowplayer_safety_resize_init ) {
345
  jQuery(window).resize(function() {
346
  jQuery('.flowplayer').each( function() {
347
+ if( jQuery(this).hasClass('lightboxed') ) return;
348
+
349
  if( fv_flowplayer_safety_resize_arr[jQuery(this).attr('id')] ) {
350
  jQuery(this).width( fv_flowplayer_safety_resize_arr[jQuery(this).attr('id')].width() );
351
  jQuery(this).height( parseInt(jQuery(this).width() * jQuery(this).attr('data-ratio')) );
355
  }
356
  }
357
 
358
+ if( typeof(flowplayer.conf.safety_resize) != "undefined" && flowplayer.conf.safety_resize ) {
359
  jQuery(document).ready(function() { fv_flowplayer_safety_resize(); } );
360
  }
361
 
362
  jQuery(document).ready( function() {
363
+ if( jQuery('.fp-playlist-external').length == 0 ) return;
364
+ jQuery('.fp-playlist-external' ).each( function(i,el) {
365
+ if( jQuery('a',el).length == 0 ) return;
366
+ jQuery('a',el).click( function(e) {
367
+ e.preventDefault();
368
+ if( jQuery( '#' + jQuery(this).parent().attr('rel') ).hasClass('dynamic-playlist') ) return;
369
+ jQuery('a',el).removeClass('is-active');
370
+ jQuery( '#' + jQuery(this).parent().attr('rel') ).flowplayer().play( jQuery('a',el).index(this) );
371
+ jQuery(this).addClass('is-active');
372
+ } );
373
+ } );
374
+
375
+ jQuery('.flowplayer').bind('load', function(e,api,video) {
376
+ var that = this;
377
+ setTimeout( function() {
378
+ if( video.index > -1 ) {
379
+ var playlist_external = jQuery('[rel='+jQuery(that).attr('id')+'] a');
380
+ if( playlist_external.length > 0 ) {
381
+ jQuery(playlist_external).removeClass('is-active');
382
+ jQuery(playlist_external).eq(video.index).addClass('is-active');
383
+ }
384
+ }
385
+ }, 250 );
386
+ } );
387
  } );
388
 
389
+ jQuery( function() { if( typeof(flowplayer.conf) != "undefined" && typeof(flowplayer.conf.key) == "undefined" ) { jQuery('.flowplayer').unbind('contextmenu'); } } );
 
 
 
 
390
 
391
  if( typeof(fv_flowplayer_playlists) != "undefined" ) {
392
  for( var i in fv_flowplayer_playlists ) {
402
  fv_flowplayer_autoplay( i );
403
  }
404
  }
 
 
 
 
 
405
  if( typeof(fv_flowplayer_browser_ff_m4v_array) != "undefined" ) {
406
  for( var i in fv_flowplayer_browser_ff_m4v_array ) {
407
  fv_flowplayer_browser_ff_m4v( i );
436
  for( var i in fv_flowplayer_redirect_array ) {
437
  fv_flowplayer_redirect( i, fv_flowplayer_redirect_array[i] );
438
  }
439
+ }
440
+
441
+ jQuery(".flowplayer").bind('boot', function() {
442
+ jQuery('.fvfp_admin_error').remove();
443
+ } );
444
+
445
+ if( jQuery.browser.msie && parseInt(jQuery.browser.version, 10) < 9 ) {
446
+ jQuery('.flowplayer').each( function() {
447
+ jQuery(this).css('width', jQuery(this).css('max-width'));
448
+ jQuery(this).css('height', jQuery(this).css('max-height'));
449
+ } );
450
+ }
451
+
452
+
453
+ jQuery('.flowplayer').bind("ready", function (e, api, video) {
454
+ setTimeout( function () {
455
+ jQuery('.fvp-share-bar',e.currentTarget).appendTo(jQuery('.fvp-share-bar',e.currentTarget).parent().find('.fp-ui'));
456
+ jQuery('.fvp-share-bar',e.currentTarget).show();
457
+ jQuery(e.currentTarget).find('.fp-embed').hide().click();
458
+ jQuery(e.currentTarget).find('.embed-code textarea').val(jQuery(e.currentTarget).find('.fp-embed-code textarea').val());
459
+ }, 100 );
460
+ });
461
+
includes/fp-api.php CHANGED
@@ -299,6 +299,9 @@ class FV_Wordpress_Flowplayer_Plugin
299
  $position = ( isset($aPopup['position']) ) ? $aPopup['position'] : array( 'edge' => 'top', 'align' => 'center' );
300
 
301
  $opt_arr = array( 'content' => $content, 'position' => $position );
 
 
 
302
 
303
  $function2 = $this->class_name.'_store_answer("'.$sKey.'", "false","' . $sNonce . '")';
304
  $function1 = $this->class_name.'_store_answer("'.$sKey.'", "true","' . $sNonce . '")';
@@ -314,7 +317,7 @@ class FV_Wordpress_Flowplayer_Plugin
314
  _ajax_nonce : nonce
315
  }
316
  jQuery.post(ajaxurl, post_data, function () {
317
- jQuery('#wp-pointer-0').remove(); // todo: does this really work?
318
  });
319
  }
320
  //]]>
@@ -347,11 +350,16 @@ class FV_Wordpress_Flowplayer_Plugin
347
  });
348
 
349
  <?php echo $id; ?>_setup = function () {
350
- $('<?php echo $selector; ?>').pointer(<?php echo $id; ?>_pointer_options).pointer('open');
 
 
 
 
 
351
  <?php if ( $button2 ) { ?>
352
- jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + '<?php echo addslashes($button2); ?>' + '</a>');
353
- jQuery('#pointer-primary').click(function () { <?php echo $button1_function; ?> });
354
- jQuery('#pointer-close').click(function () { <?php echo $button2_function; ?> });
355
  <?php } ?>
356
  };
357
 
@@ -370,6 +378,8 @@ class FV_Wordpress_Flowplayer_Plugin
370
  function is_min_wp( $version ) {
371
  return version_compare( $GLOBALS['wp_version'], $version. 'alpha', '>=' );
372
  }
 
 
373
 
374
  }
375
 
299
  $position = ( isset($aPopup['position']) ) ? $aPopup['position'] : array( 'edge' => 'top', 'align' => 'center' );
300
 
301
  $opt_arr = array( 'content' => $content, 'position' => $position );
302
+
303
+ if( isset($aPopup['pointerClass']) ) $opt_arr['pointerClass'] = $aPopup['pointerClass'];
304
+ if( isset($aPopup['pointerWidth']) ) $opt_arr['pointerWidth'] = $aPopup['pointerWidth'];
305
 
306
  $function2 = $this->class_name.'_store_answer("'.$sKey.'", "false","' . $sNonce . '")';
307
  $function1 = $this->class_name.'_store_answer("'.$sKey.'", "true","' . $sNonce . '")';
317
  _ajax_nonce : nonce
318
  }
319
  jQuery.post(ajaxurl, post_data, function () {
320
+ jQuery('.'+key).remove();
321
  });
322
  }
323
  //]]>
350
  });
351
 
352
  <?php echo $id; ?>_setup = function () {
353
+ var sSelector = '<?php echo $selector; ?>';
354
+ if( $(sSelector).length == 0 ){
355
+ sSelector = '#wpadminbar';
356
+ }
357
+ $(sSelector).append('<div class="<?php echo $id; ?>"></div>');
358
+ $(sSelector+' .<?php echo $id; ?>').pointer(<?php echo $id; ?>_pointer_options).pointer('open');
359
  <?php if ( $button2 ) { ?>
360
+ jQuery('.<?php echo $id; ?> #pointer-close').after('<a id="pointer-primary" class="button-primary">' + '<?php echo addslashes($button2); ?>' + '</a>');
361
+ jQuery('.<?php echo $id; ?> #pointer-primary').click(function () { <?php echo $button1_function; ?> });
362
+ jQuery('.<?php echo $id; ?> #pointer-close').click(function () { <?php echo $button2_function; ?> });
363
  <?php } ?>
364
  };
365
 
378
  function is_min_wp( $version ) {
379
  return version_compare( $GLOBALS['wp_version'], $version. 'alpha', '>=' );
380
  }
381
+
382
+
383
 
384
  }
385
 
includes/getid3/extension.cache.dbm.php DELETED
@@ -1,208 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // //
8
- // extension.cache.dbm.php - part of getID3() //
9
- // Please see readme.txt for more information //
10
- // ///
11
- /////////////////////////////////////////////////////////////////
12
- // //
13
- // This extension written by Allan Hansen <ah�artemis*dk> //
14
- // ///
15
- /////////////////////////////////////////////////////////////////
16
-
17
-
18
- /**
19
- * This is a caching extension for getID3(). It works the exact same
20
- * way as the getID3 class, but return cached information very fast
21
- *
22
- * Example:
23
- *
24
- * Normal getID3 usage (example):
25
- *
26
- * require_once 'getid3/getid3.php';
27
- * $getID3 = new getID3;
28
- * $getID3->encoding = 'UTF-8';
29
- * $info1 = $getID3->analyze('file1.flac');
30
- * $info2 = $getID3->analyze('file2.wv');
31
- *
32
- * getID3_cached usage:
33
- *
34
- * require_once 'getid3/getid3.php';
35
- * require_once 'getid3/getid3/extension.cache.dbm.php';
36
- * $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
37
- * '/tmp/getid3_cache.lock');
38
- * $getID3->encoding = 'UTF-8';
39
- * $info1 = $getID3->analyze('file1.flac');
40
- * $info2 = $getID3->analyze('file2.wv');
41
- *
42
- *
43
- * Supported Cache Types
44
- *
45
- * SQL Databases: (use extension.cache.mysql)
46
- *
47
- * cache_type cache_options
48
- * -------------------------------------------------------------------
49
- * mysql host, database, username, password
50
- *
51
- *
52
- * DBM-Style Databases: (this extension)
53
- *
54
- * cache_type cache_options
55
- * -------------------------------------------------------------------
56
- * gdbm dbm_filename, lock_filename
57
- * ndbm dbm_filename, lock_filename
58
- * db2 dbm_filename, lock_filename
59
- * db3 dbm_filename, lock_filename
60
- * db4 dbm_filename, lock_filename (PHP5 required)
61
- *
62
- * PHP must have write access to both dbm_filename and lock_filename.
63
- *
64
- *
65
- * Recommended Cache Types
66
- *
67
- * Infrequent updates, many reads any DBM
68
- * Frequent updates mysql
69
- */
70
-
71
-
72
- class getID3_cached_dbm extends getID3
73
- {
74
-
75
- // public: constructor - see top of this file for cache type and cache_options
76
- public function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
77
-
78
- // Check for dba extension
79
- if (!extension_loaded('dba')) {
80
- throw new Exception('PHP is not compiled with dba support, required to use DBM style cache.');
81
- }
82
-
83
- // Check for specific dba driver
84
- if (!function_exists('dba_handlers') || !in_array($cache_type, dba_handlers())) {
85
- throw new Exception('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
86
- }
87
-
88
- // Create lock file if needed
89
- if (!file_exists($lock_filename)) {
90
- if (!touch($lock_filename)) {
91
- throw new Exception('failed to create lock file: '.$lock_filename);
92
- }
93
- }
94
-
95
- // Open lock file for writing
96
- if (!is_writeable($lock_filename)) {
97
- throw new Exception('lock file: '.$lock_filename.' is not writable');
98
- }
99
- $this->lock = fopen($lock_filename, 'w');
100
-
101
- // Acquire exclusive write lock to lock file
102
- flock($this->lock, LOCK_EX);
103
-
104
- // Create dbm-file if needed
105
- if (!file_exists($dbm_filename)) {
106
- if (!touch($dbm_filename)) {
107
- throw new Exception('failed to create dbm file: '.$dbm_filename);
108
- }
109
- }
110
-
111
- // Try to open dbm file for writing
112
- $this->dba = dba_open($dbm_filename, 'w', $cache_type);
113
- if (!$this->dba) {
114
-
115
- // Failed - create new dbm file
116
- $this->dba = dba_open($dbm_filename, 'n', $cache_type);
117
-
118
- if (!$this->dba) {
119
- throw new Exception('failed to create dbm file: '.$dbm_filename);
120
- }
121
-
122
- // Insert getID3 version number
123
- dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
124
- }
125
-
126
- // Init misc values
127
- $this->cache_type = $cache_type;
128
- $this->dbm_filename = $dbm_filename;
129
-
130
- // Register destructor
131
- register_shutdown_function(array($this, '__destruct'));
132
-
133
- // Check version number and clear cache if changed
134
- if (dba_fetch(getID3::VERSION, $this->dba) != getID3::VERSION) {
135
- $this->clear_cache();
136
- }
137
-
138
- parent::getID3();
139
- }
140
-
141
-
142
-
143
- // public: destructor
144
- public function __destruct() {
145
-
146
- // Close dbm file
147
- dba_close($this->dba);
148
-
149
- // Release exclusive lock
150
- flock($this->lock, LOCK_UN);
151
-
152
- // Close lock file
153
- fclose($this->lock);
154
- }
155
-
156
-
157
-
158
- // public: clear cache
159
- public function clear_cache() {
160
-
161
- // Close dbm file
162
- dba_close($this->dba);
163
-
164
- // Create new dbm file
165
- $this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
166
-
167
- if (!$this->dba) {
168
- throw new Exception('failed to clear cache/recreate dbm file: '.$this->dbm_filename);
169
- }
170
-
171
- // Insert getID3 version number
172
- dba_insert(getID3::VERSION, getID3::VERSION, $this->dba);
173
-
174
- // Re-register shutdown function
175
- register_shutdown_function(array($this, '__destruct'));
176
- }
177
-
178
-
179
-
180
- // public: analyze file
181
- public function analyze($filename) {
182
-
183
- if (file_exists($filename)) {
184
-
185
- // Calc key filename::mod_time::size - should be unique
186
- $key = $filename.'::'.filemtime($filename).'::'.filesize($filename);
187
-
188
- // Loopup key
189
- $result = dba_fetch($key, $this->dba);
190
-
191
- // Hit
192
- if ($result !== false) {
193
- return unserialize($result);
194
- }
195
- }
196
-
197
- // Miss
198
- $result = parent::analyze($filename);
199
-
200
- // Save result
201
- if (file_exists($filename)) {
202
- dba_insert($key, serialize($result), $this->dba);
203
- }
204
-
205
- return $result;
206
- }
207
-
208
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/extension.cache.mysql.php DELETED
@@ -1,171 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // //
8
- // extension.cache.mysql.php - part of getID3() //
9
- // Please see readme.txt for more information //
10
- // ///
11
- /////////////////////////////////////////////////////////////////
12
- // //
13
- // This extension written by Allan Hansen <ah�artemis*dk> //
14
- // Table name mod by Carlo Capocasa <calro�carlocapocasa*com> //
15
- // ///
16
- /////////////////////////////////////////////////////////////////
17
-
18
-
19
- /**
20
- * This is a caching extension for getID3(). It works the exact same
21
- * way as the getID3 class, but return cached information very fast
22
- *
23
- * Example: (see also demo.cache.mysql.php in /demo/)
24
- *
25
- * Normal getID3 usage (example):
26
- *
27
- * require_once 'getid3/getid3.php';
28
- * $getID3 = new getID3;
29
- * $getID3->encoding = 'UTF-8';
30
- * $info1 = $getID3->analyze('file1.flac');
31
- * $info2 = $getID3->analyze('file2.wv');
32
- *
33
- * getID3_cached usage:
34
- *
35
- * require_once 'getid3/getid3.php';
36
- * require_once 'getid3/getid3/extension.cache.mysql.php';
37
- * // 5th parameter (tablename) is optional, default is 'getid3_cache'
38
- * $getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password', 'tablename');
39
- * $getID3->encoding = 'UTF-8';
40
- * $info1 = $getID3->analyze('file1.flac');
41
- * $info2 = $getID3->analyze('file2.wv');
42
- *
43
- *
44
- * Supported Cache Types (this extension)
45
- *
46
- * SQL Databases:
47
- *
48
- * cache_type cache_options
49
- * -------------------------------------------------------------------
50
- * mysql host, database, username, password
51
- *
52
- *
53
- * DBM-Style Databases: (use extension.cache.dbm)
54
- *
55
- * cache_type cache_options
56
- * -------------------------------------------------------------------
57
- * gdbm dbm_filename, lock_filename
58
- * ndbm dbm_filename, lock_filename
59
- * db2 dbm_filename, lock_filename
60
- * db3 dbm_filename, lock_filename
61
- * db4 dbm_filename, lock_filename (PHP5 required)
62
- *
63
- * PHP must have write access to both dbm_filename and lock_filename.
64
- *
65
- *
66
- * Recommended Cache Types
67
- *
68
- * Infrequent updates, many reads any DBM
69
- * Frequent updates mysql
70
- */
71
-
72
-
73
- class getID3_cached_mysql extends getID3
74
- {
75
-
76
- // private vars
77
- private $cursor;
78
- private $connection;
79
-
80
-
81
- // public: constructor - see top of this file for cache type and cache_options
82
- public function getID3_cached_mysql($host, $database, $username, $password, $table='getid3_cache') {
83
-
84
- // Check for mysql support
85
- if (!function_exists('mysql_pconnect')) {
86
- throw new Exception('PHP not compiled with mysql support.');
87
- }
88
-
89
- // Connect to database
90
- $this->connection = mysql_pconnect($host, $username, $password);
91
- if (!$this->connection) {
92
- throw new Exception('mysql_pconnect() failed - check permissions and spelling.');
93
- }
94
-
95
- // Select database
96
- if (!mysql_select_db($database, $this->connection)) {
97
- throw new Exception('Cannot use database '.$database);
98
- }
99
-
100
- // Set table
101
- $this->table = $table;
102
-
103
- // Create cache table if not exists
104
- $this->create_table();
105
-
106
- // Check version number and clear cache if changed
107
- $version = '';
108
- if ($this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string(getID3::VERSION)."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection)) {
109
- list($version) = mysql_fetch_array($this->cursor);
110
- }
111
- if ($version != getID3::VERSION) {
112
- $this->clear_cache();
113
- }
114
-
115
- parent::getID3();
116
- }
117
-
118
-
119
-
120
- // public: clear cache
121
- public function clear_cache() {
122
-
123
- $this->cursor = mysql_query("DELETE FROM `".mysql_real_escape_string($this->table)."`", $this->connection);
124
- $this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` VALUES ('".getID3::VERSION."', -1, -1, -1, '".getID3::VERSION."')", $this->connection);
125
- }
126
-
127
-
128
-
129
- // public: analyze file
130
- public function analyze($filename) {
131
-
132
- if (file_exists($filename)) {
133
-
134
- // Short-hands
135
- $filetime = filemtime($filename);
136
- $filesize = filesize($filename);
137
-
138
- // Lookup file
139
- $this->cursor = mysql_query("SELECT `value` FROM `".mysql_real_escape_string($this->table)."` WHERE (`filename` = '".mysql_real_escape_string($filename)."') AND (`filesize` = '".mysql_real_escape_string($filesize)."') AND (`filetime` = '".mysql_real_escape_string($filetime)."')", $this->connection);
140
- if (mysql_num_rows($this->cursor) > 0) {
141
- // Hit
142
- list($result) = mysql_fetch_array($this->cursor);
143
- return unserialize(base64_decode($result));
144
- }
145
- }
146
-
147
- // Miss
148
- $analysis = parent::analyze($filename);
149
-
150
- // Save result
151
- if (file_exists($filename)) {
152
- $this->cursor = mysql_query("INSERT INTO `".mysql_real_escape_string($this->table)."` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".mysql_real_escape_string($filename)."', '".mysql_real_escape_string($filesize)."', '".mysql_real_escape_string($filetime)."', '".mysql_real_escape_string(time())."', '".mysql_real_escape_string(base64_encode(serialize($analysis)))."')", $this->connection);
153
- }
154
- return $analysis;
155
- }
156
-
157
-
158
-
159
- // private: (re)create sql table
160
- private function create_table($drop=false) {
161
-
162
- $this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `".mysql_real_escape_string($this->table)."` (
163
- `filename` VARCHAR(255) NOT NULL DEFAULT '',
164
- `filesize` INT(11) NOT NULL DEFAULT '0',
165
- `filetime` INT(11) NOT NULL DEFAULT '0',
166
- `analyzetime` INT(11) NOT NULL DEFAULT '0',
167
- `value` TEXT NOT NULL,
168
- PRIMARY KEY (`filename`,`filesize`,`filetime`)) ENGINE=MyISAM", $this->connection);
169
- echo mysql_error($this->connection);
170
- }
171
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/extension.cache.sqlite3.php DELETED
@@ -1,264 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org ///
6
- /////////////////////////////////////////////////////////////////////////////////
7
- /// //
8
- // extension.cache.sqlite3.php - part of getID3() //
9
- // Please see readme.txt for more information //
10
- // ///
11
- /////////////////////////////////////////////////////////////////////////////////
12
- /// //
13
- // MySQL extension written by Allan Hansen <ah�artemis*dk> //
14
- // Table name mod by Carlo Capocasa <calro�carlocapocasa*com> //
15
- // MySQL extension was reworked for SQLite3 by Karl G. Holz <newaeon�mac*com> //
16
- // ///
17
- /////////////////////////////////////////////////////////////////////////////////
18
- /**
19
- * This is a caching extension for getID3(). It works the exact same
20
- * way as the getID3 class, but return cached information much faster
21
- *
22
- * Normal getID3 usage (example):
23
- *
24
- * require_once 'getid3/getid3.php';
25
- * $getID3 = new getID3;
26
- * $getID3->encoding = 'UTF-8';
27
- * $info1 = $getID3->analyze('file1.flac');
28
- * $info2 = $getID3->analyze('file2.wv');
29
- *
30
- * getID3_cached usage:
31
- *
32
- * require_once 'getid3/getid3.php';
33
- * require_once 'getid3/extension.cache.sqlite3.php';
34
- * // all parameters are optional, defaults are:
35
- * $getID3 = new getID3_cached_sqlite3($table='getid3_cache', $hide=FALSE);
36
- * $getID3->encoding = 'UTF-8';
37
- * $info1 = $getID3->analyze('file1.flac');
38
- * $info2 = $getID3->analyze('file2.wv');
39
- *
40
- *
41
- * Supported Cache Types (this extension)
42
- *
43
- * SQL Databases:
44
- *
45
- * cache_type cache_options
46
- * -------------------------------------------------------------------
47
- * mysql host, database, username, password
48
- *
49
- * sqlite3 table='getid3_cache', hide=false (PHP5)
50
- *
51
-
52
- *** database file will be stored in the same directory as this script,
53
- *** webserver must have write access to that directory!
54
- *** set $hide to TRUE to prefix db file with .ht to pervent access from web client
55
- *** this is a default setting in the Apache configuration:
56
-
57
- # The following lines prevent .htaccess and .htpasswd files from being viewed by Web clients.
58
-
59
- <Files ~ "^\.ht">
60
- Order allow,deny
61
- Deny from all
62
- Satisfy all
63
- </Files>
64
-
65
- ********************************************************************************
66
- *
67
- * -------------------------------------------------------------------
68
- * DBM-Style Databases: (use extension.cache.dbm)
69
- *
70
- * cache_type cache_options
71
- * -------------------------------------------------------------------
72
- * gdbm dbm_filename, lock_filename
73
- * ndbm dbm_filename, lock_filename
74
- * db2 dbm_filename, lock_filename
75
- * db3 dbm_filename, lock_filename
76
- * db4 dbm_filename, lock_filename (PHP5 required)
77
- *
78
- * PHP must have write access to both dbm_filename and lock_filename.
79
- *
80
- * Recommended Cache Types
81
- *
82
- * Infrequent updates, many reads any DBM
83
- * Frequent updates mysql
84
- ********************************************************************************
85
- *
86
- * IMHO this is still a bit slow, I'm using this with MP4/MOV/ M4v files
87
- * there is a plan to add directory scanning and analyzing to make things work much faster
88
- *
89
- *
90
- */
91
- class getID3_cached_sqlite3 extends getID3 {
92
-
93
- /**
94
- * __construct()
95
- * @param string $table holds name of sqlite table
96
- * @return type
97
- */
98
- public function __construct($table='getid3_cache', $hide=false) {
99
- $this->table = $table; // Set table
100
- $file = dirname(__FILE__).'/'.basename(__FILE__, 'php').'sqlite';
101
- if ($hide) {
102
- $file = dirname(__FILE__).'/.ht.'.basename(__FILE__, 'php').'sqlite';
103
- }
104
- $this->db = new SQLite3($file);
105
- $db = $this->db;
106
- $this->create_table(); // Create cache table if not exists
107
- $version = '';
108
- $sql = $this->version_check;
109
- $stmt = $db->prepare($sql);
110
- $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
111
- $result = $stmt->execute();
112
- list($version) = $result->fetchArray();
113
- if ($version != getID3::VERSION) { // Check version number and clear cache if changed
114
- $this->clear_cache();
115
- }
116
- return parent::__construct();
117
- }
118
-
119
- /**
120
- * close the database connection
121
- */
122
- public function __destruct() {
123
- $db=$this->db;
124
- $db->close();
125
- }
126
-
127
- /**
128
- * hold the sqlite db
129
- * @var SQLite Resource
130
- */
131
- private $db;
132
-
133
- /**
134
- * table to use for caching
135
- * @var string $table
136
- */
137
- private $table;
138
-
139
- /**
140
- * clear the cache
141
- * @access private
142
- * @return type
143
- */
144
- private function clear_cache() {
145
- $db = $this->db;
146
- $sql = $this->delete_cache;
147
- $db->exec($sql);
148
- $sql = $this->set_version;
149
- $stmt = $db->prepare($sql);
150
- $stmt->bindValue(':filename', getID3::VERSION, SQLITE3_TEXT);
151
- $stmt->bindValue(':dirname', getID3::VERSION, SQLITE3_TEXT);
152
- $stmt->bindValue(':val', getID3::VERSION, SQLITE3_TEXT);
153
- return $stmt->execute();
154
- }
155
-
156
- /**
157
- * analyze file and cache them, if cached pull from the db
158
- * @param type $filename
159
- * @return boolean
160
- */
161
- public function analyze($filename) {
162
- if (!file_exists($filename)) {
163
- return false;
164
- }
165
- // items to track for caching
166
- $filetime = filemtime($filename);
167
- $filesize = filesize($filename);
168
- // this will be saved for a quick directory lookup of analized files
169
- // ... why do 50 seperate sql quries when you can do 1 for the same result
170
- $dirname = dirname($filename);
171
- // Lookup file
172
- $db = $this->db;
173
- $sql = $this->get_id3_data;
174
- $stmt = $db->prepare($sql);
175
- $stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
176
- $stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER);
177
- $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
178
- $res = $stmt->execute();
179
- list($result) = $res->fetchArray();
180
- if (count($result) > 0 ) {
181
- return unserialize(base64_decode($result));
182
- }
183
- // if it hasn't been analyzed before, then do it now
184
- $analysis = parent::analyze($filename);
185
- // Save result
186
- $sql = $this->cache_file;
187
- $stmt = $db->prepare($sql);
188
- $stmt->bindValue(':filename', $filename, SQLITE3_TEXT);
189
- $stmt->bindValue(':dirname', $dirname, SQLITE3_TEXT);
190
- $stmt->bindValue(':filesize', $filesize, SQLITE3_INTEGER);
191
- $stmt->bindValue(':filetime', $filetime, SQLITE3_INTEGER);
192
- $stmt->bindValue(':atime', time(), SQLITE3_INTEGER);
193
- $stmt->bindValue(':val', base64_encode(serialize($analysis)), SQLITE3_TEXT);
194
- $res = $stmt->execute();
195
- return $analysis;
196
- }
197
-
198
- /**
199
- * create data base table
200
- * this is almost the same as MySQL, with the exception of the dirname being added
201
- * @return type
202
- */
203
- private function create_table() {
204
- $db = $this->db;
205
- $sql = $this->make_table;
206
- return $db->exec($sql);
207
- }
208
-
209
- /**
210
- * get cached directory
211
- *
212
- * This function is not in the MySQL extention, it's ment to speed up requesting multiple files
213
- * which is ideal for podcasting, playlists, etc.
214
- *
215
- * @access public
216
- * @param string $dir directory to search the cache database for
217
- * @return array return an array of matching id3 data
218
- */
219
- public function get_cached_dir($dir) {
220
- $db = $this->db;
221
- $rows = array();
222
- $sql = $this->get_cached_dir;
223
- $stmt = $db->prepare($sql);
224
- $stmt->bindValue(':dirname', $dir, SQLITE3_TEXT);
225
- $res = $stmt->execute();
226
- while ($row=$res->fetchArray()) {
227
- $rows[] = unserialize(base64_decode($row));
228
- }
229
- return $rows;
230
- }
231
-
232
- /**
233
- * use the magical __get() for sql queries
234
- *
235
- * access as easy as $this->{case name}, returns NULL if query is not found
236
- */
237
- public function __get($name) {
238
- switch($name) {
239
- case 'version_check':
240
- return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'";
241
- break;
242
- case 'delete_cache':
243
- return "DELETE FROM $this->table";
244
- break;
245
- case 'set_version':
246
- return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)";
247
- break;
248
- case 'get_id3_data':
249
- return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime";
250
- break;
251
- case 'cache_file':
252
- return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
253
- break;
254
- case 'make_table':
255
- return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) NOT NULL DEFAULT '', dirname VARCHAR(255) NOT NULL DEFAULT '', filesize INT(11) NOT NULL DEFAULT '0', filetime INT(11) NOT NULL DEFAULT '0', analyzetime INT(11) NOT NULL DEFAULT '0', val text not null, PRIMARY KEY (filename, filesize, filetime))";
256
- break;
257
- case 'get_cached_dir':
258
- return "SELECT val FROM $this->table WHERE dirname = :dirname";
259
- break;
260
- }
261
- return null;
262
- }
263
-
264
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/getid3.lib.php DELETED
@@ -1,1342 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // //
8
- // getid3.lib.php - part of getID3() //
9
- // See readme.txt for more details //
10
- // ///
11
- /////////////////////////////////////////////////////////////////
12
-
13
-
14
- class getid3_lib
15
- {
16
-
17
- public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
18
- $returnstring = '';
19
- for ($i = 0; $i < strlen($string); $i++) {
20
- if ($hex) {
21
- $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
22
- } else {
23
- $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '�');
24
- }
25
- if ($spaces) {
26
- $returnstring .= ' ';
27
- }
28
- }
29
- if (!empty($htmlencoding)) {
30
- if ($htmlencoding === true) {
31
- $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
32
- }
33
- $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
34
- }
35
- return $returnstring;
36
- }
37
-
38
- public static function trunc($floatnumber) {
39
- // truncates a floating-point number at the decimal point
40
- // returns int (if possible, otherwise float)
41
- if ($floatnumber >= 1) {
42
- $truncatednumber = floor($floatnumber);
43
- } elseif ($floatnumber <= -1) {
44
- $truncatednumber = ceil($floatnumber);
45
- } else {
46
- $truncatednumber = 0;
47
- }
48
- if (self::intValueSupported($truncatednumber)) {
49
- $truncatednumber = (int) $truncatednumber;
50
- }
51
- return $truncatednumber;
52
- }
53
-
54
-
55
- public static function safe_inc(&$variable, $increment=1) {
56
- if (isset($variable)) {
57
- $variable += $increment;
58
- } else {
59
- $variable = $increment;
60
- }
61
- return true;
62
- }
63
-
64
- public static function CastAsInt($floatnum) {
65
- // convert to float if not already
66
- $floatnum = (float) $floatnum;
67
-
68
- // convert a float to type int, only if possible
69
- if (self::trunc($floatnum) == $floatnum) {
70
- // it's not floating point
71
- if (self::intValueSupported($floatnum)) {
72
- // it's within int range
73
- $floatnum = (int) $floatnum;
74
- }
75
- }
76
- return $floatnum;
77
- }
78
-
79
- public static function intValueSupported($num) {
80
- // check if integers are 64-bit
81
- static $hasINT64 = null;
82
- if ($hasINT64 === null) { // 10x faster than is_null()
83
- $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
84
- if (!$hasINT64 && !defined('PHP_INT_MIN')) {
85
- define('PHP_INT_MIN', ~PHP_INT_MAX);
86
- }
87
- }
88
- // if integers are 64-bit - no other check required
89
- if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
90
- return true;
91
- }
92
- return false;
93
- }
94
-
95
- public static function DecimalizeFraction($fraction) {
96
- list($numerator, $denominator) = explode('/', $fraction);
97
- return $numerator / ($denominator ? $denominator : 1);
98
- }
99
-
100
-
101
- public static function DecimalBinary2Float($binarynumerator) {
102
- $numerator = self::Bin2Dec($binarynumerator);
103
- $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
104
- return ($numerator / $denominator);
105
- }
106
-
107
-
108
- public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
109
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
110
- if (strpos($binarypointnumber, '.') === false) {
111
- $binarypointnumber = '0.'.$binarypointnumber;
112
- } elseif ($binarypointnumber{0} == '.') {
113
- $binarypointnumber = '0'.$binarypointnumber;
114
- }
115
- $exponent = 0;
116
- while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
117
- if (substr($binarypointnumber, 1, 1) == '.') {
118
- $exponent--;
119
- $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
120
- } else {
121
- $pointpos = strpos($binarypointnumber, '.');
122
- $exponent += ($pointpos - 1);
123
- $binarypointnumber = str_replace('.', '', $binarypointnumber);
124
- $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
125
- }
126
- }
127
- $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
128
- return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
129
- }
130
-
131
-
132
- public static function Float2BinaryDecimal($floatvalue) {
133
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
134
- $maxbits = 128; // to how many bits of precision should the calculations be taken?
135
- $intpart = self::trunc($floatvalue);
136
- $floatpart = abs($floatvalue - $intpart);
137
- $pointbitstring = '';
138
- while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
139
- $floatpart *= 2;
140
- $pointbitstring .= (string) self::trunc($floatpart);
141
- $floatpart -= self::trunc($floatpart);
142
- }
143
- $binarypointnumber = decbin($intpart).'.'.$pointbitstring;
144
- return $binarypointnumber;
145
- }
146
-
147
-
148
- public static function Float2String($floatvalue, $bits) {
149
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
150
- switch ($bits) {
151
- case 32:
152
- $exponentbits = 8;
153
- $fractionbits = 23;
154
- break;
155
-
156
- case 64:
157
- $exponentbits = 11;
158
- $fractionbits = 52;
159
- break;
160
-
161
- default:
162
- return false;
163
- break;
164
- }
165
- if ($floatvalue >= 0) {
166
- $signbit = '0';
167
- } else {
168
- $signbit = '1';
169
- }
170
- $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
171
- $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
172
- $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
173
- $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
174
-
175
- return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
176
- }
177
-
178
-
179
- public static function LittleEndian2Float($byteword) {
180
- return self::BigEndian2Float(strrev($byteword));
181
- }
182
-
183
-
184
- public static function BigEndian2Float($byteword) {
185
- // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
186
- // http://www.psc.edu/general/software/packages/ieee/ieee.html
187
- // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
188
-
189
- $bitword = self::BigEndian2Bin($byteword);
190
- if (!$bitword) {
191
- return 0;
192
- }
193
- $signbit = $bitword{0};
194
-
195
- switch (strlen($byteword) * 8) {
196
- case 32:
197
- $exponentbits = 8;
198
- $fractionbits = 23;
199
- break;
200
-
201
- case 64:
202
- $exponentbits = 11;
203
- $fractionbits = 52;
204
- break;
205
-
206
- case 80:
207
- // 80-bit Apple SANE format
208
- // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
209
- $exponentstring = substr($bitword, 1, 15);
210
- $isnormalized = intval($bitword{16});
211
- $fractionstring = substr($bitword, 17, 63);
212
- $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
213
- $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
214
- $floatvalue = $exponent * $fraction;
215
- if ($signbit == '1') {
216
- $floatvalue *= -1;
217
- }
218
- return $floatvalue;
219
- break;
220
-
221
- default:
222
- return false;
223
- break;
224
- }
225
- $exponentstring = substr($bitword, 1, $exponentbits);
226
- $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
227
- $exponent = self::Bin2Dec($exponentstring);
228
- $fraction = self::Bin2Dec($fractionstring);
229
-
230
- if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
231
- // Not a Number
232
- $floatvalue = false;
233
- } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
234
- if ($signbit == '1') {
235
- $floatvalue = '-infinity';
236
- } else {
237
- $floatvalue = '+infinity';
238
- }
239
- } elseif (($exponent == 0) && ($fraction == 0)) {
240
- if ($signbit == '1') {
241
- $floatvalue = -0;
242
- } else {
243
- $floatvalue = 0;
244
- }
245
- $floatvalue = ($signbit ? 0 : -0);
246
- } elseif (($exponent == 0) && ($fraction != 0)) {
247
- // These are 'unnormalized' values
248
- $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
249
- if ($signbit == '1') {
250
- $floatvalue *= -1;
251
- }
252
- } elseif ($exponent != 0) {
253
- $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
254
- if ($signbit == '1') {
255
- $floatvalue *= -1;
256
- }
257
- }
258
- return (float) $floatvalue;
259
- }
260
-
261
-
262
- public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
263
- $intvalue = 0;
264
- $bytewordlen = strlen($byteword);
265
- if ($bytewordlen == 0) {
266
- return false;
267
- }
268
- for ($i = 0; $i < $bytewordlen; $i++) {
269
- if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
270
- //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
271
- $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
272
- } else {
273
- $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
274
- }
275
- }
276
- if ($signed && !$synchsafe) {
277
- // synchsafe ints are not allowed to be signed
278
- if ($bytewordlen <= PHP_INT_SIZE) {
279
- $signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
280
- if ($intvalue & $signMaskBit) {
281
- $intvalue = 0 - ($intvalue & ($signMaskBit - 1));
282
- }
283
- } else {
284
- throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
285
- break;
286
- }
287
- }
288
- return self::CastAsInt($intvalue);
289
- }
290
-
291
-
292
- public static function LittleEndian2Int($byteword, $signed=false) {
293
- return self::BigEndian2Int(strrev($byteword), false, $signed);
294
- }
295
-
296
-
297
- public static function BigEndian2Bin($byteword) {
298
- $binvalue = '';
299
- $bytewordlen = strlen($byteword);
300
- for ($i = 0; $i < $bytewordlen; $i++) {
301
- $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
302
- }
303
- return $binvalue;
304
- }
305
-
306
-
307
- public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
308
- if ($number < 0) {
309
- throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
310
- }
311
- $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
312
- $intstring = '';
313
- if ($signed) {
314
- if ($minbytes > PHP_INT_SIZE) {
315
- throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
316
- }
317
- $number = $number & (0x80 << (8 * ($minbytes - 1)));
318
- }
319
- while ($number != 0) {
320
- $quotient = ($number / ($maskbyte + 1));
321
- $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
322
- $number = floor($quotient);
323
- }
324
- return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
325
- }
326
-
327
-
328
- public static function Dec2Bin($number) {
329
- while ($number >= 256) {
330
- $bytes[] = (($number / 256) - (floor($number / 256))) * 256;
331
- $number = floor($number / 256);
332
- }
333
- $bytes[] = $number;
334
- $binstring = '';
335
- for ($i = 0; $i < count($bytes); $i++) {
336
- $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
337
- }
338
- return $binstring;
339
- }
340
-
341
-
342
- public static function Bin2Dec($binstring, $signed=false) {
343
- $signmult = 1;
344
- if ($signed) {
345
- if ($binstring{0} == '1') {
346
- $signmult = -1;
347
- }
348
- $binstring = substr($binstring, 1);
349
- }
350
- $decvalue = 0;
351
- for ($i = 0; $i < strlen($binstring); $i++) {
352
- $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
353
- }
354
- return self::CastAsInt($decvalue * $signmult);
355
- }
356
-
357
-
358
- public static function Bin2String($binstring) {
359
- // return 'hi' for input of '0110100001101001'
360
- $string = '';
361
- $binstringreversed = strrev($binstring);
362
- for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
363
- $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
364
- }
365
- return $string;
366
- }
367
-
368
-
369
- public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
370
- $intstring = '';
371
- while ($number > 0) {
372
- if ($synchsafe) {
373
- $intstring = $intstring.chr($number & 127);
374
- $number >>= 7;
375
- } else {
376
- $intstring = $intstring.chr($number & 255);
377
- $number >>= 8;
378
- }
379
- }
380
- return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
381
- }
382
-
383
-
384
- public static function array_merge_clobber($array1, $array2) {
385
- // written by kc�hireability*com
386
- // taken from http://www.php.net/manual/en/function.array-merge-recursive.php
387
- if (!is_array($array1) || !is_array($array2)) {
388
- return false;
389
- }
390
- $newarray = $array1;
391
- foreach ($array2 as $key => $val) {
392
- if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
393
- $newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
394
- } else {
395
- $newarray[$key] = $val;
396
- }
397
- }
398
- return $newarray;
399
- }
400
-
401
-
402
- public static function array_merge_noclobber($array1, $array2) {
403
- if (!is_array($array1) || !is_array($array2)) {
404
- return false;
405
- }
406
- $newarray = $array1;
407
- foreach ($array2 as $key => $val) {
408
- if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
409
- $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
410
- } elseif (!isset($newarray[$key])) {
411
- $newarray[$key] = $val;
412
- }
413
- }
414
- return $newarray;
415
- }
416
-
417
-
418
- public static function ksort_recursive(&$theArray) {
419
- ksort($theArray);
420
- foreach ($theArray as $key => $value) {
421
- if (is_array($value)) {
422
- self::ksort_recursive($theArray[$key]);
423
- }
424
- }
425
- return true;
426
- }
427
-
428
- public static function fileextension($filename, $numextensions=1) {
429
- if (strstr($filename, '.')) {
430
- $reversedfilename = strrev($filename);
431
- $offset = 0;
432
- for ($i = 0; $i < $numextensions; $i++) {
433
- $offset = strpos($reversedfilename, '.', $offset + 1);
434
- if ($offset === false) {
435
- return '';
436
- }
437
- }
438
- return strrev(substr($reversedfilename, 0, $offset));
439
- }
440
- return '';
441
- }
442
-
443
-
444
- public static function PlaytimeString($seconds) {
445
- $sign = (($seconds < 0) ? '-' : '');
446
- $seconds = round(abs($seconds));
447
- $H = (int) floor( $seconds / 3600);
448
- $M = (int) floor(($seconds - (3600 * $H) ) / 60);
449
- $S = (int) round( $seconds - (3600 * $H) - (60 * $M) );
450
- return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
451
- }
452
-
453
-
454
- public static function DateMac2Unix($macdate) {
455
- // Macintosh timestamp: seconds since 00:00h January 1, 1904
456
- // UNIX timestamp: seconds since 00:00h January 1, 1970
457
- return self::CastAsInt($macdate - 2082844800);
458
- }
459
-
460
-
461
- public static function FixedPoint8_8($rawdata) {
462
- return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
463
- }
464
-
465
-
466
- public static function FixedPoint16_16($rawdata) {
467
- return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
468
- }
469
-
470
-
471
- public static function FixedPoint2_30($rawdata) {
472
- $binarystring = self::BigEndian2Bin($rawdata);
473
- return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
474
- }
475
-
476
-
477
- public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
478
- // assigns $Value to a nested array path:
479
- // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
480
- // is the same as:
481
- // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
482
- // or
483
- // $foo['path']['to']['my'] = 'file.txt';
484
- $ArrayPath = ltrim($ArrayPath, $Separator);
485
- if (($pos = strpos($ArrayPath, $Separator)) !== false) {
486
- $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
487
- } else {
488
- $ReturnedArray[$ArrayPath] = $Value;
489
- }
490
- return $ReturnedArray;
491
- }
492
-
493
- public static function array_max($arraydata, $returnkey=false) {
494
- $maxvalue = false;
495
- $maxkey = false;
496
- foreach ($arraydata as $key => $value) {
497
- if (!is_array($value)) {
498
- if ($value > $maxvalue) {
499
- $maxvalue = $value;
500
- $maxkey = $key;
501
- }
502
- }
503
- }
504
- return ($returnkey ? $maxkey : $maxvalue);
505
- }
506
-
507
- public static function array_min($arraydata, $returnkey=false) {
508
- $minvalue = false;
509
- $minkey = false;
510
- foreach ($arraydata as $key => $value) {
511
- if (!is_array($value)) {
512
- if ($value > $minvalue) {
513
- $minvalue = $value;
514
- $minkey = $key;
515
- }
516
- }
517
- }
518
- return ($returnkey ? $minkey : $minvalue);
519
- }
520
-
521
- public static function XML2array($XMLstring) {
522
- if (function_exists('simplexml_load_string')) {
523
- if (function_exists('get_object_vars')) {
524
- $XMLobject = simplexml_load_string($XMLstring);
525
- return self::SimpleXMLelement2array($XMLobject);
526
- }
527
- }
528
- return false;
529
- }
530
-
531
- public static function SimpleXMLelement2array($XMLobject) {
532
- if (!is_object($XMLobject) && !is_array($XMLobject)) {
533
- return $XMLobject;
534
- }
535
- $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject);
536
- foreach ($XMLarray as $key => $value) {
537
- $XMLarray[$key] = self::SimpleXMLelement2array($value);
538
- }
539
- return $XMLarray;
540
- }
541
-
542
-
543
- // Allan Hansen <ah�artemis*dk>
544
- // self::md5_data() - returns md5sum for a file from startuing position to absolute end position
545
- public static function hash_data($file, $offset, $end, $algorithm) {
546
- static $tempdir = '';
547
- if (!self::intValueSupported($end)) {
548
- return false;
549
- }
550
- switch ($algorithm) {
551
- case 'md5':
552
- $hash_function = 'md5_file';
553
- $unix_call = 'md5sum';
554
- $windows_call = 'md5sum.exe';
555
- $hash_length = 32;
556
- break;
557
-
558
- case 'sha1':
559
- $hash_function = 'sha1_file';
560
- $unix_call = 'sha1sum';
561
- $windows_call = 'sha1sum.exe';
562
- $hash_length = 40;
563
- break;
564
-
565
- default:
566
- throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
567
- break;
568
- }
569
- $size = $end - $offset;
570
- while (true) {
571
- if (GETID3_OS_ISWINDOWS) {
572
-
573
- // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
574
- // Fall back to create-temp-file method:
575
- if ($algorithm == 'sha1') {
576
- break;
577
- }
578
-
579
- $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
580
- foreach ($RequiredFiles as $required_file) {
581
- if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
582
- // helper apps not available - fall back to old method
583
- break 2;
584
- }
585
- }
586
- $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
587
- $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
588
- $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
589
-
590
- } else {
591
-
592
- $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
593
- $commandline .= 'tail -c'.$size.' | ';
594
- $commandline .= $unix_call;
595
-
596
- }
597
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
598
- //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
599
- break;
600
- }
601
- return substr(`$commandline`, 0, $hash_length);
602
- }
603
-
604
- if (empty($tempdir)) {
605
- // yes this is ugly, feel free to suggest a better way
606
- require_once(dirname(__FILE__).'/getid3.php');
607
- $getid3_temp = new getID3();
608
- $tempdir = $getid3_temp->tempdir;
609
- unset($getid3_temp);
610
- }
611
- // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
612
- if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
613
- // can't find anywhere to create a temp file, just fail
614
- return false;
615
- }
616
-
617
- // Init
618
- $result = false;
619
-
620
- // copy parts of file
621
- try {
622
- self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
623
- $result = $hash_function($data_filename);
624
- } catch (Exception $e) {
625
- throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
626
- }
627
- unlink($data_filename);
628
- return $result;
629
- }
630
-
631
- public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
632
- if (!self::intValueSupported($offset + $length)) {
633
- throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
634
- }
635
- if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
636
- if (($fp_dest = fopen($filename_dest, 'wb'))) {
637
- if (fseek($fp_src, $offset, SEEK_SET) == 0) {
638
- $byteslefttowrite = $length;
639
- while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
640
- $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
641
- $byteslefttowrite -= $byteswritten;
642
- }
643
- return true;
644
- } else {
645
- throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
646
- }
647
- fclose($fp_dest);
648
- } else {
649
- throw new Exception('failed to create file for writing '.$filename_dest);
650
- }
651
- fclose($fp_src);
652
- } else {
653
- throw new Exception('failed to open file for reading '.$filename_source);
654
- }
655
- return false;
656
- }
657
-
658
- public static function iconv_fallback_int_utf8($charval) {
659
- if ($charval < 128) {
660
- // 0bbbbbbb
661
- $newcharstring = chr($charval);
662
- } elseif ($charval < 2048) {
663
- // 110bbbbb 10bbbbbb
664
- $newcharstring = chr(($charval >> 6) | 0xC0);
665
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
666
- } elseif ($charval < 65536) {
667
- // 1110bbbb 10bbbbbb 10bbbbbb
668
- $newcharstring = chr(($charval >> 12) | 0xE0);
669
- $newcharstring .= chr(($charval >> 6) | 0xC0);
670
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
671
- } else {
672
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
673
- $newcharstring = chr(($charval >> 18) | 0xF0);
674
- $newcharstring .= chr(($charval >> 12) | 0xC0);
675
- $newcharstring .= chr(($charval >> 6) | 0xC0);
676
- $newcharstring .= chr(($charval & 0x3F) | 0x80);
677
- }
678
- return $newcharstring;
679
- }
680
-
681
- // ISO-8859-1 => UTF-8
682
- public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
683
- if (function_exists('utf8_encode')) {
684
- return utf8_encode($string);
685
- }
686
- // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
687
- $newcharstring = '';
688
- if ($bom) {
689
- $newcharstring .= "\xEF\xBB\xBF";
690
- }
691
- for ($i = 0; $i < strlen($string); $i++) {
692
- $charval = ord($string{$i});
693
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
694
- }
695
- return $newcharstring;
696
- }
697
-
698
- // ISO-8859-1 => UTF-16BE
699
- public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
700
- $newcharstring = '';
701
- if ($bom) {
702
- $newcharstring .= "\xFE\xFF";
703
- }
704
- for ($i = 0; $i < strlen($string); $i++) {
705
- $newcharstring .= "\x00".$string{$i};
706
- }
707
- return $newcharstring;
708
- }
709
-
710
- // ISO-8859-1 => UTF-16LE
711
- public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
712
- $newcharstring = '';
713
- if ($bom) {
714
- $newcharstring .= "\xFF\xFE";
715
- }
716
- for ($i = 0; $i < strlen($string); $i++) {
717
- $newcharstring .= $string{$i}."\x00";
718
- }
719
- return $newcharstring;
720
- }
721
-
722
- // ISO-8859-1 => UTF-16LE (BOM)
723
- public static function iconv_fallback_iso88591_utf16($string) {
724
- return self::iconv_fallback_iso88591_utf16le($string, true);
725
- }
726
-
727
- // UTF-8 => ISO-8859-1
728
- public static function iconv_fallback_utf8_iso88591($string) {
729
- if (function_exists('utf8_decode')) {
730
- return utf8_decode($string);
731
- }
732
- // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
733
- $newcharstring = '';
734
- $offset = 0;
735
- $stringlength = strlen($string);
736
- while ($offset < $stringlength) {
737
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
738
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
739
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
740
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
741
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
742
- (ord($string{($offset + 3)}) & 0x3F);
743
- $offset += 4;
744
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
745
- // 1110bbbb 10bbbbbb 10bbbbbb
746
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
747
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
748
- (ord($string{($offset + 2)}) & 0x3F);
749
- $offset += 3;
750
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
751
- // 110bbbbb 10bbbbbb
752
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
753
- (ord($string{($offset + 1)}) & 0x3F);
754
- $offset += 2;
755
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
756
- // 0bbbbbbb
757
- $charval = ord($string{$offset});
758
- $offset += 1;
759
- } else {
760
- // error? throw some kind of warning here?
761
- $charval = false;
762
- $offset += 1;
763
- }
764
- if ($charval !== false) {
765
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
766
- }
767
- }
768
- return $newcharstring;
769
- }
770
-
771
- // UTF-8 => UTF-16BE
772
- public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
773
- $newcharstring = '';
774
- if ($bom) {
775
- $newcharstring .= "\xFE\xFF";
776
- }
777
- $offset = 0;
778
- $stringlength = strlen($string);
779
- while ($offset < $stringlength) {
780
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
781
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
782
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
783
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
784
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
785
- (ord($string{($offset + 3)}) & 0x3F);
786
- $offset += 4;
787
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
788
- // 1110bbbb 10bbbbbb 10bbbbbb
789
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
790
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
791
- (ord($string{($offset + 2)}) & 0x3F);
792
- $offset += 3;
793
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
794
- // 110bbbbb 10bbbbbb
795
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
796
- (ord($string{($offset + 1)}) & 0x3F);
797
- $offset += 2;
798
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
799
- // 0bbbbbbb
800
- $charval = ord($string{$offset});
801
- $offset += 1;
802
- } else {
803
- // error? throw some kind of warning here?
804
- $charval = false;
805
- $offset += 1;
806
- }
807
- if ($charval !== false) {
808
- $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
809
- }
810
- }
811
- return $newcharstring;
812
- }
813
-
814
- // UTF-8 => UTF-16LE
815
- public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
816
- $newcharstring = '';
817
- if ($bom) {
818
- $newcharstring .= "\xFF\xFE";
819
- }
820
- $offset = 0;
821
- $stringlength = strlen($string);
822
- while ($offset < $stringlength) {
823
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
824
- // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
825
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
826
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
827
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
828
- (ord($string{($offset + 3)}) & 0x3F);
829
- $offset += 4;
830
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
831
- // 1110bbbb 10bbbbbb 10bbbbbb
832
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
833
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
834
- (ord($string{($offset + 2)}) & 0x3F);
835
- $offset += 3;
836
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
837
- // 110bbbbb 10bbbbbb
838
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
839
- (ord($string{($offset + 1)}) & 0x3F);
840
- $offset += 2;
841
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
842
- // 0bbbbbbb
843
- $charval = ord($string{$offset});
844
- $offset += 1;
845
- } else {
846
- // error? maybe throw some warning here?
847
- $charval = false;
848
- $offset += 1;
849
- }
850
- if ($charval !== false) {
851
- $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
852
- }
853
- }
854
- return $newcharstring;
855
- }
856
-
857
- // UTF-8 => UTF-16LE (BOM)
858
- public static function iconv_fallback_utf8_utf16($string) {
859
- return self::iconv_fallback_utf8_utf16le($string, true);
860
- }
861
-
862
- // UTF-16BE => UTF-8
863
- public static function iconv_fallback_utf16be_utf8($string) {
864
- if (substr($string, 0, 2) == "\xFE\xFF") {
865
- // strip BOM
866
- $string = substr($string, 2);
867
- }
868
- $newcharstring = '';
869
- for ($i = 0; $i < strlen($string); $i += 2) {
870
- $charval = self::BigEndian2Int(substr($string, $i, 2));
871
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
872
- }
873
- return $newcharstring;
874
- }
875
-
876
- // UTF-16LE => UTF-8
877
- public static function iconv_fallback_utf16le_utf8($string) {
878
- if (substr($string, 0, 2) == "\xFF\xFE") {
879
- // strip BOM
880
- $string = substr($string, 2);
881
- }
882
- $newcharstring = '';
883
- for ($i = 0; $i < strlen($string); $i += 2) {
884
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
885
- $newcharstring .= self::iconv_fallback_int_utf8($charval);
886
- }
887
- return $newcharstring;
888
- }
889
-
890
- // UTF-16BE => ISO-8859-1
891
- public static function iconv_fallback_utf16be_iso88591($string) {
892
- if (substr($string, 0, 2) == "\xFE\xFF") {
893
- // strip BOM
894
- $string = substr($string, 2);
895
- }
896
- $newcharstring = '';
897
- for ($i = 0; $i < strlen($string); $i += 2) {
898
- $charval = self::BigEndian2Int(substr($string, $i, 2));
899
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
900
- }
901
- return $newcharstring;
902
- }
903
-
904
- // UTF-16LE => ISO-8859-1
905
- public static function iconv_fallback_utf16le_iso88591($string) {
906
- if (substr($string, 0, 2) == "\xFF\xFE") {
907
- // strip BOM
908
- $string = substr($string, 2);
909
- }
910
- $newcharstring = '';
911
- for ($i = 0; $i < strlen($string); $i += 2) {
912
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
913
- $newcharstring .= (($charval < 256) ? chr($charval) : '?');
914
- }
915
- return $newcharstring;
916
- }
917
-
918
- // UTF-16 (BOM) => ISO-8859-1
919
- public static function iconv_fallback_utf16_iso88591($string) {
920
- $bom = substr($string, 0, 2);
921
- if ($bom == "\xFE\xFF") {
922
- return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
923
- } elseif ($bom == "\xFF\xFE") {
924
- return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
925
- }
926
- return $string;
927
- }
928
-
929
- // UTF-16 (BOM) => UTF-8
930
- public static function iconv_fallback_utf16_utf8($string) {
931
- $bom = substr($string, 0, 2);
932
- if ($bom == "\xFE\xFF") {
933
- return self::iconv_fallback_utf16be_utf8(substr($string, 2));
934
- } elseif ($bom == "\xFF\xFE") {
935
- return self::iconv_fallback_utf16le_utf8(substr($string, 2));
936
- }
937
- return $string;
938
- }
939
-
940
- public static function iconv_fallback($in_charset, $out_charset, $string) {
941
-
942
- if ($in_charset == $out_charset) {
943
- return $string;
944
- }
945
-
946
- // iconv() availble
947
- if (function_exists('iconv')) {
948
- if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
949
- switch ($out_charset) {
950
- case 'ISO-8859-1':
951
- $converted_string = rtrim($converted_string, "\x00");
952
- break;
953
- }
954
- return $converted_string;
955
- }
956
-
957
- // iconv() may sometimes fail with "illegal character in input string" error message
958
- // and return an empty string, but returning the unconverted string is more useful
959
- return $string;
960
- }
961
-
962
-
963
- // iconv() not available
964
- static $ConversionFunctionList = array();
965
- if (empty($ConversionFunctionList)) {
966
- $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8';
967
- $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16';
968
- $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
969
- $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
970
- $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591';
971
- $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16';
972
- $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be';
973
- $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le';
974
- $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591';
975
- $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8';
976
- $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
977
- $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8';
978
- $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
979
- $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8';
980
- }
981
- if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
982
- $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
983
- return self::$ConversionFunction($string);
984
- }
985
- throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
986
- }
987
-
988
-
989
- public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
990
- $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
991
- $HTMLstring = '';
992
-
993
- switch ($charset) {
994
- case '1251':
995
- case '1252':
996
- case '866':
997
- case '932':
998
- case '936':
999
- case '950':
1000
- case 'BIG5':
1001
- case 'BIG5-HKSCS':
1002
- case 'cp1251':
1003
- case 'cp1252':
1004
- case 'cp866':
1005
- case 'EUC-JP':
1006
- case 'EUCJP':
1007
- case 'GB2312':
1008
- case 'ibm866':
1009
- case 'ISO-8859-1':
1010
- case 'ISO-8859-15':
1011
- case 'ISO8859-1':
1012
- case 'ISO8859-15':
1013
- case 'KOI8-R':
1014
- case 'koi8-ru':
1015
- case 'koi8r':
1016
- case 'Shift_JIS':
1017
- case 'SJIS':
1018
- case 'win-1251':
1019
- case 'Windows-1251':
1020
- case 'Windows-1252':
1021
- $HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
1022
- break;
1023
-
1024
- case 'UTF-8':
1025
- $strlen = strlen($string);
1026
- for ($i = 0; $i < $strlen; $i++) {
1027
- $char_ord_val = ord($string{$i});
1028
- $charval = 0;
1029
- if ($char_ord_val < 0x80) {
1030
- $charval = $char_ord_val;
1031
- } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
1032
- $charval = (($char_ord_val & 0x07) << 18);
1033
- $charval += ((ord($string{++$i}) & 0x3F) << 12);
1034
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
1035
- $charval += (ord($string{++$i}) & 0x3F);
1036
- } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
1037
- $charval = (($char_ord_val & 0x0F) << 12);
1038
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
1039
- $charval += (ord($string{++$i}) & 0x3F);
1040
- } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
1041
- $charval = (($char_ord_val & 0x1F) << 6);
1042
- $charval += (ord($string{++$i}) & 0x3F);
1043
- }
1044
- if (($charval >= 32) && ($charval <= 127)) {
1045
- $HTMLstring .= htmlentities(chr($charval));
1046
- } else {
1047
- $HTMLstring .= '&#'.$charval.';';
1048
- }
1049
- }
1050
- break;
1051
-
1052
- case 'UTF-16LE':
1053
- for ($i = 0; $i < strlen($string); $i += 2) {
1054
- $charval = self::LittleEndian2Int(substr($string, $i, 2));
1055
- if (($charval >= 32) && ($charval <= 127)) {
1056
- $HTMLstring .= chr($charval);
1057
- } else {
1058
- $HTMLstring .= '&#'.$charval.';';
1059
- }
1060
- }
1061
- break;
1062
-
1063
- case 'UTF-16BE':
1064
- for ($i = 0; $i < strlen($string); $i += 2) {
1065
- $charval = self::BigEndian2Int(substr($string, $i, 2));
1066
- if (($charval >= 32) && ($charval <= 127)) {
1067
- $HTMLstring .= chr($charval);
1068
- } else {
1069
- $HTMLstring .= '&#'.$charval.';';
1070
- }
1071
- }
1072
- break;
1073
-
1074
- default:
1075
- $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
1076
- break;
1077
- }
1078
- return $HTMLstring;
1079
- }
1080
-
1081
-
1082
-
1083
- public static function RGADnameLookup($namecode) {
1084
- static $RGADname = array();
1085
- if (empty($RGADname)) {
1086
- $RGADname[0] = 'not set';
1087
- $RGADname[1] = 'Track Gain Adjustment';
1088
- $RGADname[2] = 'Album Gain Adjustment';
1089
- }
1090
-
1091
- return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
1092
- }
1093
-
1094
-
1095
- public static function RGADoriginatorLookup($originatorcode) {
1096
- static $RGADoriginator = array();
1097
- if (empty($RGADoriginator)) {
1098
- $RGADoriginator[0] = 'unspecified';
1099
- $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
1100
- $RGADoriginator[2] = 'set by user';
1101
- $RGADoriginator[3] = 'determined automatically';
1102
- }
1103
-
1104
- return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
1105
- }
1106
-
1107
-
1108
- public static function RGADadjustmentLookup($rawadjustment, $signbit) {
1109
- $adjustment = $rawadjustment / 10;
1110
- if ($signbit == 1) {
1111
- $adjustment *= -1;
1112
- }
1113
- return (float) $adjustment;
1114
- }
1115
-
1116
-
1117
- public static function RGADgainString($namecode, $originatorcode, $replaygain) {
1118
- if ($replaygain < 0) {
1119
- $signbit = '1';
1120
- } else {
1121
- $signbit = '0';
1122
- }
1123
- $storedreplaygain = intval(round($replaygain * 10));
1124
- $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
1125
- $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
1126
- $gainstring .= $signbit;
1127
- $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
1128
-
1129
- return $gainstring;
1130
- }
1131
-
1132
- public static function RGADamplitude2dB($amplitude) {
1133
- return 20 * log10($amplitude);
1134
- }
1135
-
1136
-
1137
- public static function GetDataImageSize($imgData, &$imageinfo=array()) {
1138
- static $tempdir = '';
1139
- if (empty($tempdir)) {
1140
- // yes this is ugly, feel free to suggest a better way
1141
- require_once(dirname(__FILE__).'/getid3.php');
1142
- $getid3_temp = new getID3();
1143
- $tempdir = $getid3_temp->tempdir;
1144
- unset($getid3_temp);
1145
- }
1146
- $GetDataImageSize = false;
1147
- if ($tempfilename = tempnam($tempdir, 'gI3')) {
1148
- if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
1149
- fwrite($tmp, $imgData);
1150
- fclose($tmp);
1151
- $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
1152
- }
1153
- unlink($tempfilename);
1154
- }
1155
- return $GetDataImageSize;
1156
- }
1157
-
1158
- public static function ImageExtFromMime($mime_type) {
1159
- // temporary way, works OK for now, but should be reworked in the future
1160
- return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
1161
- }
1162
-
1163
- public static function ImageTypesLookup($imagetypeid) {
1164
- static $ImageTypesLookup = array();
1165
- if (empty($ImageTypesLookup)) {
1166
- $ImageTypesLookup[1] = 'gif';
1167
- $ImageTypesLookup[2] = 'jpeg';
1168
- $ImageTypesLookup[3] = 'png';
1169
- $ImageTypesLookup[4] = 'swf';
1170
- $ImageTypesLookup[5] = 'psd';
1171
- $ImageTypesLookup[6] = 'bmp';
1172
- $ImageTypesLookup[7] = 'tiff (little-endian)';
1173
- $ImageTypesLookup[8] = 'tiff (big-endian)';
1174
- $ImageTypesLookup[9] = 'jpc';
1175
- $ImageTypesLookup[10] = 'jp2';
1176
- $ImageTypesLookup[11] = 'jpx';
1177
- $ImageTypesLookup[12] = 'jb2';
1178
- $ImageTypesLookup[13] = 'swc';
1179
- $ImageTypesLookup[14] = 'iff';
1180
- }
1181
- return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
1182
- }
1183
-
1184
- public static function CopyTagsToComments(&$ThisFileInfo) {
1185
-
1186
- // Copy all entries from ['tags'] into common ['comments']
1187
- if (!empty($ThisFileInfo['tags'])) {
1188
- foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
1189
- foreach ($tagarray as $tagname => $tagdata) {
1190
- foreach ($tagdata as $key => $value) {
1191
- if (!empty($value)) {
1192
- if (empty($ThisFileInfo['comments'][$tagname])) {
1193
-
1194
- // fall through and append value
1195
-
1196
- } elseif ($tagtype == 'id3v1') {
1197
-
1198
- $newvaluelength = strlen(trim($value));
1199
- foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1200
- $oldvaluelength = strlen(trim($existingvalue));
1201
- if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
1202
- // new value is identical but shorter-than (or equal-length to) one already in comments - skip
1203
- break 2;
1204
- }
1205
- }
1206
-
1207
- } elseif (!is_array($value)) {
1208
-
1209
- $newvaluelength = strlen(trim($value));
1210
- foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1211
- $oldvaluelength = strlen(trim($existingvalue));
1212
- if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
1213
- $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
1214
- break 2;
1215
- }
1216
- }
1217
-
1218
- }
1219
- if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
1220
- $value = (is_string($value) ? trim($value) : $value);
1221
- $ThisFileInfo['comments'][$tagname][] = $value;
1222
- }
1223
- }
1224
- }
1225
- }
1226
- }
1227
-
1228
- // Copy to ['comments_html']
1229
- foreach ($ThisFileInfo['comments'] as $field => $values) {
1230
- if ($field == 'picture') {
1231
- // pictures can take up a lot of space, and we don't need multiple copies of them
1232
- // let there be a single copy in [comments][picture], and not elsewhere
1233
- continue;
1234
- }
1235
- foreach ($values as $index => $value) {
1236
- if (is_array($value)) {
1237
- $ThisFileInfo['comments_html'][$field][$index] = $value;
1238
- } else {
1239
- $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
1240
- }
1241
- }
1242
- }
1243
- }
1244
- return true;
1245
- }
1246
-
1247
-
1248
- public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
1249
-
1250
- // Cached
1251
- static $cache;
1252
- if (isset($cache[$file][$name])) {
1253
- return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1254
- }
1255
-
1256
- // Init
1257
- $keylength = strlen($key);
1258
- $line_count = $end - $begin - 7;
1259
-
1260
- // Open php file
1261
- $fp = fopen($file, 'r');
1262
-
1263
- // Discard $begin lines
1264
- for ($i = 0; $i < ($begin + 3); $i++) {
1265
- fgets($fp, 1024);
1266
- }
1267
-
1268
- // Loop thru line
1269
- while (0 < $line_count--) {
1270
-
1271
- // Read line
1272
- $line = ltrim(fgets($fp, 1024), "\t ");
1273
-
1274
- // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
1275
- //$keycheck = substr($line, 0, $keylength);
1276
- //if ($key == $keycheck) {
1277
- // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
1278
- // break;
1279
- //}
1280
-
1281
- // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
1282
- //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
1283
- $explodedLine = explode("\t", $line, 2);
1284
- $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : '');
1285
- $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
1286
- $cache[$file][$name][$ThisKey] = trim($ThisValue);
1287
- }
1288
-
1289
- // Close and return
1290
- fclose($fp);
1291
- return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1292
- }
1293
-
1294
- public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
1295
- global $GETID3_ERRORARRAY;
1296
-
1297
- if (file_exists($filename)) {
1298
- if (include_once($filename)) {
1299
- return true;
1300
- } else {
1301
- $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
1302
- }
1303
- } else {
1304
- $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
1305
- }
1306
- if ($DieOnFailure) {
1307
- throw new Exception($diemessage);
1308
- } else {
1309
- $GETID3_ERRORARRAY[] = $diemessage;
1310
- }
1311
- return false;
1312
- }
1313
-
1314
- public static function trimNullByte($string) {
1315
- return trim($string, "\x00");
1316
- }
1317
-
1318
- public static function getFileSizeSyscall($path) {
1319
- $filesize = false;
1320
-
1321
- if (GETID3_OS_ISWINDOWS) {
1322
- if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
1323
- $filesystem = new COM('Scripting.FileSystemObject');
1324
- $file = $filesystem->GetFile($path);
1325
- $filesize = $file->Size();
1326
- unset($filesystem, $file);
1327
- } else {
1328
- $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
1329
- }
1330
- } else {
1331
- $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
1332
- }
1333
- if (isset($commandline)) {
1334
- $output = trim(`$commandline`);
1335
- if (ctype_digit($output)) {
1336
- $filesize = (float) $output;
1337
- }
1338
- }
1339
- return $filesize;
1340
- }
1341
-
1342
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/getid3.php DELETED
@@ -1,1776 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // //
8
- // Please see readme.txt for more information //
9
- // ///
10
- /////////////////////////////////////////////////////////////////
11
-
12
- // define a constant rather than looking up every time it is needed
13
- if (!defined('GETID3_OS_ISWINDOWS')) {
14
- define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0));
15
- }
16
- // Get base path of getID3() - ONCE
17
- if (!defined('GETID3_INCLUDEPATH')) {
18
- define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
19
- }
20
-
21
- // attempt to define temp dir as something flexible but reliable
22
- $temp_dir = ini_get('upload_tmp_dir');
23
- if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
24
- $temp_dir = '';
25
- }
26
- if (!$temp_dir && function_exists('sys_get_temp_dir')) {
27
- // PHP v5.2.1+
28
- // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
29
- $temp_dir = sys_get_temp_dir();
30
- }
31
- $temp_dir = realpath($temp_dir);
32
- $open_basedir = ini_get('open_basedir');
33
- if ($open_basedir) {
34
- // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/"
35
- $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir);
36
- $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir);
37
- if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) {
38
- $temp_dir .= DIRECTORY_SEPARATOR;
39
- }
40
- $found_valid_tempdir = false;
41
- $open_basedirs = explode(PATH_SEPARATOR, $open_basedir);
42
- foreach ($open_basedirs as $basedir) {
43
- if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) {
44
- $basedir .= DIRECTORY_SEPARATOR;
45
- }
46
- if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) {
47
- $found_valid_tempdir = true;
48
- break;
49
- }
50
- }
51
- if (!$found_valid_tempdir) {
52
- $temp_dir = '';
53
- }
54
- unset($open_basedirs, $found_valid_tempdir, $basedir);
55
- }
56
- if (!$temp_dir) {
57
- $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir
58
- }
59
- // $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system
60
- define('GETID3_TEMP_DIR', $temp_dir);
61
- unset($open_basedir, $temp_dir);
62
-
63
- // End: Defines
64
-
65
-
66
- class getID3
67
- {
68
- // public: Settings
69
- public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE
70
- public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252'
71
-
72
- // public: Optional tag checks - disable for speed.
73
- public $option_tag_id3v1 = true; // Read and process ID3v1 tags
74
- public $option_tag_id3v2 = true; // Read and process ID3v2 tags
75
- public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags
76
- public $option_tag_apetag = true; // Read and process APE tags
77
- public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding
78
- public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
79
-
80
- // public: Optional tag/comment calucations
81
- public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc
82
-
83
- // public: Optional handling of embedded attachments (e.g. images)
84
- public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility
85
-
86
- // public: Optional calculations
87
- public $option_md5_data = false; // Get MD5 sum of data part - slow
88
- public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
89
- public $option_sha1_data = false; // Get SHA1 sum of data part - slow
90
- public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX)
91
-
92
- // public: Read buffer size in bytes
93
- public $option_fread_buffer_size = 32768;
94
-
95
- // Public variables
96
- public $filename; // Filename of file being analysed.
97
- public $fp; // Filepointer to file being analysed.
98
- public $info; // Result array.
99
- public $tempdir = GETID3_TEMP_DIR;
100
-
101
- // Protected variables
102
- protected $startup_error = '';
103
- protected $startup_warning = '';
104
- protected $memory_limit = 0;
105
-
106
- const VERSION = '1.9.5-20130220';
107
- const FREAD_BUFFER_SIZE = 32768;
108
-
109
- const ATTACHMENTS_NONE = false;
110
- const ATTACHMENTS_INLINE = true;
111
-
112
- // public: constructor
113
- public function __construct() {
114
-
115
- // Check for PHP version
116
- $required_php_version = '5.0.5';
117
- if (version_compare(PHP_VERSION, $required_php_version, '<')) {
118
- $this->startup_error .= 'getID3() requires PHP v'.$required_php_version.' or higher - you are running v'.PHP_VERSION;
119
- return false;
120
- }
121
-
122
- // Check memory
123
- $this->memory_limit = ini_get('memory_limit');
124
- if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) {
125
- // could be stored as "16M" rather than 16777216 for example
126
- $this->memory_limit = $matches[1] * 1048576;
127
- } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
128
- // could be stored as "2G" rather than 2147483648 for example
129
- $this->memory_limit = $matches[1] * 1073741824;
130
- }
131
- if ($this->memory_limit <= 0) {
132
- // memory limits probably disabled
133
- } elseif ($this->memory_limit <= 4194304) {
134
- $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini';
135
- } elseif ($this->memory_limit <= 12582912) {
136
- $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
137
- }
138
-
139
- // Check safe_mode off
140
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
141
- $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
142
- }
143
-
144
- if (intval(ini_get('mbstring.func_overload')) > 0) {
145
- $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.');
146
- }
147
-
148
- // Check for magic_quotes_runtime
149
- if (function_exists('get_magic_quotes_runtime')) {
150
- if (get_magic_quotes_runtime()) {
151
- return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
152
- }
153
- }
154
-
155
- // Check for magic_quotes_gpc
156
- if (function_exists('magic_quotes_gpc')) {
157
- if (get_magic_quotes_gpc()) {
158
- return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).');
159
- }
160
- }
161
-
162
- // Load support library
163
- if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
164
- $this->startup_error .= 'getid3.lib.php is missing or corrupt';
165
- }
166
-
167
- if ($this->option_max_2gb_check === null) {
168
- $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647);
169
- }
170
-
171
-
172
- // Needed for Windows only:
173
- // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
174
- // as well as other helper functions such as head, tail, md5sum, etc
175
- // This path cannot contain spaces, but the below code will attempt to get the
176
- // 8.3-equivalent path automatically
177
- // IMPORTANT: This path must include the trailing slash
178
- if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
179
-
180
- $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
181
-
182
- if (!is_dir($helperappsdir)) {
183
- $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
184
- } elseif (strpos(realpath($helperappsdir), ' ') !== false) {
185
- $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
186
- $path_so_far = array();
187
- foreach ($DirPieces as $key => $value) {
188
- if (strpos($value, ' ') !== false) {
189
- if (!empty($path_so_far)) {
190
- $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far));
191
- $dir_listing = `$commandline`;
192
- $lines = explode("\n", $dir_listing);
193
- foreach ($lines as $line) {
194
- $line = trim($line);
195
- if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(<DIR>|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) {
196
- list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches;
197
- if ((strtoupper($filesize) == '<DIR>') && (strtolower($filename) == strtolower($value))) {
198
- $value = $shortname;
199
- }
200
- }
201
- }
202
- } else {
203
- $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.';
204
- }
205
- }
206
- $path_so_far[] = $value;
207
- }
208
- $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far);
209
- }
210
- define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR);
211
- }
212
-
213
- return true;
214
- }
215
-
216
- public function version() {
217
- return self::VERSION;
218
- }
219
-
220
- public function fread_buffer_size() {
221
- return $this->option_fread_buffer_size;
222
- }
223
-
224
-
225
- // public: setOption
226
- public function setOption($optArray) {
227
- if (!is_array($optArray) || empty($optArray)) {
228
- return false;
229
- }
230
- foreach ($optArray as $opt => $val) {
231
- if (isset($this->$opt) === false) {
232
- continue;
233
- }
234
- $this->$opt = $val;
235
- }
236
- return true;
237
- }
238
-
239
-
240
- public function openfile($filename) {
241
- try {
242
- if (!empty($this->startup_error)) {
243
- throw new getid3_exception($this->startup_error);
244
- }
245
- if (!empty($this->startup_warning)) {
246
- $this->warning($this->startup_warning);
247
- }
248
-
249
- // init result array and set parameters
250
- $this->filename = $filename;
251
- $this->info = array();
252
- $this->info['GETID3_VERSION'] = $this->version();
253
- $this->info['php_memory_limit'] = $this->memory_limit;
254
-
255
- // remote files not supported
256
- if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
257
- throw new getid3_exception('Remote files are not supported - please copy the file locally first');
258
- }
259
-
260
- $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
261
- $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename);
262
-
263
- // open local file
264
- if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
265
- // great
266
- } else {
267
- throw new getid3_exception('Could not open "'.$filename.'" (does not exist, or is not a file)');
268
- }
269
-
270
- $this->info['filesize'] = filesize($filename);
271
- // set redundant parameters - might be needed in some include file
272
- $this->info['filename'] = basename($filename);
273
- $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename)));
274
- $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename'];
275
-
276
-
277
- // option_max_2gb_check
278
- if ($this->option_max_2gb_check) {
279
- // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB)
280
- // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
281
- // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
282
- $fseek = fseek($this->fp, 0, SEEK_END);
283
- if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
284
- ($this->info['filesize'] < 0) ||
285
- (ftell($this->fp) < 0)) {
286
- $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']);
287
-
288
- if ($real_filesize === false) {
289
- unset($this->info['filesize']);
290
- fclose($this->fp);
291
- throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.');
292
- } elseif (getid3_lib::intValueSupported($real_filesize)) {
293
- unset($this->info['filesize']);
294
- fclose($this->fp);
295
- throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
296
- }
297
- $this->info['filesize'] = $real_filesize;
298
- $this->error('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
299
- }
300
- }
301
-
302
- // set more parameters
303
- $this->info['avdataoffset'] = 0;
304
- $this->info['avdataend'] = $this->info['filesize'];
305
- $this->info['fileformat'] = ''; // filled in later
306
- $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used
307
- $this->info['video']['dataformat'] = ''; // filled in later, unset if not used
308
- $this->info['tags'] = array(); // filled in later, unset if not used
309
- $this->info['error'] = array(); // filled in later, unset if not used
310
- $this->info['warning'] = array(); // filled in later, unset if not used
311
- $this->info['comments'] = array(); // filled in later, unset if not used
312
- $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired
313
-
314
- return true;
315
-
316
- } catch (Exception $e) {
317
- $this->error($e->getMessage());
318
- }
319
- return false;
320
- }
321
-
322
- // public: analyze file
323
- public function analyze($filename) {
324
- try {
325
- if (!$this->openfile($filename)) {
326
- return $this->info;
327
- }
328
-
329
- // Handle tags
330
- foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
331
- $option_tag = 'option_tag_'.$tag_name;
332
- if ($this->$option_tag) {
333
- $this->include_module('tag.'.$tag_name);
334
- try {
335
- $tag_class = 'getid3_'.$tag_name;
336
- $tag = new $tag_class($this);
337
- $tag->Analyze();
338
- }
339
- catch (getid3_exception $e) {
340
- throw $e;
341
- }
342
- }
343
- }
344
- if (isset($this->info['id3v2']['tag_offset_start'])) {
345
- $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
346
- }
347
- foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) {
348
- if (isset($this->info[$tag_key]['tag_offset_start'])) {
349
- $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
350
- }
351
- }
352
-
353
- // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
354
- if (!$this->option_tag_id3v2) {
355
- fseek($this->fp, 0, SEEK_SET);
356
- $header = fread($this->fp, 10);
357
- if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
358
- $this->info['id3v2']['header'] = true;
359
- $this->info['id3v2']['majorversion'] = ord($header{3});
360
- $this->info['id3v2']['minorversion'] = ord($header{4});
361
- $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
362
- }
363
- }
364
-
365
- // read 32 kb file data
366
- fseek($this->fp, $this->info['avdataoffset'], SEEK_SET);
367
- $formattest = fread($this->fp, 32774);
368
-
369
- // determine format
370
- $determined_format = $this->GetFileFormat($formattest, $filename);
371
-
372
- // unable to determine file format
373
- if (!$determined_format) {
374
- fclose($this->fp);
375
- return $this->error('unable to determine file format');
376
- }
377
-
378
- // check for illegal ID3 tags
379
- if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
380
- if ($determined_format['fail_id3'] === 'ERROR') {
381
- fclose($this->fp);
382
- return $this->error('ID3 tags not allowed on this file type.');
383
- } elseif ($determined_format['fail_id3'] === 'WARNING') {
384
- $this->warning('ID3 tags not allowed on this file type.');
385
- }
386
- }
387
-
388
- // check for illegal APE tags
389
- if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
390
- if ($determined_format['fail_ape'] === 'ERROR') {
391
- fclose($this->fp);
392
- return $this->error('APE tags not allowed on this file type.');
393
- } elseif ($determined_format['fail_ape'] === 'WARNING') {
394
- $this->warning('APE tags not allowed on this file type.');
395
- }
396
- }
397
-
398
- // set mime type
399
- $this->info['mime_type'] = $determined_format['mime_type'];
400
-
401
- // supported format signature pattern detected, but module deleted
402
- if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
403
- fclose($this->fp);
404
- return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
405
- }
406
-
407
- // module requires iconv support
408
- // Check encoding/iconv support
409
- if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
410
- $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
411
- if (GETID3_OS_ISWINDOWS) {
412
- $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
413
- } else {
414
- $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
415
- }
416
- return $this->error($errormessage);
417
- }
418
-
419
- // include module
420
- include_once(GETID3_INCLUDEPATH.$determined_format['include']);
421
-
422
- // instantiate module class
423
- $class_name = 'getid3_'.$determined_format['module'];
424
- if (!class_exists($class_name)) {
425
- return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
426
- }
427
- $class = new $class_name($this);
428
- $class->Analyze();
429
- unset($class);
430
-
431
- // close file
432
- fclose($this->fp);
433
-
434
- // process all tags - copy to 'tags' and convert charsets
435
- if ($this->option_tags_process) {
436
- $this->HandleAllTags();
437
- }
438
-
439
- // perform more calculations
440
- if ($this->option_extra_info) {
441
- $this->ChannelsBitratePlaytimeCalculations();
442
- $this->CalculateCompressionRatioVideo();
443
- $this->CalculateCompressionRatioAudio();
444
- $this->CalculateReplayGain();
445
- $this->ProcessAudioStreams();
446
- }
447
-
448
- // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
449
- if ($this->option_md5_data) {
450
- // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
451
- if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
452
- $this->getHashdata('md5');
453
- }
454
- }
455
-
456
- // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
457
- if ($this->option_sha1_data) {
458
- $this->getHashdata('sha1');
459
- }
460
-
461
- // remove undesired keys
462
- $this->CleanUp();
463
-
464
- } catch (Exception $e) {
465
- $this->error('Caught exception: '.$e->getMessage());
466
- }
467
-
468
- // return info array
469
- return $this->info;
470
- }
471
-
472
-
473
- // private: error handling
474
- public function error($message) {
475
- $this->CleanUp();
476
- if (!isset($this->info['error'])) {
477
- $this->info['error'] = array();
478
- }
479
- $this->info['error'][] = $message;
480
- return $this->info;
481
- }
482
-
483
-
484
- // private: warning handling
485
- public function warning($message) {
486
- $this->info['warning'][] = $message;
487
- return true;
488
- }
489
-
490
-
491
- // private: CleanUp
492
- private function CleanUp() {
493
-
494
- // remove possible empty keys
495
- $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
496
- foreach ($AVpossibleEmptyKeys as $dummy => $key) {
497
- if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
498
- unset($this->info['audio'][$key]);
499
- }
500
- if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
501
- unset($this->info['video'][$key]);
502
- }
503
- }
504
-
505
- // remove empty root keys
506
- if (!empty($this->info)) {
507
- foreach ($this->info as $key => $value) {
508
- if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
509
- unset($this->info[$key]);
510
- }
511
- }
512
- }
513
-
514
- // remove meaningless entries from unknown-format files
515
- if (empty($this->info['fileformat'])) {
516
- if (isset($this->info['avdataoffset'])) {
517
- unset($this->info['avdataoffset']);
518
- }
519
- if (isset($this->info['avdataend'])) {
520
- unset($this->info['avdataend']);
521
- }
522
- }
523
-
524
- // remove possible duplicated identical entries
525
- if (!empty($this->info['error'])) {
526
- $this->info['error'] = array_values(array_unique($this->info['error']));
527
- }
528
- if (!empty($this->info['warning'])) {
529
- $this->info['warning'] = array_values(array_unique($this->info['warning']));
530
- }
531
-
532
- // remove "global variable" type keys
533
- unset($this->info['php_memory_limit']);
534
-
535
- return true;
536
- }
537
-
538
-
539
- // return array containing information about all supported formats
540
- public function GetFileFormatArray() {
541
- static $format_info = array();
542
- if (empty($format_info)) {
543
- $format_info = array(
544
-
545
- // Audio formats
546
-
547
- // AC-3 - audio - Dolby AC-3 / Dolby Digital
548
- 'ac3' => array(
549
- 'pattern' => '^\x0B\x77',
550
- 'group' => 'audio',
551
- 'module' => 'ac3',
552
- 'mime_type' => 'audio/ac3',
553
- ),
554
-
555
- // AAC - audio - Advanced Audio Coding (AAC) - ADIF format
556
- 'adif' => array(
557
- 'pattern' => '^ADIF',
558
- 'group' => 'audio',
559
- 'module' => 'aac',
560
- 'mime_type' => 'application/octet-stream',
561
- 'fail_ape' => 'WARNING',
562
- ),
563
-
564
- /*
565
- // AA - audio - Audible Audiobook
566
- 'aa' => array(
567
- 'pattern' => '^.{4}\x57\x90\x75\x36',
568
- 'group' => 'audio',
569
- 'module' => 'aa',
570
- 'mime_type' => 'audio/audible',
571
- ),
572
- */
573
- // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
574
- 'adts' => array(
575
- 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]',
576
- 'group' => 'audio',
577
- 'module' => 'aac',
578
- 'mime_type' => 'application/octet-stream',
579
- 'fail_ape' => 'WARNING',
580
- ),
581
-
582
-
583
- // AU - audio - NeXT/Sun AUdio (AU)
584
- 'au' => array(
585
- 'pattern' => '^\.snd',
586
- 'group' => 'audio',
587
- 'module' => 'au',
588
- 'mime_type' => 'audio/basic',
589
- ),
590
-
591
- // AVR - audio - Audio Visual Research
592
- 'avr' => array(
593
- 'pattern' => '^2BIT',
594
- 'group' => 'audio',
595
- 'module' => 'avr',
596
- 'mime_type' => 'application/octet-stream',
597
- ),
598
-
599
- // BONK - audio - Bonk v0.9+
600
- 'bonk' => array(
601
- 'pattern' => '^\x00(BONK|INFO|META| ID3)',
602
- 'group' => 'audio',
603
- 'module' => 'bonk',
604
- 'mime_type' => 'audio/xmms-bonk',
605
- ),
606
-
607
- // DSS - audio - Digital Speech Standard
608
- 'dss' => array(
609
- 'pattern' => '^[\x02-\x03]ds[s2]',
610
- 'group' => 'audio',
611
- 'module' => 'dss',
612
- 'mime_type' => 'application/octet-stream',
613
- ),
614
-
615
- // DTS - audio - Dolby Theatre System
616
- 'dts' => array(
617
- 'pattern' => '^\x7F\xFE\x80\x01',
618
- 'group' => 'audio',
619
- 'module' => 'dts',
620
- 'mime_type' => 'audio/dts',
621
- ),
622
-
623
- // FLAC - audio - Free Lossless Audio Codec
624
- 'flac' => array(
625
- 'pattern' => '^fLaC',
626
- 'group' => 'audio',
627
- 'module' => 'flac',
628
- 'mime_type' => 'audio/x-flac',
629
- ),
630
-
631
- // LA - audio - Lossless Audio (LA)
632
- 'la' => array(
633
- 'pattern' => '^LA0[2-4]',
634
- 'group' => 'audio',
635
- 'module' => 'la',
636
- 'mime_type' => 'application/octet-stream',
637
- ),
638
-
639
- // LPAC - audio - Lossless Predictive Audio Compression (LPAC)
640
- 'lpac' => array(
641
- 'pattern' => '^LPAC',
642
- 'group' => 'audio',
643
- 'module' => 'lpac',
644
- 'mime_type' => 'application/octet-stream',
645
- ),
646
-
647
- // MIDI - audio - MIDI (Musical Instrument Digital Interface)
648
- 'midi' => array(
649
- 'pattern' => '^MThd',
650
- 'group' => 'audio',
651
- 'module' => 'midi',
652
- 'mime_type' => 'audio/midi',
653
- ),
654
-
655
- // MAC - audio - Monkey's Audio Compressor
656
- 'mac' => array(
657
- 'pattern' => '^MAC ',
658
- 'group' => 'audio',
659
- 'module' => 'monkey',
660
- 'mime_type' => 'application/octet-stream',
661
- ),
662
-
663
- // has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
664
- // // MOD - audio - MODule (assorted sub-formats)
665
- // 'mod' => array(
666
- // 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
667
- // 'group' => 'audio',
668
- // 'module' => 'mod',
669
- // 'option' => 'mod',
670
- // 'mime_type' => 'audio/mod',
671
- // ),
672
-
673
- // MOD - audio - MODule (Impulse Tracker)
674
- 'it' => array(
675
- 'pattern' => '^IMPM',
676
- 'group' => 'audio',
677
- 'module' => 'mod',
678
- //'option' => 'it',
679
- 'mime_type' => 'audio/it',
680
- ),
681
-
682
- // MOD - audio - MODule (eXtended Module, various sub-formats)
683
- 'xm' => array(
684
- 'pattern' => '^Extended Module',
685
- 'group' => 'audio',
686
- 'module' => 'mod',
687
- //'option' => 'xm',
688
- 'mime_type' => 'audio/xm',
689
- ),
690
-
691
- // MOD - audio - MODule (ScreamTracker)
692
- 's3m' => array(
693
- 'pattern' => '^.{44}SCRM',
694
- 'group' => 'audio',
695
- 'module' => 'mod',
696
- //'option' => 's3m',
697
- 'mime_type' => 'audio/s3m',
698
- ),
699
-
700
- // MPC - audio - Musepack / MPEGplus
701
- 'mpc' => array(
702
- 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
703
- 'group' => 'audio',
704
- 'module' => 'mpc',
705
- 'mime_type' => 'audio/x-musepack',
706
- ),
707
-
708
- // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS)
709
- 'mp3' => array(
710
- 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]',
711
- 'group' => 'audio',
712
- 'module' => 'mp3',
713
- 'mime_type' => 'audio/mpeg',
714
- ),
715
-
716
- // OFR - audio - OptimFROG
717
- 'ofr' => array(
718
- 'pattern' => '^(\*RIFF|OFR)',
719
- 'group' => 'audio',
720
- 'module' => 'optimfrog',
721
- 'mime_type' => 'application/octet-stream',
722
- ),
723
-
724
- // RKAU - audio - RKive AUdio compressor
725
- 'rkau' => array(
726
- 'pattern' => '^RKA',
727
- 'group' => 'audio',
728
- 'module' => 'rkau',
729
- 'mime_type' => 'application/octet-stream',
730
- ),
731
-
732
- // SHN - audio - Shorten
733
- 'shn' => array(
734
- 'pattern' => '^ajkg',
735
- 'group' => 'audio',
736
- 'module' => 'shorten',
737
- 'mime_type' => 'audio/xmms-shn',
738
- 'fail_id3' => 'ERROR',
739
- 'fail_ape' => 'ERROR',
740
- ),
741
-
742
- // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
743
- 'tta' => array(
744
- 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)'
745
- 'group' => 'audio',
746
- 'module' => 'tta',
747
- 'mime_type' => 'application/octet-stream',
748
- ),
749
-
750
- // VOC - audio - Creative Voice (VOC)
751
- 'voc' => array(
752
- 'pattern' => '^Creative Voice File',
753
- 'group' => 'audio',
754
- 'module' => 'voc',
755
- 'mime_type' => 'audio/voc',
756
- ),
757
-
758
- // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF)
759
- 'vqf' => array(
760
- 'pattern' => '^TWIN',
761
- 'group' => 'audio',
762
- 'module' => 'vqf',
763
- 'mime_type' => 'application/octet-stream',
764
- ),
765
-
766
- // WV - audio - WavPack (v4.0+)
767
- 'wv' => array(
768
- 'pattern' => '^wvpk',
769
- 'group' => 'audio',
770
- 'module' => 'wavpack',
771
- 'mime_type' => 'application/octet-stream',
772
- ),
773
-
774
-
775
- // Audio-Video formats
776
-
777
- // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
778
- 'asf' => array(
779
- 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
780
- 'group' => 'audio-video',
781
- 'module' => 'asf',
782
- 'mime_type' => 'video/x-ms-asf',
783
- 'iconv_req' => false,
784
- ),
785
-
786
- // BINK - audio/video - Bink / Smacker
787
- 'bink' => array(
788
- 'pattern' => '^(BIK|SMK)',
789
- 'group' => 'audio-video',
790
- 'module' => 'bink',
791
- 'mime_type' => 'application/octet-stream',
792
- ),
793
-
794
- // FLV - audio/video - FLash Video
795
- 'flv' => array(
796
- 'pattern' => '^FLV\x01',
797
- 'group' => 'audio-video',
798
- 'module' => 'flv',
799
- 'mime_type' => 'video/x-flv',
800
- ),
801
-
802
- // MKAV - audio/video - Mastroka
803
- 'matroska' => array(
804
- 'pattern' => '^\x1A\x45\xDF\xA3',
805
- 'group' => 'audio-video',
806
- 'module' => 'matroska',
807
- 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
808
- ),
809
-
810
- // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
811
- 'mpeg' => array(
812
- 'pattern' => '^\x00\x00\x01(\xBA|\xB3)',
813
- 'group' => 'audio-video',
814
- 'module' => 'mpeg',
815
- 'mime_type' => 'video/mpeg',
816
- ),
817
-
818
- // NSV - audio/video - Nullsoft Streaming Video (NSV)
819
- 'nsv' => array(
820
- 'pattern' => '^NSV[sf]',
821
- 'group' => 'audio-video',
822
- 'module' => 'nsv',
823
- 'mime_type' => 'application/octet-stream',
824
- ),
825
-
826
- // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
827
- 'ogg' => array(
828
- 'pattern' => '^OggS',
829
- 'group' => 'audio',
830
- 'module' => 'ogg',
831
- 'mime_type' => 'application/ogg',
832
- 'fail_id3' => 'WARNING',
833
- 'fail_ape' => 'WARNING',
834
- ),
835
-
836
- // QT - audio/video - Quicktime
837
- 'quicktime' => array(
838
- 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
839
- 'group' => 'audio-video',
840
- 'module' => 'quicktime',
841
- 'mime_type' => 'video/quicktime',
842
- ),
843
-
844
- // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
845
- 'riff' => array(
846
- 'pattern' => '^(RIFF|SDSS|FORM)',
847
- 'group' => 'audio-video',
848
- 'module' => 'riff',
849
- 'mime_type' => 'audio/x-wave',
850
- 'fail_ape' => 'WARNING',
851
- ),
852
-
853
- // Real - audio/video - RealAudio, RealVideo
854
- 'real' => array(
855
- 'pattern' => '^(\\.RMF|\\.ra)',
856
- 'group' => 'audio-video',
857
- 'module' => 'real',
858
- 'mime_type' => 'audio/x-realaudio',
859
- ),
860
-
861
- // SWF - audio/video - ShockWave Flash
862
- 'swf' => array(
863
- 'pattern' => '^(F|C)WS',
864
- 'group' => 'audio-video',
865
- 'module' => 'swf',
866
- 'mime_type' => 'application/x-shockwave-flash',
867
- ),
868
-
869
- // TS - audio/video - MPEG-2 Transport Stream
870
- 'ts' => array(
871
- 'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern
872
- 'group' => 'audio-video',
873
- 'module' => 'ts',
874
- 'mime_type' => 'video/MP2T',
875
- ),
876
-
877
-
878
- // Still-Image formats
879
-
880
- // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
881
- 'bmp' => array(
882
- 'pattern' => '^BM',
883
- 'group' => 'graphic',
884
- 'module' => 'bmp',
885
- 'mime_type' => 'image/bmp',
886
- 'fail_id3' => 'ERROR',
887
- 'fail_ape' => 'ERROR',
888
- ),
889
-
890
- // GIF - still image - Graphics Interchange Format
891
- 'gif' => array(
892
- 'pattern' => '^GIF',
893
- 'group' => 'graphic',
894
- 'module' => 'gif',
895
- 'mime_type' => 'image/gif',
896
- 'fail_id3' => 'ERROR',
897
- 'fail_ape' => 'ERROR',
898
- ),
899
-
900
- // JPEG - still image - Joint Photographic Experts Group (JPEG)
901
- 'jpg' => array(
902
- 'pattern' => '^\xFF\xD8\xFF',
903
- 'group' => 'graphic',
904
- 'module' => 'jpg',
905
- 'mime_type' => 'image/jpeg',
906
- 'fail_id3' => 'ERROR',
907
- 'fail_ape' => 'ERROR',
908
- ),
909
-
910
- // PCD - still image - Kodak Photo CD
911
- 'pcd' => array(
912
- 'pattern' => '^.{2048}PCD_IPI\x00',
913
- 'group' => 'graphic',
914
- 'module' => 'pcd',
915
- 'mime_type' => 'image/x-photo-cd',
916
- 'fail_id3' => 'ERROR',
917
- 'fail_ape' => 'ERROR',
918
- ),
919
-
920
-
921
- // PNG - still image - Portable Network Graphics (PNG)
922
- 'png' => array(
923
- 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
924
- 'group' => 'graphic',
925
- 'module' => 'png',
926
- 'mime_type' => 'image/png',
927
- 'fail_id3' => 'ERROR',
928
- 'fail_ape' => 'ERROR',
929
- ),
930
-
931
-
932
- // SVG - still image - Scalable Vector Graphics (SVG)
933
- 'svg' => array(
934
- 'pattern' => '(<!DOCTYPE svg PUBLIC |xmlns="http:\/\/www\.w3\.org\/2000\/svg")',
935
- 'group' => 'graphic',
936
- 'module' => 'svg',
937
- 'mime_type' => 'image/svg+xml',
938
- 'fail_id3' => 'ERROR',
939
- 'fail_ape' => 'ERROR',
940
- ),
941
-
942
-
943
- // TIFF - still image - Tagged Information File Format (TIFF)
944
- 'tiff' => array(
945
- 'pattern' => '^(II\x2A\x00|MM\x00\x2A)',
946
- 'group' => 'graphic',
947
- 'module' => 'tiff',
948
- 'mime_type' => 'image/tiff',
949
- 'fail_id3' => 'ERROR',
950
- 'fail_ape' => 'ERROR',
951
- ),
952
-
953
-
954
- // EFAX - still image - eFax (TIFF derivative)
955
- 'efax' => array(
956
- 'pattern' => '^\xDC\xFE',
957
- 'group' => 'graphic',
958
- 'module' => 'efax',
959
- 'mime_type' => 'image/efax',
960
- 'fail_id3' => 'ERROR',
961
- 'fail_ape' => 'ERROR',
962
- ),
963
-
964
-
965
- // Data formats
966
-
967
- // ISO - data - International Standards Organization (ISO) CD-ROM Image
968
- 'iso' => array(
969
- 'pattern' => '^.{32769}CD001',
970
- 'group' => 'misc',
971
- 'module' => 'iso',
972
- 'mime_type' => 'application/octet-stream',
973
- 'fail_id3' => 'ERROR',
974
- 'fail_ape' => 'ERROR',
975
- 'iconv_req' => false,
976
- ),
977
-
978
- // RAR - data - RAR compressed data
979
- 'rar' => array(
980
- 'pattern' => '^Rar\!',
981
- 'group' => 'archive',
982
- 'module' => 'rar',
983
- 'mime_type' => 'application/octet-stream',
984
- 'fail_id3' => 'ERROR',
985
- 'fail_ape' => 'ERROR',
986
- ),
987
-
988
- // SZIP - audio/data - SZIP compressed data
989
- 'szip' => array(
990
- 'pattern' => '^SZ\x0A\x04',
991
- 'group' => 'archive',
992
- 'module' => 'szip',
993
- 'mime_type' => 'application/octet-stream',
994
- 'fail_id3' => 'ERROR',
995
- 'fail_ape' => 'ERROR',
996
- ),
997
-
998
- // TAR - data - TAR compressed data
999
- 'tar' => array(
1000
- 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
1001
- 'group' => 'archive',
1002
- 'module' => 'tar',
1003
- 'mime_type' => 'application/x-tar',
1004
- 'fail_id3' => 'ERROR',
1005
- 'fail_ape' => 'ERROR',
1006
- ),
1007
-
1008
- // GZIP - data - GZIP compressed data
1009
- 'gz' => array(
1010
- 'pattern' => '^\x1F\x8B\x08',
1011
- 'group' => 'archive',
1012
- 'module' => 'gzip',
1013
- 'mime_type' => 'application/x-gzip',
1014
- 'fail_id3' => 'ERROR',
1015
- 'fail_ape' => 'ERROR',
1016
- ),
1017
-
1018
- // ZIP - data - ZIP compressed data
1019
- 'zip' => array(
1020
- 'pattern' => '^PK\x03\x04',
1021
- 'group' => 'archive',
1022
- 'module' => 'zip',
1023
- 'mime_type' => 'application/zip',
1024
- 'fail_id3' => 'ERROR',
1025
- 'fail_ape' => 'ERROR',
1026
- ),
1027
-
1028
-
1029
- // Misc other formats
1030
-
1031
- // PAR2 - data - Parity Volume Set Specification 2.0
1032
- 'par2' => array (
1033
- 'pattern' => '^PAR2\x00PKT',
1034
- 'group' => 'misc',
1035
- 'module' => 'par2',
1036
- 'mime_type' => 'application/octet-stream',
1037
- 'fail_id3' => 'ERROR',
1038
- 'fail_ape' => 'ERROR',
1039
- ),
1040
-
1041
- // PDF - data - Portable Document Format
1042
- 'pdf' => array(
1043
- 'pattern' => '^\x25PDF',
1044
- 'group' => 'misc',
1045
- 'module' => 'pdf',
1046
- 'mime_type' => 'application/pdf',
1047
- 'fail_id3' => 'ERROR',
1048
- 'fail_ape' => 'ERROR',
1049
- ),
1050
-
1051
- // MSOFFICE - data - ZIP compressed data
1052
- 'msoffice' => array(
1053
- 'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document
1054
- 'group' => 'misc',
1055
- 'module' => 'msoffice',
1056
- 'mime_type' => 'application/octet-stream',
1057
- 'fail_id3' => 'ERROR',
1058
- 'fail_ape' => 'ERROR',
1059
- ),
1060
-
1061
- // CUE - data - CUEsheet (index to single-file disc images)
1062
- 'cue' => array(
1063
- 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents
1064
- 'group' => 'misc',
1065
- 'module' => 'cue',
1066
- 'mime_type' => 'application/octet-stream',
1067
- ),
1068
-
1069
- );
1070
- }
1071
-
1072
- return $format_info;
1073
- }
1074
-
1075
-
1076
-
1077
- public function GetFileFormat(&$filedata, $filename='') {
1078
- // this function will determine the format of a file based on usually
1079
- // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
1080
- // and in the case of ISO CD image, 6 bytes offset 32kb from the start
1081
- // of the file).
1082
-
1083
- // Identify file format - loop through $format_info and detect with reg expr
1084
- foreach ($this->GetFileFormatArray() as $format_name => $info) {
1085
- // The /s switch on preg_match() forces preg_match() NOT to treat
1086
- // newline (0x0A) characters as special chars but do a binary match
1087
- if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) {
1088
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1089
- return $info;
1090
- }
1091
- }
1092
-
1093
-
1094
- if (preg_match('#\.mp[123a]$#i', $filename)) {
1095
- // Too many mp3 encoders on the market put gabage in front of mpeg files
1096
- // use assume format on these if format detection failed
1097
- $GetFileFormatArray = $this->GetFileFormatArray();
1098
- $info = $GetFileFormatArray['mp3'];
1099
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1100
- return $info;
1101
- } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) {
1102
- // there's not really a useful consistent "magic" at the beginning of .cue files to identify them
1103
- // so until I think of something better, just go by filename if all other format checks fail
1104
- // and verify there's at least one instance of "TRACK xx AUDIO" in the file
1105
- $GetFileFormatArray = $this->GetFileFormatArray();
1106
- $info = $GetFileFormatArray['cue'];
1107
- $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
1108
- return $info;
1109
- }
1110
-
1111
- return false;
1112
- }
1113
-
1114
-
1115
- // converts array to $encoding charset from $this->encoding
1116
- public function CharConvert(&$array, $encoding) {
1117
-
1118
- // identical encoding - end here
1119
- if ($encoding == $this->encoding) {
1120
- return;
1121
- }
1122
-
1123
- // loop thru array
1124
- foreach ($array as $key => $value) {
1125
-
1126
- // go recursive
1127
- if (is_array($value)) {
1128
- $this->CharConvert($array[$key], $encoding);
1129
- }
1130
-
1131
- // convert string
1132
- elseif (is_string($value)) {
1133
- $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
1134
- }
1135
- }
1136
- }
1137
-
1138
-
1139
- public function HandleAllTags() {
1140
-
1141
- // key name => array (tag name, character encoding)
1142
- static $tags;
1143
- if (empty($tags)) {
1144
- $tags = array(
1145
- 'asf' => array('asf' , 'UTF-16LE'),
1146
- 'midi' => array('midi' , 'ISO-8859-1'),
1147
- 'nsv' => array('nsv' , 'ISO-8859-1'),
1148
- 'ogg' => array('vorbiscomment' , 'UTF-8'),
1149
- 'png' => array('png' , 'UTF-8'),
1150
- 'tiff' => array('tiff' , 'ISO-8859-1'),
1151
- 'quicktime' => array('quicktime' , 'UTF-8'),
1152
- 'real' => array('real' , 'ISO-8859-1'),
1153
- 'vqf' => array('vqf' , 'ISO-8859-1'),
1154
- 'zip' => array('zip' , 'ISO-8859-1'),
1155
- 'riff' => array('riff' , 'ISO-8859-1'),
1156
- 'lyrics3' => array('lyrics3' , 'ISO-8859-1'),
1157
- 'id3v1' => array('id3v1' , $this->encoding_id3v1),
1158
- 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
1159
- 'ape' => array('ape' , 'UTF-8'),
1160
- 'cue' => array('cue' , 'ISO-8859-1'),
1161
- 'matroska' => array('matroska' , 'UTF-8'),
1162
- 'flac' => array('vorbiscomment' , 'UTF-8'),
1163
- 'divxtag' => array('divx' , 'ISO-8859-1'),
1164
- );
1165
- }
1166
-
1167
- // loop through comments array
1168
- foreach ($tags as $comment_name => $tagname_encoding_array) {
1169
- list($tag_name, $encoding) = $tagname_encoding_array;
1170
-
1171
- // fill in default encoding type if not already present
1172
- if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
1173
- $this->info[$comment_name]['encoding'] = $encoding;
1174
- }
1175
-
1176
- // copy comments if key name set
1177
- if (!empty($this->info[$comment_name]['comments'])) {
1178
- foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
1179
- foreach ($valuearray as $key => $value) {
1180
- if (is_string($value)) {
1181
- $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed!
1182
- }
1183
- if ($value) {
1184
- $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value;
1185
- }
1186
- }
1187
- if ($tag_key == 'picture') {
1188
- unset($this->info[$comment_name]['comments'][$tag_key]);
1189
- }
1190
- }
1191
-
1192
- if (!isset($this->info['tags'][$tag_name])) {
1193
- // comments are set but contain nothing but empty strings, so skip
1194
- continue;
1195
- }
1196
-
1197
- if ($this->option_tags_html) {
1198
- foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
1199
- foreach ($valuearray as $key => $value) {
1200
- if (is_string($value)) {
1201
- //$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
1202
- $this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', trim(getid3_lib::MultiByteCharString2HTML($value, $encoding)));
1203
- } else {
1204
- $this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
1205
- }
1206
- }
1207
- }
1208
- }
1209
-
1210
- $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted!
1211
- }
1212
-
1213
- }
1214
-
1215
- // pictures can take up a lot of space, and we don't need multiple copies of them
1216
- // let there be a single copy in [comments][picture], and not elsewhere
1217
- if (!empty($this->info['tags'])) {
1218
- $unset_keys = array('tags', 'tags_html');
1219
- foreach ($this->info['tags'] as $tagtype => $tagarray) {
1220
- foreach ($tagarray as $tagname => $tagdata) {
1221
- if ($tagname == 'picture') {
1222
- foreach ($tagdata as $key => $tagarray) {
1223
- $this->info['comments']['picture'][] = $tagarray;
1224
- if (isset($tagarray['data']) && isset($tagarray['image_mime'])) {
1225
- if (isset($this->info['tags'][$tagtype][$tagname][$key])) {
1226
- unset($this->info['tags'][$tagtype][$tagname][$key]);
1227
- }
1228
- if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) {
1229
- unset($this->info['tags_html'][$tagtype][$tagname][$key]);
1230
- }
1231
- }
1232
- }
1233
- }
1234
- }
1235
- foreach ($unset_keys as $unset_key) {
1236
- // remove possible empty keys from (e.g. [tags][id3v2][picture])
1237
- if (empty($this->info[$unset_key][$tagtype]['picture'])) {
1238
- unset($this->info[$unset_key][$tagtype]['picture']);
1239
- }
1240
- if (empty($this->info[$unset_key][$tagtype])) {
1241
- unset($this->info[$unset_key][$tagtype]);
1242
- }
1243
- if (empty($this->info[$unset_key])) {
1244
- unset($this->info[$unset_key]);
1245
- }
1246
- }
1247
- // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture])
1248
- if (isset($this->info[$tagtype]['comments']['picture'])) {
1249
- unset($this->info[$tagtype]['comments']['picture']);
1250
- }
1251
- if (empty($this->info[$tagtype]['comments'])) {
1252
- unset($this->info[$tagtype]['comments']);
1253
- }
1254
- if (empty($this->info[$tagtype])) {
1255
- unset($this->info[$tagtype]);
1256
- }
1257
- }
1258
- }
1259
- return true;
1260
- }
1261
-
1262
-
1263
- public function getHashdata($algorithm) {
1264
- switch ($algorithm) {
1265
- case 'md5':
1266
- case 'sha1':
1267
- break;
1268
-
1269
- default:
1270
- return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
1271
- break;
1272
- }
1273
-
1274
- if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
1275
-
1276
- // We cannot get an identical md5_data value for Ogg files where the comments
1277
- // span more than 1 Ogg page (compared to the same audio data with smaller
1278
- // comments) using the normal getID3() method of MD5'ing the data between the
1279
- // end of the comments and the end of the file (minus any trailing tags),
1280
- // because the page sequence numbers of the pages that the audio data is on
1281
- // do not match. Under normal circumstances, where comments are smaller than
1282
- // the nominal 4-8kB page size, then this is not a problem, but if there are
1283
- // very large comments, the only way around it is to strip off the comment
1284
- // tags with vorbiscomment and MD5 that file.
1285
- // This procedure must be applied to ALL Ogg files, not just the ones with
1286
- // comments larger than 1 page, because the below method simply MD5's the
1287
- // whole file with the comments stripped, not just the portion after the
1288
- // comments block (which is the standard getID3() method.
1289
-
1290
- // The above-mentioned problem of comments spanning multiple pages and changing
1291
- // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
1292
- // currently vorbiscomment only works on OggVorbis files.
1293
-
1294
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
1295
-
1296
- $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
1297
- $this->info[$algorithm.'_data'] = false;
1298
-
1299
- } else {
1300
-
1301
- // Prevent user from aborting script
1302
- $old_abort = ignore_user_abort(true);
1303
-
1304
- // Create empty file
1305
- $empty = tempnam(GETID3_TEMP_DIR, 'getID3');
1306
- touch($empty);
1307
-
1308
- // Use vorbiscomment to make temp file without comments
1309
- $temp = tempnam(GETID3_TEMP_DIR, 'getID3');
1310
- $file = $this->info['filenamepath'];
1311
-
1312
- if (GETID3_OS_ISWINDOWS) {
1313
-
1314
- if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
1315
-
1316
- $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
1317
- $VorbisCommentError = `$commandline`;
1318
-
1319
- } else {
1320
-
1321
- $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
1322
-
1323
- }
1324
-
1325
- } else {
1326
-
1327
- $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
1328
- $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
1329
- $VorbisCommentError = `$commandline`;
1330
-
1331
- }
1332
-
1333
- if (!empty($VorbisCommentError)) {
1334
-
1335
- $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
1336
- $this->info[$algorithm.'_data'] = false;
1337
-
1338
- } else {
1339
-
1340
- // Get hash of newly created file
1341
- switch ($algorithm) {
1342
- case 'md5':
1343
- $this->info[$algorithm.'_data'] = md5_file($temp);
1344
- break;
1345
-
1346
- case 'sha1':
1347
- $this->info[$algorithm.'_data'] = sha1_file($temp);
1348
- break;
1349
- }
1350
- }
1351
-
1352
- // Clean up
1353
- unlink($empty);
1354
- unlink($temp);
1355
-
1356
- // Reset abort setting
1357
- ignore_user_abort($old_abort);
1358
-
1359
- }
1360
-
1361
- } else {
1362
-
1363
- if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
1364
-
1365
- // get hash from part of file
1366
- $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
1367
-
1368
- } else {
1369
-
1370
- // get hash from whole file
1371
- switch ($algorithm) {
1372
- case 'md5':
1373
- $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']);
1374
- break;
1375
-
1376
- case 'sha1':
1377
- $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']);
1378
- break;
1379
- }
1380
- }
1381
-
1382
- }
1383
- return true;
1384
- }
1385
-
1386
-
1387
- public function ChannelsBitratePlaytimeCalculations() {
1388
-
1389
- // set channelmode on audio
1390
- if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) {
1391
- // ignore
1392
- } elseif ($this->info['audio']['channels'] == 1) {
1393
- $this->info['audio']['channelmode'] = 'mono';
1394
- } elseif ($this->info['audio']['channels'] == 2) {
1395
- $this->info['audio']['channelmode'] = 'stereo';
1396
- }
1397
-
1398
- // Calculate combined bitrate - audio + video
1399
- $CombinedBitrate = 0;
1400
- $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
1401
- $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
1402
- if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
1403
- $this->info['bitrate'] = $CombinedBitrate;
1404
- }
1405
- //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
1406
- // // for example, VBR MPEG video files cannot determine video bitrate:
1407
- // // should not set overall bitrate and playtime from audio bitrate only
1408
- // unset($this->info['bitrate']);
1409
- //}
1410
-
1411
- // video bitrate undetermined, but calculable
1412
- if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
1413
- // if video bitrate not set
1414
- if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
1415
- // AND if audio bitrate is set to same as overall bitrate
1416
- if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
1417
- // AND if playtime is set
1418
- if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
1419
- // AND if AV data offset start/end is known
1420
- // THEN we can calculate the video bitrate
1421
- $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
1422
- $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
1423
- }
1424
- }
1425
- }
1426
- }
1427
-
1428
- if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
1429
- $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
1430
- }
1431
-
1432
- if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
1433
- $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
1434
- }
1435
- if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
1436
- if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
1437
- // audio only
1438
- $this->info['audio']['bitrate'] = $this->info['bitrate'];
1439
- } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
1440
- // video only
1441
- $this->info['video']['bitrate'] = $this->info['bitrate'];
1442
- }
1443
- }
1444
-
1445
- // Set playtime string
1446
- if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
1447
- $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
1448
- }
1449
- }
1450
-
1451
-
1452
- public function CalculateCompressionRatioVideo() {
1453
- if (empty($this->info['video'])) {
1454
- return false;
1455
- }
1456
- if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
1457
- return false;
1458
- }
1459
- if (empty($this->info['video']['bits_per_sample'])) {
1460
- return false;
1461
- }
1462
-
1463
- switch ($this->info['video']['dataformat']) {
1464
- case 'bmp':
1465
- case 'gif':
1466
- case 'jpeg':
1467
- case 'jpg':
1468
- case 'png':
1469
- case 'tiff':
1470
- $FrameRate = 1;
1471
- $PlaytimeSeconds = 1;
1472
- $BitrateCompressed = $this->info['filesize'] * 8;
1473
- break;
1474
-
1475
- default:
1476
- if (!empty($this->info['video']['frame_rate'])) {
1477
- $FrameRate = $this->info['video']['frame_rate'];
1478
- } else {
1479
- return false;
1480
- }
1481
- if (!empty($this->info['playtime_seconds'])) {
1482
- $PlaytimeSeconds = $this->info['playtime_seconds'];
1483
- } else {
1484
- return false;
1485
- }
1486
- if (!empty($this->info['video']['bitrate'])) {
1487
- $BitrateCompressed = $this->info['video']['bitrate'];
1488
- } else {
1489
- return false;
1490
- }
1491
- break;
1492
- }
1493
- $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
1494
-
1495
- $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
1496
- return true;
1497
- }
1498
-
1499
-
1500
- public function CalculateCompressionRatioAudio() {
1501
- if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) {
1502
- return false;
1503
- }
1504
- $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
1505
-
1506
- if (!empty($this->info['audio']['streams'])) {
1507
- foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
1508
- if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
1509
- $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
1510
- }
1511
- }
1512
- }
1513
- return true;
1514
- }
1515
-
1516
-
1517
- public function CalculateReplayGain() {
1518
- if (isset($this->info['replay_gain'])) {
1519
- if (!isset($this->info['replay_gain']['reference_volume'])) {
1520
- $this->info['replay_gain']['reference_volume'] = (double) 89.0;
1521
- }
1522
- if (isset($this->info['replay_gain']['track']['adjustment'])) {
1523
- $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
1524
- }
1525
- if (isset($this->info['replay_gain']['album']['adjustment'])) {
1526
- $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
1527
- }
1528
-
1529
- if (isset($this->info['replay_gain']['track']['peak'])) {
1530
- $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
1531
- }
1532
- if (isset($this->info['replay_gain']['album']['peak'])) {
1533
- $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
1534
- }
1535
- }
1536
- return true;
1537
- }
1538
-
1539
- public function ProcessAudioStreams() {
1540
- if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
1541
- if (!isset($this->info['audio']['streams'])) {
1542
- foreach ($this->info['audio'] as $key => $value) {
1543
- if ($key != 'streams') {
1544
- $this->info['audio']['streams'][0][$key] = $value;
1545
- }
1546
- }
1547
- }
1548
- }
1549
- return true;
1550
- }
1551
-
1552
- public function getid3_tempnam() {
1553
- return tempnam($this->tempdir, 'gI3');
1554
- }
1555
-
1556
- public function include_module($name) {
1557
- //if (!file_exists($this->include_path.'module.'.$name.'.php')) {
1558
- if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) {
1559
- throw new getid3_exception('Required module.'.$name.'.php is missing.');
1560
- }
1561
- include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php');
1562
- return true;
1563
- }
1564
-
1565
- }
1566
-
1567
-
1568
- abstract class getid3_handler
1569
- {
1570
- protected $getid3; // pointer
1571
-
1572
- protected $data_string_flag = false; // analyzing filepointer or string
1573
- protected $data_string = ''; // string to analyze
1574
- protected $data_string_position = 0; // seek position in string
1575
- protected $data_string_length = 0; // string length
1576
-
1577
- private $dependency_to = null;
1578
-
1579
-
1580
- public function __construct(getID3 $getid3, $call_module=null) {
1581
- $this->getid3 = $getid3;
1582
-
1583
- if ($call_module) {
1584
- $this->dependency_to = str_replace('getid3_', '', $call_module);
1585
- }
1586
- }
1587
-
1588
-
1589
- // Analyze from file pointer
1590
- abstract public function Analyze();
1591
-
1592
-
1593
- // Analyze from string instead
1594
- public function AnalyzeString($string) {
1595
- // Enter string mode
1596
- $this->setStringMode($string);
1597
-
1598
- // Save info
1599
- $saved_avdataoffset = $this->getid3->info['avdataoffset'];
1600
- $saved_avdataend = $this->getid3->info['avdataend'];
1601
- $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call
1602
-
1603
- // Reset some info
1604
- $this->getid3->info['avdataoffset'] = 0;
1605
- $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length;
1606
-
1607
- // Analyze
1608
- $this->Analyze();
1609
-
1610
- // Restore some info
1611
- $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
1612
- $this->getid3->info['avdataend'] = $saved_avdataend;
1613
- $this->getid3->info['filesize'] = $saved_filesize;
1614
-
1615
- // Exit string mode
1616
- $this->data_string_flag = false;
1617
- }
1618
-
1619
- public function setStringMode($string) {
1620
- $this->data_string_flag = true;
1621
- $this->data_string = $string;
1622
- $this->data_string_length = strlen($string);
1623
- }
1624
-
1625
- protected function ftell() {
1626
- if ($this->data_string_flag) {
1627
- return $this->data_string_position;
1628
- }
1629
- return ftell($this->getid3->fp);
1630
- }
1631
-
1632
- protected function fread($bytes) {
1633
- if ($this->data_string_flag) {
1634
- $this->data_string_position += $bytes;
1635
- return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
1636
- }
1637
- $pos = $this->ftell() + $bytes;
1638
- if (!getid3_lib::intValueSupported($pos)) {
1639
- throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10);
1640
- }
1641
- return fread($this->getid3->fp, $bytes);
1642
- }
1643
-
1644
- protected function fseek($bytes, $whence=SEEK_SET) {
1645
- if ($this->data_string_flag) {
1646
- switch ($whence) {
1647
- case SEEK_SET:
1648
- $this->data_string_position = $bytes;
1649
- break;
1650
-
1651
- case SEEK_CUR:
1652
- $this->data_string_position += $bytes;
1653
- break;
1654
-
1655
- case SEEK_END:
1656
- $this->data_string_position = $this->data_string_length + $bytes;
1657
- break;
1658
- }
1659
- return 0;
1660
- } else {
1661
- $pos = $bytes;
1662
- if ($whence == SEEK_CUR) {
1663
- $pos = $this->ftell() + $bytes;
1664
- } elseif ($whence == SEEK_END) {
1665
- $pos = $this->info['filesize'] + $bytes;
1666
- }
1667
- if (!getid3_lib::intValueSupported($pos)) {
1668
- throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10);
1669
- }
1670
- }
1671
- return fseek($this->getid3->fp, $bytes, $whence);
1672
- }
1673
-
1674
- protected function feof() {
1675
- if ($this->data_string_flag) {
1676
- return $this->data_string_position >= $this->data_string_length;
1677
- }
1678
- return feof($this->getid3->fp);
1679
- }
1680
-
1681
- final protected function isDependencyFor($module) {
1682
- return $this->dependency_to == $module;
1683
- }
1684
-
1685
- protected function error($text)
1686
- {
1687
- $this->getid3->info['error'][] = $text;
1688
-
1689
- return false;
1690
- }
1691
-
1692
- protected function warning($text)
1693
- {
1694
- return $this->getid3->warning($text);
1695
- }
1696
-
1697
- protected function notice($text)
1698
- {
1699
- // does nothing for now
1700
- }
1701
-
1702
- public function saveAttachment($name, $offset, $length, $image_mime=null) {
1703
- try {
1704
-
1705
- // do not extract at all
1706
- if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) {
1707
-
1708
- $attachment = null; // do not set any
1709
-
1710
- // extract to return array
1711
- } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) {
1712
-
1713
- $this->fseek($offset);
1714
- $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory
1715
- if ($attachment === false || strlen($attachment) != $length) {
1716
- throw new Exception('failed to read attachment data');
1717
- }
1718
-
1719
- // assume directory path is given
1720
- } else {
1721
-
1722
- // set up destination path
1723
- $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1724
- if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory
1725
- throw new Exception('supplied path ('.$dir.') does not exist, or is not writable');
1726
- }
1727
- $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : '');
1728
-
1729
- // create dest file
1730
- if (($fp_dest = fopen($dest, 'wb')) == false) {
1731
- throw new Exception('failed to create file '.$dest);
1732
- }
1733
-
1734
- // copy data
1735
- $this->fseek($offset);
1736
- $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size());
1737
- $bytesleft = $length;
1738
- while ($bytesleft > 0) {
1739
- if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) {
1740
- throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
1741
- }
1742
- $bytesleft -= $byteswritten;
1743
- }
1744
-
1745
- fclose($fp_dest);
1746
- $attachment = $dest;
1747
-
1748
- }
1749
-
1750
- } catch (Exception $e) {
1751
-
1752
- // close and remove dest file if created
1753
- if (isset($fp_dest) && is_resource($fp_dest)) {
1754
- fclose($fp_dest);
1755
- unlink($dest);
1756
- }
1757
-
1758
- // do not set any is case of error
1759
- $attachment = null;
1760
- $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage());
1761
-
1762
- }
1763
-
1764
- // seek to the end of attachment
1765
- $this->fseek($offset + $length);
1766
-
1767
- return $attachment;
1768
- }
1769
-
1770
- }
1771
-
1772
-
1773
- class getid3_exception extends Exception
1774
- {
1775
- public $message;
1776
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.flv.php DELETED
@@ -1,729 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- // //
7
- // FLV module by Seth Kaufman <seth�whirl-i-gig*com> //
8
- // //
9
- // * version 0.1 (26 June 2005) //
10
- // //
11
- // //
12
- // * version 0.1.1 (15 July 2005) //
13
- // minor modifications by James Heinrich <info@getid3.org> //
14
- // //
15
- // * version 0.2 (22 February 2006) //
16
- // Support for On2 VP6 codec and meta information //
17
- // by Steve Webster <steve.webster�featurecreep*com> //
18
- // //
19
- // * version 0.3 (15 June 2006) //
20
- // Modified to not read entire file into memory //
21
- // by James Heinrich <info@getid3.org> //
22
- // //
23
- // * version 0.4 (07 December 2007) //
24
- // Bugfixes for incorrectly parsed FLV dimensions //
25
- // and incorrect parsing of onMetaTag //
26
- // by Evgeny Moysevich <moysevich�gmail*com> //
27
- // //
28
- // * version 0.5 (21 May 2009) //
29
- // Fixed parsing of audio tags and added additional codec //
30
- // details. The duration is now read from onMetaTag (if //
31
- // exists), rather than parsing whole file //
32
- // by Nigel Barnes <ngbarnes�hotmail*com> //
33
- // //
34
- // * version 0.6 (24 May 2009) //
35
- // Better parsing of files with h264 video //
36
- // by Evgeny Moysevich <moysevich�gmail*com> //
37
- // //
38
- // * version 0.6.1 (30 May 2011) //
39
- // prevent infinite loops in expGolombUe() //
40
- // //
41
- /////////////////////////////////////////////////////////////////
42
- // //
43
- // module.audio-video.flv.php //
44
- // module for analyzing Shockwave Flash Video files //
45
- // dependencies: NONE //
46
- // ///
47
- /////////////////////////////////////////////////////////////////
48
-
49
- define('GETID3_FLV_TAG_AUDIO', 8);
50
- define('GETID3_FLV_TAG_VIDEO', 9);
51
- define('GETID3_FLV_TAG_META', 18);
52
-
53
- define('GETID3_FLV_VIDEO_H263', 2);
54
- define('GETID3_FLV_VIDEO_SCREEN', 3);
55
- define('GETID3_FLV_VIDEO_VP6FLV', 4);
56
- define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
57
- define('GETID3_FLV_VIDEO_SCREENV2', 6);
58
- define('GETID3_FLV_VIDEO_H264', 7);
59
-
60
- define('H264_AVC_SEQUENCE_HEADER', 0);
61
- define('H264_PROFILE_BASELINE', 66);
62
- define('H264_PROFILE_MAIN', 77);
63
- define('H264_PROFILE_EXTENDED', 88);
64
- define('H264_PROFILE_HIGH', 100);
65
- define('H264_PROFILE_HIGH10', 110);
66
- define('H264_PROFILE_HIGH422', 122);
67
- define('H264_PROFILE_HIGH444', 144);
68
- define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
69
-
70
- class getid3_flv extends getid3_handler
71
- {
72
- public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration
73
-
74
- public function Analyze() {
75
- $info = &$this->getid3->info;
76
-
77
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
78
-
79
- $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
80
- $FLVheader = fread($this->getid3->fp, 5);
81
-
82
- $info['fileformat'] = 'flv';
83
- $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
84
- $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
85
- $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
86
-
87
- $magic = 'FLV';
88
- if ($info['flv']['header']['signature'] != $magic) {
89
- $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"';
90
- unset($info['flv']);
91
- unset($info['fileformat']);
92
- return false;
93
- }
94
-
95
- $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
96
- $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
97
-
98
- $FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 4));
99
- $FLVheaderFrameLength = 9;
100
- if ($FrameSizeDataLength > $FLVheaderFrameLength) {
101
- fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
102
- }
103
- $Duration = 0;
104
- $found_video = false;
105
- $found_audio = false;
106
- $found_meta = false;
107
- $found_valid_meta_playtime = false;
108
- $tagParseCount = 0;
109
- $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
110
- $flv_framecount = &$info['flv']['framecount'];
111
- while (((ftell($this->getid3->fp) + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) {
112
- $ThisTagHeader = fread($this->getid3->fp, 16);
113
-
114
- $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4));
115
- $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1));
116
- $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3));
117
- $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3));
118
- $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
119
- $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength;
120
- if ($Timestamp > $Duration) {
121
- $Duration = $Timestamp;
122
- }
123
-
124
- $flv_framecount['total']++;
125
- switch ($TagType) {
126
- case GETID3_FLV_TAG_AUDIO:
127
- $flv_framecount['audio']++;
128
- if (!$found_audio) {
129
- $found_audio = true;
130
- $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F;
131
- $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03;
132
- $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
133
- $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01;
134
- }
135
- break;
136
-
137
- case GETID3_FLV_TAG_VIDEO:
138
- $flv_framecount['video']++;
139
- if (!$found_video) {
140
- $found_video = true;
141
- $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
142
-
143
- $FLVvideoHeader = fread($this->getid3->fp, 11);
144
-
145
- if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
146
- // this code block contributed by: moysevich�gmail*com
147
-
148
- $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
149
- if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
150
- // read AVCDecoderConfigurationRecord
151
- $configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
152
- $AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
153
- $profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
154
- $lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
155
- $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
156
-
157
- if (($numOfSequenceParameterSets & 0x1F) != 0) {
158
- // there is at least one SequenceParameterSet
159
- // read size of the first SequenceParameterSet
160
- //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
161
- $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
162
- // read the first SequenceParameterSet
163
- $sps = fread($this->getid3->fp, $spsSize);
164
- if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red
165
- $spsReader = new AVCSequenceParameterSetReader($sps);
166
- $spsReader->readData();
167
- $info['video']['resolution_x'] = $spsReader->getWidth();
168
- $info['video']['resolution_y'] = $spsReader->getHeight();
169
- }
170
- }
171
- }
172
- // end: moysevich�gmail*com
173
-
174
- } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
175
-
176
- $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
177
- $PictureSizeType = $PictureSizeType & 0x0007;
178
- $info['flv']['header']['videoSizeType'] = $PictureSizeType;
179
- switch ($PictureSizeType) {
180
- case 0:
181
- //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
182
- //$PictureSizeEnc <<= 1;
183
- //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
184
- //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
185
- //$PictureSizeEnc <<= 1;
186
- //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
187
-
188
- $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
189
- $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
190
- $PictureSizeEnc['x'] >>= 7;
191
- $PictureSizeEnc['y'] >>= 7;
192
- $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
193
- $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
194
- break;
195
-
196
- case 1:
197
- $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
198
- $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
199
- $PictureSizeEnc['x'] >>= 7;
200
- $PictureSizeEnc['y'] >>= 7;
201
- $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
202
- $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
203
- break;
204
-
205
- case 2:
206
- $info['video']['resolution_x'] = 352;
207
- $info['video']['resolution_y'] = 288;
208
- break;
209
-
210
- case 3:
211
- $info['video']['resolution_x'] = 176;
212
- $info['video']['resolution_y'] = 144;
213
- break;
214
-
215
- case 4:
216
- $info['video']['resolution_x'] = 128;
217
- $info['video']['resolution_y'] = 96;
218
- break;
219
-
220
- case 5:
221
- $info['video']['resolution_x'] = 320;
222
- $info['video']['resolution_y'] = 240;
223
- break;
224
-
225
- case 6:
226
- $info['video']['resolution_x'] = 160;
227
- $info['video']['resolution_y'] = 120;
228
- break;
229
-
230
- default:
231
- $info['video']['resolution_x'] = 0;
232
- $info['video']['resolution_y'] = 0;
233
- break;
234
-
235
- }
236
- }
237
- $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
238
- }
239
- break;
240
-
241
- // Meta tag
242
- case GETID3_FLV_TAG_META:
243
- if (!$found_meta) {
244
- $found_meta = true;
245
- fseek($this->getid3->fp, -1, SEEK_CUR);
246
- $datachunk = fread($this->getid3->fp, $DataLength);
247
- $AMFstream = new AMFStream($datachunk);
248
- $reader = new AMFReader($AMFstream);
249
- $eventName = $reader->readData();
250
- $info['flv']['meta'][$eventName] = $reader->readData();
251
- unset($reader);
252
-
253
- $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate');
254
- foreach ($copykeys as $sourcekey => $destkey) {
255
- if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
256
- switch ($sourcekey) {
257
- case 'width':
258
- case 'height':
259
- $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
260
- break;
261
- case 'audiodatarate':
262
- $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
263
- break;
264
- case 'videodatarate':
265
- case 'frame_rate':
266
- default:
267
- $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
268
- break;
269
- }
270
- }
271
- }
272
- if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
273
- $found_valid_meta_playtime = true;
274
- }
275
- }
276
- break;
277
-
278
- default:
279
- // noop
280
- break;
281
- }
282
- fseek($this->getid3->fp, $NextOffset, SEEK_SET);
283
- }
284
-
285
- $info['playtime_seconds'] = $Duration / 1000;
286
- if ($info['playtime_seconds'] > 0) {
287
- $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
288
- }
289
-
290
- if ($info['flv']['header']['hasAudio']) {
291
- $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']);
292
- $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']);
293
- $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']);
294
-
295
- $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
296
- $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
297
- $info['audio']['dataformat'] = 'flv';
298
- }
299
- if (!empty($info['flv']['header']['hasVideo'])) {
300
- $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']);
301
- $info['video']['dataformat'] = 'flv';
302
- $info['video']['lossless'] = false;
303
- }
304
-
305
- // Set information from meta
306
- if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
307
- $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
308
- $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
309
- }
310
- if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
311
- $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
312
- }
313
- if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
314
- $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
315
- }
316
- return true;
317
- }
318
-
319
-
320
- public function FLVaudioFormat($id) {
321
- $FLVaudioFormat = array(
322
- 0 => 'Linear PCM, platform endian',
323
- 1 => 'ADPCM',
324
- 2 => 'mp3',
325
- 3 => 'Linear PCM, little endian',
326
- 4 => 'Nellymoser 16kHz mono',
327
- 5 => 'Nellymoser 8kHz mono',
328
- 6 => 'Nellymoser',
329
- 7 => 'G.711A-law logarithmic PCM',
330
- 8 => 'G.711 mu-law logarithmic PCM',
331
- 9 => 'reserved',
332
- 10 => 'AAC',
333
- 11 => false, // unknown?
334
- 12 => false, // unknown?
335
- 13 => false, // unknown?
336
- 14 => 'mp3 8kHz',
337
- 15 => 'Device-specific sound',
338
- );
339
- return (isset($FLVaudioFormat[$id]) ? $FLVaudioFormat[$id] : false);
340
- }
341
-
342
- public function FLVaudioRate($id) {
343
- $FLVaudioRate = array(
344
- 0 => 5500,
345
- 1 => 11025,
346
- 2 => 22050,
347
- 3 => 44100,
348
- );
349
- return (isset($FLVaudioRate[$id]) ? $FLVaudioRate[$id] : false);
350
- }
351
-
352
- public function FLVaudioBitDepth($id) {
353
- $FLVaudioBitDepth = array(
354
- 0 => 8,
355
- 1 => 16,
356
- );
357
- return (isset($FLVaudioBitDepth[$id]) ? $FLVaudioBitDepth[$id] : false);
358
- }
359
-
360
- public function FLVvideoCodec($id) {
361
- $FLVvideoCodec = array(
362
- GETID3_FLV_VIDEO_H263 => 'Sorenson H.263',
363
- GETID3_FLV_VIDEO_SCREEN => 'Screen video',
364
- GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6',
365
- GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
366
- GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2',
367
- GETID3_FLV_VIDEO_H264 => 'Sorenson H.264',
368
- );
369
- return (isset($FLVvideoCodec[$id]) ? $FLVvideoCodec[$id] : false);
370
- }
371
- }
372
-
373
- class AMFStream {
374
- public $bytes;
375
- public $pos;
376
-
377
- public function AMFStream(&$bytes) {
378
- $this->bytes =& $bytes;
379
- $this->pos = 0;
380
- }
381
-
382
- public function readByte() {
383
- return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
384
- }
385
-
386
- public function readInt() {
387
- return ($this->readByte() << 8) + $this->readByte();
388
- }
389
-
390
- public function readLong() {
391
- return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
392
- }
393
-
394
- public function readDouble() {
395
- return getid3_lib::BigEndian2Float($this->read(8));
396
- }
397
-
398
- public function readUTF() {
399
- $length = $this->readInt();
400
- return $this->read($length);
401
- }
402
-
403
- public function readLongUTF() {
404
- $length = $this->readLong();
405
- return $this->read($length);
406
- }
407
-
408
- public function read($length) {
409
- $val = substr($this->bytes, $this->pos, $length);
410
- $this->pos += $length;
411
- return $val;
412
- }
413
-
414
- public function peekByte() {
415
- $pos = $this->pos;
416
- $val = $this->readByte();
417
- $this->pos = $pos;
418
- return $val;
419
- }
420
-
421
- public function peekInt() {
422
- $pos = $this->pos;
423
- $val = $this->readInt();
424
- $this->pos = $pos;
425
- return $val;
426
- }
427
-
428
- public function peekLong() {
429
- $pos = $this->pos;
430
- $val = $this->readLong();
431
- $this->pos = $pos;
432
- return $val;
433
- }
434
-
435
- public function peekDouble() {
436
- $pos = $this->pos;
437
- $val = $this->readDouble();
438
- $this->pos = $pos;
439
- return $val;
440
- }
441
-
442
- public function peekUTF() {
443
- $pos = $this->pos;
444
- $val = $this->readUTF();
445
- $this->pos = $pos;
446
- return $val;
447
- }
448
-
449
- public function peekLongUTF() {
450
- $pos = $this->pos;
451
- $val = $this->readLongUTF();
452
- $this->pos = $pos;
453
- return $val;
454
- }
455
- }
456
-
457
- class AMFReader {
458
- public $stream;
459
-
460
- public function AMFReader(&$stream) {
461
- $this->stream =& $stream;
462
- }
463
-
464
- public function readData() {
465
- $value = null;
466
-
467
- $type = $this->stream->readByte();
468
- switch ($type) {
469
-
470
- // Double
471
- case 0:
472
- $value = $this->readDouble();
473
- break;
474
-
475
- // Boolean
476
- case 1:
477
- $value = $this->readBoolean();
478
- break;
479
-
480
- // String
481
- case 2:
482
- $value = $this->readString();
483
- break;
484
-
485
- // Object
486
- case 3:
487
- $value = $this->readObject();
488
- break;
489
-
490
- // null
491
- case 6:
492
- return null;
493
- break;
494
-
495
- // Mixed array
496
- case 8:
497
- $value = $this->readMixedArray();
498
- break;
499
-
500
- // Array
501
- case 10:
502
- $value = $this->readArray();
503
- break;
504
-
505
- // Date
506
- case 11:
507
- $value = $this->readDate();
508
- break;
509
-
510
- // Long string
511
- case 13:
512
- $value = $this->readLongString();
513
- break;
514
-
515
- // XML (handled as string)
516
- case 15:
517
- $value = $this->readXML();
518
- break;
519
-
520
- // Typed object (handled as object)
521
- case 16:
522
- $value = $this->readTypedObject();
523
- break;
524
-
525
- // Long string
526
- default:
527
- $value = '(unknown or unsupported data type)';
528
- break;
529
- }
530
-
531
- return $value;
532
- }
533
-
534
- public function readDouble() {
535
- return $this->stream->readDouble();
536
- }
537
-
538
- public function readBoolean() {
539
- return $this->stream->readByte() == 1;
540
- }
541
-
542
- public function readString() {
543
- return $this->stream->readUTF();
544
- }
545
-
546
- public function readObject() {
547
- // Get highest numerical index - ignored
548
- // $highestIndex = $this->stream->readLong();
549
-
550
- $data = array();
551
-
552
- while ($key = $this->stream->readUTF()) {
553
- $data[$key] = $this->readData();
554
- }
555
- // Mixed array record ends with empty string (0x00 0x00) and 0x09
556
- if (($key == '') && ($this->stream->peekByte() == 0x09)) {
557
- // Consume byte
558
- $this->stream->readByte();
559
- }
560
- return $data;
561
- }
562
-
563
- public function readMixedArray() {
564
- // Get highest numerical index - ignored
565
- $highestIndex = $this->stream->readLong();
566
-
567
- $data = array();
568
-
569
- while ($key = $this->stream->readUTF()) {
570
- if (is_numeric($key)) {
571
- $key = (float) $key;
572
- }
573
- $data[$key] = $this->readData();
574
- }
575
- // Mixed array record ends with empty string (0x00 0x00) and 0x09
576
- if (($key == '') && ($this->stream->peekByte() == 0x09)) {
577
- // Consume byte
578
- $this->stream->readByte();
579
- }
580
-
581
- return $data;
582
- }
583
-
584
- public function readArray() {
585
- $length = $this->stream->readLong();
586
- $data = array();
587
-
588
- for ($i = 0; $i < $length; $i++) {
589
- $data[] = $this->readData();
590
- }
591
- return $data;
592
- }
593
-
594
- public function readDate() {
595
- $timestamp = $this->stream->readDouble();
596
- $timezone = $this->stream->readInt();
597
- return $timestamp;
598
- }
599
-
600
- public function readLongString() {
601
- return $this->stream->readLongUTF();
602
- }
603
-
604
- public function readXML() {
605
- return $this->stream->readLongUTF();
606
- }
607
-
608
- public function readTypedObject() {
609
- $className = $this->stream->readUTF();
610
- return $this->readObject();
611
- }
612
- }
613
-
614
- class AVCSequenceParameterSetReader {
615
- public $sps;
616
- public $start = 0;
617
- public $currentBytes = 0;
618
- public $currentBits = 0;
619
- public $width;
620
- public $height;
621
-
622
- public function AVCSequenceParameterSetReader($sps) {
623
- $this->sps = $sps;
624
- }
625
-
626
- public function readData() {
627
- $this->skipBits(8);
628
- $this->skipBits(8);
629
- $profile = $this->getBits(8); // read profile
630
- $this->skipBits(16);
631
- $this->expGolombUe(); // read sps id
632
- if (in_array($profile, array(H264_PROFILE_HIGH, H264_PROFILE_HIGH10, H264_PROFILE_HIGH422, H264_PROFILE_HIGH444, H264_PROFILE_HIGH444_PREDICTIVE))) {
633
- if ($this->expGolombUe() == 3) {
634
- $this->skipBits(1);
635
- }
636
- $this->expGolombUe();
637
- $this->expGolombUe();
638
- $this->skipBits(1);
639
- if ($this->getBit()) {
640
- for ($i = 0; $i < 8; $i++) {
641
- if ($this->getBit()) {
642
- $size = $i < 6 ? 16 : 64;
643
- $lastScale = 8;
644
- $nextScale = 8;
645
- for ($j = 0; $j < $size; $j++) {
646
- if ($nextScale != 0) {
647
- $deltaScale = $this->expGolombUe();
648
- $nextScale = ($lastScale + $deltaScale + 256) % 256;
649
- }
650
- if ($nextScale != 0) {
651
- $lastScale = $nextScale;
652
- }
653
- }
654
- }
655
- }
656
- }
657
- }
658
- $this->expGolombUe();
659
- $pocType = $this->expGolombUe();
660
- if ($pocType == 0) {
661
- $this->expGolombUe();
662
- } elseif ($pocType == 1) {
663
- $this->skipBits(1);
664
- $this->expGolombSe();
665
- $this->expGolombSe();
666
- $pocCycleLength = $this->expGolombUe();
667
- for ($i = 0; $i < $pocCycleLength; $i++) {
668
- $this->expGolombSe();
669
- }
670
- }
671
- $this->expGolombUe();
672
- $this->skipBits(1);
673
- $this->width = ($this->expGolombUe() + 1) * 16;
674
- $heightMap = $this->expGolombUe() + 1;
675
- $this->height = (2 - $this->getBit()) * $heightMap * 16;
676
- }
677
-
678
- public function skipBits($bits) {
679
- $newBits = $this->currentBits + $bits;
680
- $this->currentBytes += (int)floor($newBits / 8);
681
- $this->currentBits = $newBits % 8;
682
- }
683
-
684
- public function getBit() {
685
- $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
686
- $this->skipBits(1);
687
- return $result;
688
- }
689
-
690
- public function getBits($bits) {
691
- $result = 0;
692
- for ($i = 0; $i < $bits; $i++) {
693
- $result = ($result << 1) + $this->getBit();
694
- }
695
- return $result;
696
- }
697
-
698
- public function expGolombUe() {
699
- $significantBits = 0;
700
- $bit = $this->getBit();
701
- while ($bit == 0) {
702
- $significantBits++;
703
- $bit = $this->getBit();
704
-
705
- if ($significantBits > 31) {
706
- // something is broken, this is an emergency escape to prevent infinite loops
707
- return 0;
708
- }
709
- }
710
- return (1 << $significantBits) + $this->getBits($significantBits) - 1;
711
- }
712
-
713
- public function expGolombSe() {
714
- $result = $this->expGolombUe();
715
- if (($result & 0x01) == 0) {
716
- return -($result >> 1);
717
- } else {
718
- return ($result + 1) >> 1;
719
- }
720
- }
721
-
722
- public function getWidth() {
723
- return $this->width;
724
- }
725
-
726
- public function getHeight() {
727
- return $this->height;
728
- }
729
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.matroska.php DELETED
@@ -1,1771 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio-video.matriska.php //
11
- // module for analyzing Matroska containers //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
18
- define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
19
- define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found <http://www.matroska.org/technical/specs/tagging/index.html>.
20
- define('EBML_ID_INFO', 0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
21
- define('EBML_ID_TRACKS', 0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described.
22
- define('EBML_ID_SEGMENT', 0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
23
- define('EBML_ID_ATTACHMENTS', 0x0941A469); // [19][41][A4][69] -- Contain attached files.
24
- define('EBML_ID_EBML', 0x0A45DFA3); // [1A][45][DF][A3] -- Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
25
- define('EBML_ID_CUES', 0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment.
26
- define('EBML_ID_CLUSTER', 0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure.
27
- define('EBML_ID_LANGUAGE', 0x02B59C); // [22][B5][9C] -- Specifies the language of the track in the Matroska languages form.
28
- define('EBML_ID_TRACKTIMECODESCALE', 0x03314F); // [23][31][4F] -- The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
29
- define('EBML_ID_DEFAULTDURATION', 0x03E383); // [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame.
30
- define('EBML_ID_CODECNAME', 0x058688); // [25][86][88] -- A human-readable string specifying the codec.
31
- define('EBML_ID_CODECDOWNLOADURL', 0x06B240); // [26][B2][40] -- A URL to download about the codec used.
32
- define('EBML_ID_TIMECODESCALE', 0x0AD7B1); // [2A][D7][B1] -- Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds).
33
- define('EBML_ID_COLOURSPACE', 0x0EB524); // [2E][B5][24] -- Same value as in AVI (32 bits).
34
- define('EBML_ID_GAMMAVALUE', 0x0FB523); // [2F][B5][23] -- Gamma Value.
35
- define('EBML_ID_CODECSETTINGS', 0x1A9697); // [3A][96][97] -- A string describing the encoding setting used.
36
- define('EBML_ID_CODECINFOURL', 0x1B4040); // [3B][40][40] -- A URL to find information about the codec used.
37
- define('EBML_ID_PREVFILENAME', 0x1C83AB); // [3C][83][AB] -- An escaped filename corresponding to the previous segment.
38
- define('EBML_ID_PREVUID', 0x1CB923); // [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits).
39
- define('EBML_ID_NEXTFILENAME', 0x1E83BB); // [3E][83][BB] -- An escaped filename corresponding to the next segment.
40
- define('EBML_ID_NEXTUID', 0x1EB923); // [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits).
41
- define('EBML_ID_CONTENTCOMPALGO', 0x0254); // [42][54] -- The compression algorithm used. Algorithms that have been specified so far are:
42
- define('EBML_ID_CONTENTCOMPSETTINGS', 0x0255); // [42][55] -- Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track.
43
- define('EBML_ID_DOCTYPE', 0x0282); // [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case).
44
- define('EBML_ID_DOCTYPEREADVERSION', 0x0285); // [42][85] -- The minimum DocType version an interpreter has to support to read this file.
45
- define('EBML_ID_EBMLVERSION', 0x0286); // [42][86] -- The version of EBML parser used to create the file.
46
- define('EBML_ID_DOCTYPEVERSION', 0x0287); // [42][87] -- The version of DocType interpreter used to create the file.
47
- define('EBML_ID_EBMLMAXIDLENGTH', 0x02F2); // [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska).
48
- define('EBML_ID_EBMLMAXSIZELENGTH', 0x02F3); // [42][F3] -- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
49
- define('EBML_ID_EBMLREADVERSION', 0x02F7); // [42][F7] -- The minimum EBML version a parser has to support to read this file.
50
- define('EBML_ID_CHAPLANGUAGE', 0x037C); // [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
51
- define('EBML_ID_CHAPCOUNTRY', 0x037E); // [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains.
52
- define('EBML_ID_SEGMENTFAMILY', 0x0444); // [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits).
53
- define('EBML_ID_DATEUTC', 0x0461); // [44][61] -- Date of the origin of timecode (value 0), i.e. production date.
54
- define('EBML_ID_TAGLANGUAGE', 0x047A); // [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form.
55
- define('EBML_ID_TAGDEFAULT', 0x0484); // [44][84] -- Indication to know if this is the default/original language to use for the given tag.
56
- define('EBML_ID_TAGBINARY', 0x0485); // [44][85] -- The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.
57
- define('EBML_ID_TAGSTRING', 0x0487); // [44][87] -- The value of the Tag.
58
- define('EBML_ID_DURATION', 0x0489); // [44][89] -- Duration of the segment (based on TimecodeScale).
59
- define('EBML_ID_CHAPPROCESSPRIVATE', 0x050D); // [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent.
60
- define('EBML_ID_CHAPTERFLAGENABLED', 0x0598); // [45][98] -- Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter.
61
- define('EBML_ID_TAGNAME', 0x05A3); // [45][A3] -- The name of the Tag that is going to be stored.
62
- define('EBML_ID_EDITIONENTRY', 0x05B9); // [45][B9] -- Contains all information about a segment edition.
63
- define('EBML_ID_EDITIONUID', 0x05BC); // [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition.
64
- define('EBML_ID_EDITIONFLAGHIDDEN', 0x05BD); // [45][BD] -- If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks).
65
- define('EBML_ID_EDITIONFLAGDEFAULT', 0x05DB); // [45][DB] -- If a flag is set (1) the edition should be used as the default one.
66
- define('EBML_ID_EDITIONFLAGORDERED', 0x05DD); // [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced.
67
- define('EBML_ID_FILEDATA', 0x065C); // [46][5C] -- The data of the file.
68
- define('EBML_ID_FILEMIMETYPE', 0x0660); // [46][60] -- MIME type of the file.
69
- define('EBML_ID_FILENAME', 0x066E); // [46][6E] -- Filename of the attached file.
70
- define('EBML_ID_FILEREFERRAL', 0x0675); // [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
71
- define('EBML_ID_FILEDESCRIPTION', 0x067E); // [46][7E] -- A human-friendly name for the attached file.
72
- define('EBML_ID_FILEUID', 0x06AE); // [46][AE] -- Unique ID representing the file, as random as possible.
73
- define('EBML_ID_CONTENTENCALGO', 0x07E1); // [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
74
- define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
75
- define('EBML_ID_CONTENTSIGNATURE', 0x07E3); // [47][E3] -- A cryptographic signature of the contents.
76
- define('EBML_ID_CONTENTSIGKEYID', 0x07E4); // [47][E4] -- This is the ID of the private key the data was signed with.
77
- define('EBML_ID_CONTENTSIGALGO', 0x07E5); // [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
78
- define('EBML_ID_CONTENTSIGHASHALGO', 0x07E6); // [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
79
- define('EBML_ID_MUXINGAPP', 0x0D80); // [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
80
- define('EBML_ID_SEEK', 0x0DBB); // [4D][BB] -- Contains a single seek entry to an EBML element.
81
- define('EBML_ID_CONTENTENCODINGORDER', 0x1031); // [50][31] -- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.
82
- define('EBML_ID_CONTENTENCODINGSCOPE', 0x1032); // [50][32] -- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
83
- define('EBML_ID_CONTENTENCODINGTYPE', 0x1033); // [50][33] -- A value describing what kind of transformation has been done. Possible values:
84
- define('EBML_ID_CONTENTCOMPRESSION', 0x1034); // [50][34] -- Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
85
- define('EBML_ID_CONTENTENCRYPTION', 0x1035); // [50][35] -- Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
86
- define('EBML_ID_CUEREFNUMBER', 0x135F); // [53][5F] -- Number of the referenced Block of Track X in the specified Cluster.
87
- define('EBML_ID_NAME', 0x136E); // [53][6E] -- A human-readable track name.
88
- define('EBML_ID_CUEBLOCKNUMBER', 0x1378); // [53][78] -- Number of the Block in the specified Cluster.
89
- define('EBML_ID_TRACKOFFSET', 0x137F); // [53][7F] -- A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track.
90
- define('EBML_ID_SEEKID', 0x13AB); // [53][AB] -- The binary ID corresponding to the element name.
91
- define('EBML_ID_SEEKPOSITION', 0x13AC); // [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
92
- define('EBML_ID_STEREOMODE', 0x13B8); // [53][B8] -- Stereo-3D video mode.
93
- define('EBML_ID_OLDSTEREOMODE', 0x13B9); // [53][B9] -- Bogus StereoMode value used in old versions of libmatroska. DO NOT USE. (0: mono, 1: right eye, 2: left eye, 3: both eyes).
94
- define('EBML_ID_PIXELCROPBOTTOM', 0x14AA); // [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
95
- define('EBML_ID_DISPLAYWIDTH', 0x14B0); // [54][B0] -- Width of the video frames to display.
96
- define('EBML_ID_DISPLAYUNIT', 0x14B2); // [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
97
- define('EBML_ID_ASPECTRATIOTYPE', 0x14B3); // [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
98
- define('EBML_ID_DISPLAYHEIGHT', 0x14BA); // [54][BA] -- Height of the video frames to display.
99
- define('EBML_ID_PIXELCROPTOP', 0x14BB); // [54][BB] -- The number of video pixels to remove at the top of the image.
100
- define('EBML_ID_PIXELCROPLEFT', 0x14CC); // [54][CC] -- The number of video pixels to remove on the left of the image.
101
- define('EBML_ID_PIXELCROPRIGHT', 0x14DD); // [54][DD] -- The number of video pixels to remove on the right of the image.
102
- define('EBML_ID_FLAGFORCED', 0x15AA); // [55][AA] -- Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind.
103
- define('EBML_ID_MAXBLOCKADDITIONID', 0x15EE); // [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
104
- define('EBML_ID_WRITINGAPP', 0x1741); // [57][41] -- Writing application ("mkvmerge-0.3.3").
105
- define('EBML_ID_CLUSTERSILENTTRACKS', 0x1854); // [58][54] -- The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
106
- define('EBML_ID_CLUSTERSILENTTRACKNUMBER', 0x18D7); // [58][D7] -- One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.
107
- define('EBML_ID_ATTACHEDFILE', 0x21A7); // [61][A7] -- An attached file.
108
- define('EBML_ID_CONTENTENCODING', 0x2240); // [62][40] -- Settings for one content encoding like compression or encryption.
109
- define('EBML_ID_BITDEPTH', 0x2264); // [62][64] -- Bits per sample, mostly used for PCM.
110
- define('EBML_ID_CODECPRIVATE', 0x23A2); // [63][A2] -- Private data only known to the codec.
111
- define('EBML_ID_TARGETS', 0x23C0); // [63][C0] -- Contain all UIDs where the specified meta data apply. It is void to describe everything in the segment.
112
- define('EBML_ID_CHAPTERPHYSICALEQUIV', 0x23C3); // [63][C3] -- Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values.
113
- define('EBML_ID_TAGCHAPTERUID', 0x23C4); // [63][C4] -- A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment.
114
- define('EBML_ID_TAGTRACKUID', 0x23C5); // [63][C5] -- A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.
115
- define('EBML_ID_TAGATTACHMENTUID', 0x23C6); // [63][C6] -- A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.
116
- define('EBML_ID_TAGEDITIONUID', 0x23C9); // [63][C9] -- A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment.
117
- define('EBML_ID_TARGETTYPE', 0x23CA); // [63][CA] -- An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType).
118
- define('EBML_ID_TRACKTRANSLATE', 0x2624); // [66][24] -- The track identification for the given Chapter Codec.
119
- define('EBML_ID_TRACKTRANSLATETRACKID', 0x26A5); // [66][A5] -- The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used.
120
- define('EBML_ID_TRACKTRANSLATECODEC', 0x26BF); // [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
121
- define('EBML_ID_TRACKTRANSLATEEDITIONUID', 0x26FC); // [66][FC] -- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.
122
- define('EBML_ID_SIMPLETAG', 0x27C8); // [67][C8] -- Contains general information about the target.
123
- define('EBML_ID_TARGETTYPEVALUE', 0x28CA); // [68][CA] -- A number to indicate the logical level of the target (see TargetType).
124
- define('EBML_ID_CHAPPROCESSCOMMAND', 0x2911); // [69][11] -- Contains all the commands associated to the Atom.
125
- define('EBML_ID_CHAPPROCESSTIME', 0x2922); // [69][22] -- Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).
126
- define('EBML_ID_CHAPTERTRANSLATE', 0x2924); // [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment.
127
- define('EBML_ID_CHAPPROCESSDATA', 0x2933); // [69][33] -- Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands.
128
- define('EBML_ID_CHAPPROCESS', 0x2944); // [69][44] -- Contains all the commands associated to the Atom.
129
- define('EBML_ID_CHAPPROCESSCODECID', 0x2955); // [69][55] -- Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later.
130
- define('EBML_ID_CHAPTERTRANSLATEID', 0x29A5); // [69][A5] -- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
131
- define('EBML_ID_CHAPTERTRANSLATECODEC', 0x29BF); // [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
132
- define('EBML_ID_CHAPTERTRANSLATEEDITIONUID', 0x29FC); // [69][FC] -- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.
133
- define('EBML_ID_CONTENTENCODINGS', 0x2D80); // [6D][80] -- Settings for several content encoding mechanisms like compression or encryption.
134
- define('EBML_ID_MINCACHE', 0x2DE7); // [6D][E7] -- The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.
135
- define('EBML_ID_MAXCACHE', 0x2DF8); // [6D][F8] -- The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.
136
- define('EBML_ID_CHAPTERSEGMENTUID', 0x2E67); // [6E][67] -- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.
137
- define('EBML_ID_CHAPTERSEGMENTEDITIONUID', 0x2EBC); // [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID.
138
- define('EBML_ID_TRACKOVERLAY', 0x2FAB); // [6F][AB] -- Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc.
139
- define('EBML_ID_TAG', 0x3373); // [73][73] -- Element containing elements specific to Tracks/Chapters.
140
- define('EBML_ID_SEGMENTFILENAME', 0x3384); // [73][84] -- A filename corresponding to this segment.
141
- define('EBML_ID_SEGMENTUID', 0x33A4); // [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits).
142
- define('EBML_ID_CHAPTERUID', 0x33C4); // [73][C4] -- A unique ID to identify the Chapter.
143
- define('EBML_ID_TRACKUID', 0x33C5); // [73][C5] -- A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.
144
- define('EBML_ID_ATTACHMENTLINK', 0x3446); // [74][46] -- The UID of an attachment that is used by this codec.
145
- define('EBML_ID_CLUSTERBLOCKADDITIONS', 0x35A1); // [75][A1] -- Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data.
146
- define('EBML_ID_CHANNELPOSITIONS', 0x347B); // [7D][7B] -- Table of horizontal angles for each successive channel, see appendix.
147
- define('EBML_ID_OUTPUTSAMPLINGFREQUENCY', 0x38B5); // [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques).
148
- define('EBML_ID_TITLE', 0x3BA9); // [7B][A9] -- General name of the segment.
149
- define('EBML_ID_CHAPTERDISPLAY', 0x00); // [80] -- Contains all possible strings to use for the chapter display.
150
- define('EBML_ID_TRACKTYPE', 0x03); // [83] -- A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
151
- define('EBML_ID_CHAPSTRING', 0x05); // [85] -- Contains the string to use as the chapter atom.
152
- define('EBML_ID_CODECID', 0x06); // [86] -- An ID corresponding to the codec, see the codec page for more info.
153
- define('EBML_ID_FLAGDEFAULT', 0x08); // [88] -- Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference.
154
- define('EBML_ID_CHAPTERTRACKNUMBER', 0x09); // [89] -- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.
155
- define('EBML_ID_CLUSTERSLICES', 0x0E); // [8E] -- Contains slices description.
156
- define('EBML_ID_CHAPTERTRACK', 0x0F); // [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply
157
- define('EBML_ID_CHAPTERTIMESTART', 0x11); // [91] -- Timecode of the start of Chapter (not scaled).
158
- define('EBML_ID_CHAPTERTIMEEND', 0x12); // [92] -- Timecode of the end of Chapter (timecode excluded, not scaled).
159
- define('EBML_ID_CUEREFTIME', 0x16); // [96] -- Timecode of the referenced Block.
160
- define('EBML_ID_CUEREFCLUSTER', 0x17); // [97] -- Position of the Cluster containing the referenced Block.
161
- define('EBML_ID_CHAPTERFLAGHIDDEN', 0x18); // [98] -- If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks).
162
- define('EBML_ID_FLAGINTERLACED', 0x1A); // [9A] -- Set if the video is interlaced.
163
- define('EBML_ID_CLUSTERBLOCKDURATION', 0x1B); // [9B] -- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track. When not written and with no DefaultDuration, the value is assumed to be the difference between the timecode of this Block and the timecode of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks.
164
- define('EBML_ID_FLAGLACING', 0x1C); // [9C] -- Set if the track may contain blocks using lacing.
165
- define('EBML_ID_CHANNELS', 0x1F); // [9F] -- Numbers of channels in the track.
166
- define('EBML_ID_CLUSTERBLOCKGROUP', 0x20); // [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
167
- define('EBML_ID_CLUSTERBLOCK', 0x21); // [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode.
168
- define('EBML_ID_CLUSTERBLOCKVIRTUAL', 0x22); // [A2] -- A Block with no data. It must be stored in the stream at the place the real Block should be in display order.
169
- define('EBML_ID_CLUSTERSIMPLEBLOCK', 0x23); // [A3] -- Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed.
170
- define('EBML_ID_CLUSTERCODECSTATE', 0x24); // [A4] -- The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry.
171
- define('EBML_ID_CLUSTERBLOCKADDITIONAL', 0x25); // [A5] -- Interpreted by the codec as it wishes (using the BlockAddID).
172
- define('EBML_ID_CLUSTERBLOCKMORE', 0x26); // [A6] -- Contain the BlockAdditional and some parameters.
173
- define('EBML_ID_CLUSTERPOSITION', 0x27); // [A7] -- Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
174
- define('EBML_ID_CODECDECODEALL', 0x2A); // [AA] -- The codec can decode potentially damaged data.
175
- define('EBML_ID_CLUSTERPREVSIZE', 0x2B); // [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing.
176
- define('EBML_ID_TRACKENTRY', 0x2E); // [AE] -- Describes a track with all elements.
177
- define('EBML_ID_CLUSTERENCRYPTEDBLOCK', 0x2F); // [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed).
178
- define('EBML_ID_PIXELWIDTH', 0x30); // [B0] -- Width of the encoded video frames in pixels.
179
- define('EBML_ID_CUETIME', 0x33); // [B3] -- Absolute timecode according to the segment time base.
180
- define('EBML_ID_SAMPLINGFREQUENCY', 0x35); // [B5] -- Sampling frequency in Hz.
181
- define('EBML_ID_CHAPTERATOM', 0x36); // [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks).
182
- define('EBML_ID_CUETRACKPOSITIONS', 0x37); // [B7] -- Contain positions for different tracks corresponding to the timecode.
183
- define('EBML_ID_FLAGENABLED', 0x39); // [B9] -- Set if the track is used.
184
- define('EBML_ID_PIXELHEIGHT', 0x3A); // [BA] -- Height of the encoded video frames in pixels.
185
- define('EBML_ID_CUEPOINT', 0x3B); // [BB] -- Contains all information relative to a seek point in the segment.
186
- define('EBML_ID_CRC32', 0x3F); // [BF] -- The CRC is computed on all the data of the Master element it's in, regardless of its position. It's recommended to put the CRC value at the beggining of the Master element for easier reading. All level 1 elements should include a CRC-32.
187
- define('EBML_ID_CLUSTERBLOCKADDITIONID', 0x4B); // [CB] -- The ID of the BlockAdditional element (0 is the main Block).
188
- define('EBML_ID_CLUSTERLACENUMBER', 0x4C); // [CC] -- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
189
- define('EBML_ID_CLUSTERFRAMENUMBER', 0x4D); // [CD] -- The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame).
190
- define('EBML_ID_CLUSTERDELAY', 0x4E); // [CE] -- The (scaled) delay to apply to the element.
191
- define('EBML_ID_CLUSTERDURATION', 0x4F); // [CF] -- The (scaled) duration to apply to the element.
192
- define('EBML_ID_TRACKNUMBER', 0x57); // [D7] -- The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).
193
- define('EBML_ID_CUEREFERENCE', 0x5B); // [DB] -- The Clusters containing the required referenced Blocks.
194
- define('EBML_ID_VIDEO', 0x60); // [E0] -- Video settings.
195
- define('EBML_ID_AUDIO', 0x61); // [E1] -- Audio settings.
196
- define('EBML_ID_CLUSTERTIMESLICE', 0x68); // [E8] -- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
197
- define('EBML_ID_CUECODECSTATE', 0x6A); // [EA] -- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.
198
- define('EBML_ID_CUEREFCODECSTATE', 0x6B); // [EB] -- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.
199
- define('EBML_ID_VOID', 0x6C); // [EC] -- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
200
- define('EBML_ID_CLUSTERTIMECODE', 0x67); // [E7] -- Absolute timecode of the cluster (based on TimecodeScale).
201
- define('EBML_ID_CLUSTERBLOCKADDID', 0x6E); // [EE] -- An ID to identify the BlockAdditional level.
202
- define('EBML_ID_CUECLUSTERPOSITION', 0x71); // [F1] -- The position of the Cluster containing the required Block.
203
- define('EBML_ID_CUETRACK', 0x77); // [F7] -- The track for which a position is given.
204
- define('EBML_ID_CLUSTERREFERENCEPRIORITY', 0x7A); // [FA] -- This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced.
205
- define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] -- Timecode of another frame used as a reference (ie: B or P frame). The timecode is relative to the block it's attached to.
206
- define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block.
207
-
208
-
209
- /**
210
- * @tutorial http://www.matroska.org/technical/specs/index.html
211
- *
212
- * @todo Rewrite EBML parser to reduce it's size and honor default element values
213
- * @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection
214
- */
215
- class getid3_matroska extends getid3_handler
216
- {
217
- // public options
218
- public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE]
219
- public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE]
220
-
221
- // private parser settings/placeholders
222
- private $EBMLbuffer = '';
223
- private $EBMLbuffer_offset = 0;
224
- private $EBMLbuffer_length = 0;
225
- private $current_offset = 0;
226
- private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID);
227
-
228
- public function Analyze()
229
- {
230
- $info = &$this->getid3->info;
231
-
232
- // parse container
233
- try {
234
- $this->parseEBML($info);
235
- } catch (Exception $e) {
236
- $info['error'][] = 'EBML parser: '.$e->getMessage();
237
- }
238
-
239
- // calculate playtime
240
- if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
241
- foreach ($info['matroska']['info'] as $key => $infoarray) {
242
- if (isset($infoarray['Duration'])) {
243
- // TimecodeScale is how many nanoseconds each Duration unit is
244
- $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
245
- break;
246
- }
247
- }
248
- }
249
-
250
- // extract tags
251
- if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) {
252
- foreach ($info['matroska']['tags'] as $key => $infoarray) {
253
- $this->ExtractCommentsSimpleTag($infoarray);
254
- }
255
- }
256
-
257
- // process tracks
258
- if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
259
- foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) {
260
-
261
- $track_info = array();
262
- $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']);
263
- $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true);
264
- if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; }
265
-
266
- switch ($trackarray['TrackType']) {
267
-
268
- case 1: // Video
269
- $track_info['resolution_x'] = $trackarray['PixelWidth'];
270
- $track_info['resolution_y'] = $trackarray['PixelHeight'];
271
- $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0);
272
- $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']);
273
- $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']);
274
-
275
- if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; }
276
- if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; }
277
- if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; }
278
- if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; }
279
- if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); }
280
- if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
281
-
282
- switch ($trackarray['CodecID']) {
283
- case 'V_MS/VFW/FOURCC':
284
- if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
285
- $this->warning('Unable to parse codec private data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
286
- break;
287
- }
288
- $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']);
289
- $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']);
290
- $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
291
- break;
292
-
293
- /*case 'V_MPEG4/ISO/AVC':
294
- $h264['profile'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1));
295
- $h264['level'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1));
296
- $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1));
297
- $h264['NALUlength'] = ($rn & 3) + 1;
298
- $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1));
299
- $nsps = ($rn & 31);
300
- $offset = 6;
301
- for ($i = 0; $i < $nsps; $i ++) {
302
- $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
303
- $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
304
- $offset += 2 + $length;
305
- }
306
- $npps = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1));
307
- $offset += 1;
308
- for ($i = 0; $i < $npps; $i ++) {
309
- $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2));
310
- $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length);
311
- $offset += 2 + $length;
312
- }
313
- $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264;
314
- break;*/
315
- }
316
-
317
- $info['video']['streams'][] = $track_info;
318
- break;
319
-
320
- case 2: // Audio
321
- $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0);
322
- $track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1);
323
- $track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng');
324
- if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; }
325
- if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; }
326
-
327
- switch ($trackarray['CodecID']) {
328
- case 'A_PCM/INT/LIT':
329
- case 'A_PCM/INT/BIG':
330
- $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
331
- break;
332
-
333
- case 'A_AC3':
334
- case 'A_DTS':
335
- case 'A_MPEG/L3':
336
- case 'A_MPEG/L2':
337
- case 'A_FLAC':
338
- if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, false)) {
339
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.'.$track_info['dataformat'].'.php"');
340
- break;
341
- }
342
-
343
- if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) {
344
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set');
345
- break;
346
- }
347
-
348
- // create temp instance
349
- $getid3_temp = new getID3();
350
- if ($track_info['dataformat'] != 'flac') {
351
- $getid3_temp->openfile($this->getid3->filename);
352
- }
353
- $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
354
- if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
355
- $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length'];
356
- }
357
-
358
- // analyze
359
- $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']);
360
- $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat'];
361
- $getid3_audio = new $class($getid3_temp, __CLASS__);
362
- if ($track_info['dataformat'] == 'flac') {
363
- $getid3_audio->AnalyzeString($trackarray['CodecPrivate']);
364
- }
365
- else {
366
- $getid3_audio->Analyze();
367
- }
368
- if (!empty($getid3_temp->info[$header_data_key])) {
369
- $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key];
370
- if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
371
- foreach ($getid3_temp->info['audio'] as $key => $value) {
372
- $track_info[$key] = $value;
373
- }
374
- }
375
- }
376
- else {
377
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']);
378
- }
379
-
380
- // copy errors and warnings
381
- if (!empty($getid3_temp->info['error'])) {
382
- foreach ($getid3_temp->info['error'] as $newerror) {
383
- $this->warning($class.'() says: ['.$newerror.']');
384
- }
385
- }
386
- if (!empty($getid3_temp->info['warning'])) {
387
- foreach ($getid3_temp->info['warning'] as $newerror) {
388
- if ($track_info['dataformat'] == 'mp3' && preg_match('/^Probable truncated file: expecting \d+ bytes of audio data, only found \d+ \(short by \d+ bytes\)$/', $newerror)) {
389
- // LAME/Xing header is probably set, but audio data is chunked into Matroska file and near-impossible to verify if audio stream is complete, so ignore useless warning
390
- continue;
391
- }
392
- $this->warning($class.'() says: ['.$newerror.']');
393
- }
394
- }
395
- unset($getid3_temp, $getid3_audio);
396
- break;
397
-
398
- case 'A_AAC':
399
- case 'A_AAC/MPEG2/LC':
400
- case 'A_AAC/MPEG2/LC/SBR':
401
- case 'A_AAC/MPEG4/LC':
402
- case 'A_AAC/MPEG4/LC/SBR':
403
- $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated');
404
- break;
405
-
406
- case 'A_VORBIS':
407
- if (!isset($trackarray['CodecPrivate'])) {
408
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set');
409
- break;
410
- }
411
- $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1);
412
- if ($vorbis_offset === false) {
413
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword');
414
- break;
415
- }
416
- $vorbis_offset -= 1;
417
-
418
- if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) {
419
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio.ogg.php"');
420
- break;
421
- }
422
-
423
- // create temp instance
424
- $getid3_temp = new getID3();
425
-
426
- // analyze
427
- $getid3_ogg = new getid3_ogg($getid3_temp);
428
- $oggpageinfo['page_seqno'] = 0;
429
- $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo);
430
- if (!empty($getid3_temp->info['ogg'])) {
431
- $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg'];
432
- if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) {
433
- foreach ($getid3_temp->info['audio'] as $key => $value) {
434
- $track_info[$key] = $value;
435
- }
436
- }
437
- }
438
-
439
- // copy errors and warnings
440
- if (!empty($getid3_temp->info['error'])) {
441
- foreach ($getid3_temp->info['error'] as $newerror) {
442
- $this->warning('getid3_ogg() says: ['.$newerror.']');
443
- }
444
- }
445
- if (!empty($getid3_temp->info['warning'])) {
446
- foreach ($getid3_temp->info['warning'] as $newerror) {
447
- $this->warning('getid3_ogg() says: ['.$newerror.']');
448
- }
449
- }
450
-
451
- if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) {
452
- $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal'];
453
- }
454
- unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset);
455
- break;
456
-
457
- case 'A_MS/ACM':
458
- if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
459
- $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because cannot include "module.audio-video.riff.php"');
460
- break;
461
- }
462
-
463
- $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']);
464
- foreach ($parsed as $key => $value) {
465
- if ($key != 'raw') {
466
- $track_info[$key] = $value;
467
- }
468
- }
469
- $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed;
470
- break;
471
-
472
- default:
473
- $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"');
474
- }
475
-
476
- $info['audio']['streams'][] = $track_info;
477
- break;
478
- }
479
- }
480
-
481
- if (!empty($info['video']['streams'])) {
482
- $info['video'] = self::getDefaultStreamInfo($info['video']['streams']);
483
- }
484
- if (!empty($info['audio']['streams'])) {
485
- $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']);
486
- }
487
- }
488
-
489
- // process attachments
490
- if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) {
491
- foreach ($info['matroska']['attachments'] as $i => $entry) {
492
- if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) {
493
- $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']);
494
- }
495
- }
496
- }
497
-
498
- // determine mime type
499
- if (!empty($info['video']['streams'])) {
500
- $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska');
501
- } elseif (!empty($info['audio']['streams'])) {
502
- $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska');
503
- } elseif (isset($info['mime_type'])) {
504
- unset($info['mime_type']);
505
- }
506
-
507
- return true;
508
- }
509
-
510
- private function parseEBML(&$info) {
511
- // http://www.matroska.org/technical/specs/index.html#EBMLBasics
512
- $this->current_offset = $info['avdataoffset'];
513
-
514
- while ($this->getEBMLelement($top_element, $info['avdataend'])) {
515
- switch ($top_element['id']) {
516
-
517
- case EBML_ID_EBML:
518
- $info['fileformat'] = 'matroska';
519
- $info['matroska']['header']['offset'] = $top_element['offset'];
520
- $info['matroska']['header']['length'] = $top_element['length'];
521
-
522
- while ($this->getEBMLelement($element_data, $top_element['end'], true)) {
523
- switch ($element_data['id']) {
524
-
525
- case EBML_ID_EBMLVERSION:
526
- case EBML_ID_EBMLREADVERSION:
527
- case EBML_ID_EBMLMAXIDLENGTH:
528
- case EBML_ID_EBMLMAXSIZELENGTH:
529
- case EBML_ID_DOCTYPEVERSION:
530
- case EBML_ID_DOCTYPEREADVERSION:
531
- $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']);
532
- break;
533
-
534
- case EBML_ID_DOCTYPE:
535
- $element_data['data'] = getid3_lib::trimNullByte($element_data['data']);
536
- $info['matroska']['doctype'] = $element_data['data'];
537
- break;
538
-
539
- case EBML_ID_CRC32: // not useful, ignore
540
- $this->current_offset = $element_data['end'];
541
- unset($element_data);
542
- break;
543
-
544
- default:
545
- $this->unhandledElement('header', __LINE__, $element_data);
546
- }
547
- if (!empty($element_data)) {
548
- unset($element_data['offset'], $element_data['end']);
549
- $info['matroska']['header']['elements'][] = $element_data;
550
- }
551
- }
552
- break;
553
-
554
- case EBML_ID_SEGMENT:
555
- $info['matroska']['segment'][0]['offset'] = $top_element['offset'];
556
- $info['matroska']['segment'][0]['length'] = $top_element['length'];
557
-
558
- while ($this->getEBMLelement($element_data, $top_element['end'])) {
559
- if ($element_data['id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
560
- $info['matroska']['segments'][] = $element_data;
561
- }
562
- switch ($element_data['id']) {
563
-
564
- case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements.
565
-
566
- while ($this->getEBMLelement($seek_entry, $element_data['end'])) {
567
- switch ($seek_entry['id']) {
568
-
569
- case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
570
- while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) {
571
-
572
- switch ($sub_seek_entry['id']) {
573
-
574
- case EBML_ID_SEEKID:
575
- $seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']);
576
- $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']);
577
- break;
578
-
579
- case EBML_ID_SEEKPOSITION:
580
- $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']);
581
- break;
582
-
583
- default:
584
- $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); }
585
- }
586
-
587
- if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required
588
- $info['matroska']['seek'][] = $seek_entry;
589
- }
590
- break;
591
-
592
- default:
593
- $this->unhandledElement('seekhead', __LINE__, $seek_entry);
594
- }
595
- }
596
- break;
597
-
598
- case EBML_ID_TRACKS: // A top-level block of information with many tracks described.
599
- $info['matroska']['tracks'] = $element_data;
600
-
601
- while ($this->getEBMLelement($track_entry, $element_data['end'])) {
602
- switch ($track_entry['id']) {
603
-
604
- case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
605
-
606
- while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
607
- switch ($subelement['id']) {
608
-
609
- case EBML_ID_TRACKNUMBER:
610
- case EBML_ID_TRACKUID:
611
- case EBML_ID_TRACKTYPE:
612
- case EBML_ID_MINCACHE:
613
- case EBML_ID_MAXCACHE:
614
- case EBML_ID_MAXBLOCKADDITIONID:
615
- case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
616
- $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
617
- break;
618
-
619
- case EBML_ID_TRACKTIMECODESCALE:
620
- $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
621
- break;
622
-
623
- case EBML_ID_CODECID:
624
- case EBML_ID_LANGUAGE:
625
- case EBML_ID_NAME:
626
- case EBML_ID_CODECNAME:
627
- $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
628
- break;
629
-
630
- case EBML_ID_CODECPRIVATE:
631
- $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true);
632
- break;
633
-
634
- case EBML_ID_FLAGENABLED:
635
- case EBML_ID_FLAGDEFAULT:
636
- case EBML_ID_FLAGFORCED:
637
- case EBML_ID_FLAGLACING:
638
- case EBML_ID_CODECDECODEALL:
639
- $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']);
640
- break;
641
-
642
- case EBML_ID_VIDEO:
643
-
644
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
645
- switch ($sub_subelement['id']) {
646
-
647
- case EBML_ID_PIXELWIDTH:
648
- case EBML_ID_PIXELHEIGHT:
649
- case EBML_ID_PIXELCROPBOTTOM:
650
- case EBML_ID_PIXELCROPTOP:
651
- case EBML_ID_PIXELCROPLEFT:
652
- case EBML_ID_PIXELCROPRIGHT:
653
- case EBML_ID_DISPLAYWIDTH:
654
- case EBML_ID_DISPLAYHEIGHT:
655
- case EBML_ID_DISPLAYUNIT:
656
- case EBML_ID_ASPECTRATIOTYPE:
657
- case EBML_ID_STEREOMODE:
658
- case EBML_ID_OLDSTEREOMODE:
659
- $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
660
- break;
661
-
662
- case EBML_ID_FLAGINTERLACED:
663
- $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
664
- break;
665
-
666
- case EBML_ID_GAMMAVALUE:
667
- $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
668
- break;
669
-
670
- case EBML_ID_COLOURSPACE:
671
- $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
672
- break;
673
-
674
- default:
675
- $this->unhandledElement('track.video', __LINE__, $sub_subelement);
676
- }
677
- }
678
- break;
679
-
680
- case EBML_ID_AUDIO:
681
-
682
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
683
- switch ($sub_subelement['id']) {
684
-
685
- case EBML_ID_CHANNELS:
686
- case EBML_ID_BITDEPTH:
687
- $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
688
- break;
689
-
690
- case EBML_ID_SAMPLINGFREQUENCY:
691
- case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
692
- $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']);
693
- break;
694
-
695
- case EBML_ID_CHANNELPOSITIONS:
696
- $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
697
- break;
698
-
699
- default:
700
- $this->unhandledElement('track.audio', __LINE__, $sub_subelement);
701
- }
702
- }
703
- break;
704
-
705
- case EBML_ID_CONTENTENCODINGS:
706
-
707
- while ($this->getEBMLelement($sub_subelement, $subelement['end'])) {
708
- switch ($sub_subelement['id']) {
709
-
710
- case EBML_ID_CONTENTENCODING:
711
-
712
- while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) {
713
- switch ($sub_sub_subelement['id']) {
714
-
715
- case EBML_ID_CONTENTENCODINGORDER:
716
- case EBML_ID_CONTENTENCODINGSCOPE:
717
- case EBML_ID_CONTENTENCODINGTYPE:
718
- $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
719
- break;
720
-
721
- case EBML_ID_CONTENTCOMPRESSION:
722
-
723
- while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
724
- switch ($sub_sub_sub_subelement['id']) {
725
-
726
- case EBML_ID_CONTENTCOMPALGO:
727
- $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
728
- break;
729
-
730
- case EBML_ID_CONTENTCOMPSETTINGS:
731
- $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
732
- break;
733
-
734
- default:
735
- $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
736
- }
737
- }
738
- break;
739
-
740
- case EBML_ID_CONTENTENCRYPTION:
741
-
742
- while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
743
- switch ($sub_sub_sub_subelement['id']) {
744
-
745
- case EBML_ID_CONTENTENCALGO:
746
- case EBML_ID_CONTENTSIGALGO:
747
- case EBML_ID_CONTENTSIGHASHALGO:
748
- $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
749
- break;
750
-
751
- case EBML_ID_CONTENTENCKEYID:
752
- case EBML_ID_CONTENTSIGNATURE:
753
- case EBML_ID_CONTENTSIGKEYID:
754
- $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
755
- break;
756
-
757
- default:
758
- $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement);
759
- }
760
- }
761
- break;
762
-
763
- default:
764
- $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement);
765
- }
766
- }
767
- break;
768
-
769
- default:
770
- $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement);
771
- }
772
- }
773
- break;
774
-
775
- default:
776
- $this->unhandledElement('track', __LINE__, $subelement);
777
- }
778
- }
779
-
780
- $info['matroska']['tracks']['tracks'][] = $track_entry;
781
- break;
782
-
783
- default:
784
- $this->unhandledElement('tracks', __LINE__, $track_entry);
785
- }
786
- }
787
- break;
788
-
789
- case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file.
790
- $info_entry = array();
791
-
792
- while ($this->getEBMLelement($subelement, $element_data['end'], true)) {
793
- switch ($subelement['id']) {
794
-
795
- case EBML_ID_TIMECODESCALE:
796
- $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
797
- break;
798
-
799
- case EBML_ID_DURATION:
800
- $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']);
801
- break;
802
-
803
- case EBML_ID_DATEUTC:
804
- $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
805
- $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]);
806
- break;
807
-
808
- case EBML_ID_SEGMENTUID:
809
- case EBML_ID_PREVUID:
810
- case EBML_ID_NEXTUID:
811
- $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
812
- break;
813
-
814
- case EBML_ID_SEGMENTFAMILY:
815
- $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']);
816
- break;
817
-
818
- case EBML_ID_SEGMENTFILENAME:
819
- case EBML_ID_PREVFILENAME:
820
- case EBML_ID_NEXTFILENAME:
821
- case EBML_ID_TITLE:
822
- case EBML_ID_MUXINGAPP:
823
- case EBML_ID_WRITINGAPP:
824
- $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']);
825
- $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']];
826
- break;
827
-
828
- case EBML_ID_CHAPTERTRANSLATE:
829
- $chaptertranslate_entry = array();
830
-
831
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
832
- switch ($sub_subelement['id']) {
833
-
834
- case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
835
- $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']);
836
- break;
837
-
838
- case EBML_ID_CHAPTERTRANSLATECODEC:
839
- $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
840
- break;
841
-
842
- case EBML_ID_CHAPTERTRANSLATEID:
843
- $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
844
- break;
845
-
846
- default:
847
- $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement);
848
- }
849
- }
850
- $info_entry[$subelement['id_name']] = $chaptertranslate_entry;
851
- break;
852
-
853
- default:
854
- $this->unhandledElement('info', __LINE__, $subelement);
855
- }
856
- }
857
- $info['matroska']['info'][] = $info_entry;
858
- break;
859
-
860
- case EBML_ID_CUES: // A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non "live" streams.
861
- if (self::$hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway
862
- $this->current_offset = $element_data['end'];
863
- break;
864
- }
865
- $cues_entry = array();
866
-
867
- while ($this->getEBMLelement($subelement, $element_data['end'])) {
868
- switch ($subelement['id']) {
869
-
870
- case EBML_ID_CUEPOINT:
871
- $cuepoint_entry = array();
872
-
873
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) {
874
- switch ($sub_subelement['id']) {
875
-
876
- case EBML_ID_CUETRACKPOSITIONS:
877
- $cuetrackpositions_entry = array();
878
-
879
- while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
880
- switch ($sub_sub_subelement['id']) {
881
-
882
- case EBML_ID_CUETRACK:
883
- case EBML_ID_CUECLUSTERPOSITION:
884
- case EBML_ID_CUEBLOCKNUMBER:
885
- case EBML_ID_CUECODECSTATE:
886
- $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
887
- break;
888
-
889
- default:
890
- $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement);
891
- }
892
- }
893
- $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry;
894
- break;
895
-
896
- case EBML_ID_CUETIME:
897
- $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
898
- break;
899
-
900
- default:
901
- $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement);
902
- }
903
- }
904
- $cues_entry[] = $cuepoint_entry;
905
- break;
906
-
907
- default:
908
- $this->unhandledElement('cues', __LINE__, $subelement);
909
- }
910
- }
911
- $info['matroska']['cues'] = $cues_entry;
912
- break;
913
-
914
- case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters.
915
- $tags_entry = array();
916
-
917
- while ($this->getEBMLelement($subelement, $element_data['end'], false)) {
918
- switch ($subelement['id']) {
919
-
920
- case EBML_ID_TAG:
921
- $tag_entry = array();
922
-
923
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) {
924
- switch ($sub_subelement['id']) {
925
-
926
- case EBML_ID_TARGETS:
927
- $targets_entry = array();
928
-
929
- while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) {
930
- switch ($sub_sub_subelement['id']) {
931
-
932
- case EBML_ID_TARGETTYPEVALUE:
933
- $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
934
- $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]);
935
- break;
936
-
937
- case EBML_ID_TARGETTYPE:
938
- $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
939
- break;
940
-
941
- case EBML_ID_TAGTRACKUID:
942
- case EBML_ID_TAGEDITIONUID:
943
- case EBML_ID_TAGCHAPTERUID:
944
- case EBML_ID_TAGATTACHMENTUID:
945
- $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
946
- break;
947
-
948
- default:
949
- $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement);
950
- }
951
- }
952
- $tag_entry[$sub_subelement['id_name']] = $targets_entry;
953
- break;
954
-
955
- case EBML_ID_SIMPLETAG:
956
- $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']);
957
- break;
958
-
959
- default:
960
- $this->unhandledElement('tags.tag', __LINE__, $sub_subelement);
961
- }
962
- }
963
- $tags_entry[] = $tag_entry;
964
- break;
965
-
966
- default:
967
- $this->unhandledElement('tags', __LINE__, $subelement);
968
- }
969
- }
970
- $info['matroska']['tags'] = $tags_entry;
971
- break;
972
-
973
- case EBML_ID_ATTACHMENTS: // Contain attached files.
974
-
975
- while ($this->getEBMLelement($subelement, $element_data['end'])) {
976
- switch ($subelement['id']) {
977
-
978
- case EBML_ID_ATTACHEDFILE:
979
- $attachedfile_entry = array();
980
-
981
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) {
982
- switch ($sub_subelement['id']) {
983
-
984
- case EBML_ID_FILEDESCRIPTION:
985
- case EBML_ID_FILENAME:
986
- case EBML_ID_FILEMIMETYPE:
987
- $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data'];
988
- break;
989
-
990
- case EBML_ID_FILEDATA:
991
- $attachedfile_entry['data_offset'] = $this->current_offset;
992
- $attachedfile_entry['data_length'] = $sub_subelement['length'];
993
-
994
- $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment(
995
- $attachedfile_entry['FileName'],
996
- $attachedfile_entry['data_offset'],
997
- $attachedfile_entry['data_length']);
998
-
999
- $this->current_offset = $sub_subelement['end'];
1000
- break;
1001
-
1002
- case EBML_ID_FILEUID:
1003
- $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1004
- break;
1005
-
1006
- default:
1007
- $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement);
1008
- }
1009
- }
1010
- $info['matroska']['attachments'][] = $attachedfile_entry;
1011
- break;
1012
-
1013
- default:
1014
- $this->unhandledElement('attachments', __LINE__, $subelement);
1015
- }
1016
- }
1017
- break;
1018
-
1019
- case EBML_ID_CHAPTERS:
1020
-
1021
- while ($this->getEBMLelement($subelement, $element_data['end'])) {
1022
- switch ($subelement['id']) {
1023
-
1024
- case EBML_ID_EDITIONENTRY:
1025
- $editionentry_entry = array();
1026
-
1027
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) {
1028
- switch ($sub_subelement['id']) {
1029
-
1030
- case EBML_ID_EDITIONUID:
1031
- $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1032
- break;
1033
-
1034
- case EBML_ID_EDITIONFLAGHIDDEN:
1035
- case EBML_ID_EDITIONFLAGDEFAULT:
1036
- case EBML_ID_EDITIONFLAGORDERED:
1037
- $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']);
1038
- break;
1039
-
1040
- case EBML_ID_CHAPTERATOM:
1041
- $chapteratom_entry = array();
1042
-
1043
- while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) {
1044
- switch ($sub_sub_subelement['id']) {
1045
-
1046
- case EBML_ID_CHAPTERSEGMENTUID:
1047
- case EBML_ID_CHAPTERSEGMENTEDITIONUID:
1048
- $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data'];
1049
- break;
1050
-
1051
- case EBML_ID_CHAPTERFLAGENABLED:
1052
- case EBML_ID_CHAPTERFLAGHIDDEN:
1053
- $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1054
- break;
1055
-
1056
- case EBML_ID_CHAPTERUID:
1057
- case EBML_ID_CHAPTERTIMESTART:
1058
- case EBML_ID_CHAPTERTIMEEND:
1059
- $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
1060
- break;
1061
-
1062
- case EBML_ID_CHAPTERTRACK:
1063
- $chaptertrack_entry = array();
1064
-
1065
- while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1066
- switch ($sub_sub_sub_subelement['id']) {
1067
-
1068
- case EBML_ID_CHAPTERTRACKNUMBER:
1069
- $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']);
1070
- break;
1071
-
1072
- default:
1073
- $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement);
1074
- }
1075
- }
1076
- $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry;
1077
- break;
1078
-
1079
- case EBML_ID_CHAPTERDISPLAY:
1080
- $chapterdisplay_entry = array();
1081
-
1082
- while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) {
1083
- switch ($sub_sub_sub_subelement['id']) {
1084
-
1085
- case EBML_ID_CHAPSTRING:
1086
- case EBML_ID_CHAPLANGUAGE:
1087
- case EBML_ID_CHAPCOUNTRY:
1088
- $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data'];
1089
- break;
1090
-
1091
- default:
1092
- $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement);
1093
- }
1094
- }
1095
- $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry;
1096
- break;
1097
-
1098
- default:
1099
- $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement);
1100
- }
1101
- }
1102
- $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry;
1103
- break;
1104
-
1105
- default:
1106
- $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement);
1107
- }
1108
- }
1109
- $info['matroska']['chapters'][] = $editionentry_entry;
1110
- break;
1111
-
1112
- default:
1113
- $this->unhandledElement('chapters', __LINE__, $subelement);
1114
- }
1115
- }
1116
- break;
1117
-
1118
- case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure.
1119
- $cluster_entry = array();
1120
-
1121
- while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) {
1122
- switch ($subelement['id']) {
1123
-
1124
- case EBML_ID_CLUSTERTIMECODE:
1125
- case EBML_ID_CLUSTERPOSITION:
1126
- case EBML_ID_CLUSTERPREVSIZE:
1127
- $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']);
1128
- break;
1129
-
1130
- case EBML_ID_CLUSTERSILENTTRACKS:
1131
- $cluster_silent_tracks = array();
1132
-
1133
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) {
1134
- switch ($sub_subelement['id']) {
1135
-
1136
- case EBML_ID_CLUSTERSILENTTRACKNUMBER:
1137
- $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1138
- break;
1139
-
1140
- default:
1141
- $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement);
1142
- }
1143
- }
1144
- $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks;
1145
- break;
1146
-
1147
- case EBML_ID_CLUSTERBLOCKGROUP:
1148
- $cluster_block_group = array('offset' => $this->current_offset);
1149
-
1150
- while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) {
1151
- switch ($sub_subelement['id']) {
1152
-
1153
- case EBML_ID_CLUSTERBLOCK:
1154
- $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info);
1155
- break;
1156
-
1157
- case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
1158
- case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int
1159
- $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']);
1160
- break;
1161
-
1162
- case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int
1163
- $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true);
1164
- break;
1165
-
1166
- case EBML_ID_CLUSTERCODECSTATE:
1167
- $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']);
1168
- break;
1169
-
1170
- default:
1171
- $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement);
1172
- }
1173
- }
1174
- $cluster_entry[$subelement['id_name']][] = $cluster_block_group;
1175
- break;
1176
-
1177
- case EBML_ID_CLUSTERSIMPLEBLOCK:
1178
- $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info);
1179
- break;
1180
-
1181
- default:
1182
- $this->unhandledElement('cluster', __LINE__, $subelement);
1183
- }
1184
- $this->current_offset = $subelement['end'];
1185
- }
1186
- if (!self::$hide_clusters) {
1187
- $info['matroska']['cluster'][] = $cluster_entry;
1188
- }
1189
-
1190
- // check to see if all the data we need exists already, if so, break out of the loop
1191
- if (!self::$parse_whole_file) {
1192
- if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) {
1193
- if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) {
1194
- if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) {
1195
- return;
1196
- }
1197
- }
1198
- }
1199
- }
1200
- break;
1201
-
1202
- default:
1203
- $this->unhandledElement('segment', __LINE__, $element_data);
1204
- }
1205
- }
1206
- break;
1207
-
1208
- default:
1209
- $this->unhandledElement('root', __LINE__, $top_element);
1210
- }
1211
- }
1212
- }
1213
-
1214
- private function EnsureBufferHasEnoughData($min_data=1024) {
1215
- if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) {
1216
- $read_bytes = max($min_data, $this->getid3->fread_buffer_size());
1217
-
1218
- try {
1219
- $this->fseek($this->current_offset);
1220
- $this->EBMLbuffer_offset = $this->current_offset;
1221
- $this->EBMLbuffer = $this->fread($read_bytes);
1222
- $this->EBMLbuffer_length = strlen($this->EBMLbuffer);
1223
- } catch (getid3_exception $e) {
1224
- $this->warning('EBML parser: '.$e->getMessage());
1225
- return false;
1226
- }
1227
-
1228
- if ($this->EBMLbuffer_length == 0 && $this->feof()) {
1229
- return $this->error('EBML parser: ran out of file at offset '.$this->current_offset);
1230
- }
1231
- }
1232
- return true;
1233
- }
1234
-
1235
- private function readEBMLint() {
1236
- $actual_offset = $this->current_offset - $this->EBMLbuffer_offset;
1237
-
1238
- // get length of integer
1239
- $first_byte_int = ord($this->EBMLbuffer[$actual_offset]);
1240
- if (0x80 & $first_byte_int) {
1241
- $length = 1;
1242
- } elseif (0x40 & $first_byte_int) {
1243
- $length = 2;
1244
- } elseif (0x20 & $first_byte_int) {
1245
- $length = 3;
1246
- } elseif (0x10 & $first_byte_int) {
1247
- $length = 4;
1248
- } elseif (0x08 & $first_byte_int) {
1249
- $length = 5;
1250
- } elseif (0x04 & $first_byte_int) {
1251
- $length = 6;
1252
- } elseif (0x02 & $first_byte_int) {
1253
- $length = 7;
1254
- } elseif (0x01 & $first_byte_int) {
1255
- $length = 8;
1256
- } else {
1257
- throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset);
1258
- }
1259
-
1260
- // read
1261
- $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length));
1262
- $this->current_offset += $length;
1263
-
1264
- return $int_value;
1265
- }
1266
-
1267
- private function readEBMLelementData($length, $check_buffer=false) {
1268
- if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) {
1269
- return false;
1270
- }
1271
- $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length);
1272
- $this->current_offset += $length;
1273
- return $data;
1274
- }
1275
-
1276
- private function getEBMLelement(&$element, $parent_end, $get_data=false) {
1277
- if ($this->current_offset >= $parent_end) {
1278
- return false;
1279
- }
1280
-
1281
- if (!$this->EnsureBufferHasEnoughData()) {
1282
- $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information
1283
- return false;
1284
- }
1285
-
1286
- $element = array();
1287
-
1288
- // set offset
1289
- $element['offset'] = $this->current_offset;
1290
-
1291
- // get ID
1292
- $element['id'] = $this->readEBMLint();
1293
-
1294
- // get name
1295
- $element['id_name'] = self::EBMLidName($element['id']);
1296
-
1297
- // get length
1298
- $element['length'] = $this->readEBMLint();
1299
-
1300
- // get end offset
1301
- $element['end'] = $this->current_offset + $element['length'];
1302
-
1303
- // get raw data
1304
- $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id']));
1305
- if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) {
1306
- $element['data'] = $this->readEBMLelementData($element['length'], $element);
1307
- }
1308
-
1309
- return true;
1310
- }
1311
-
1312
- private function unhandledElement($type, $line, $element) {
1313
- // warn only about unknown and missed elements, not about unuseful
1314
- if (!in_array($element['id'], $this->unuseful_elements)) {
1315
- $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']);
1316
- }
1317
-
1318
- // increase offset for unparsed elements
1319
- if (!isset($element['data'])) {
1320
- $this->current_offset = $element['end'];
1321
- }
1322
- }
1323
-
1324
- private function ExtractCommentsSimpleTag($SimpleTagArray) {
1325
- if (!empty($SimpleTagArray['SimpleTag'])) {
1326
- foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) {
1327
- if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) {
1328
- $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString'];
1329
- }
1330
- if (!empty($SimpleTagData['SimpleTag'])) {
1331
- $this->ExtractCommentsSimpleTag($SimpleTagData);
1332
- }
1333
- }
1334
- }
1335
-
1336
- return true;
1337
- }
1338
-
1339
- private function HandleEMBLSimpleTag($parent_end) {
1340
- $simpletag_entry = array();
1341
-
1342
- while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) {
1343
- switch ($element['id']) {
1344
-
1345
- case EBML_ID_TAGNAME:
1346
- case EBML_ID_TAGLANGUAGE:
1347
- case EBML_ID_TAGSTRING:
1348
- case EBML_ID_TAGBINARY:
1349
- $simpletag_entry[$element['id_name']] = $element['data'];
1350
- break;
1351
-
1352
- case EBML_ID_SIMPLETAG:
1353
- $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']);
1354
- break;
1355
-
1356
- case EBML_ID_TAGDEFAULT:
1357
- $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']);
1358
- break;
1359
-
1360
- default:
1361
- $this->unhandledElement('tag.simpletag', __LINE__, $element);
1362
- }
1363
- }
1364
-
1365
- return $simpletag_entry;
1366
- }
1367
-
1368
- private function HandleEMBLClusterBlock($element, $block_type, &$info) {
1369
- // http://www.matroska.org/technical/specs/index.html#block_structure
1370
- // http://www.matroska.org/technical/specs/index.html#simpleblock_structure
1371
-
1372
- $block_data = array();
1373
- $block_data['tracknumber'] = $this->readEBMLint();
1374
- $block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true);
1375
- $block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1376
-
1377
- if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1378
- $block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7);
1379
- //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4);
1380
- }
1381
- else {
1382
- //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4);
1383
- }
1384
- $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3);
1385
- $block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing
1386
- if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) {
1387
- $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01));
1388
- }
1389
- else {
1390
- //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0);
1391
- }
1392
- $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']);
1393
-
1394
- // Lace (when lacing bit is set)
1395
- if ($block_data['flags']['lacing'] > 0) {
1396
- $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8)
1397
- if ($block_data['flags']['lacing'] != 0x02) {
1398
- for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
1399
- if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing
1400
- $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing.
1401
- }
1402
- else { // Xiph lacing
1403
- $block_data['lace_frames_size'][$i] = 0;
1404
- do {
1405
- $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1));
1406
- $block_data['lace_frames_size'][$i] += $size;
1407
- }
1408
- while ($size == 255);
1409
- }
1410
- }
1411
- if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly
1412
- $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']);
1413
- }
1414
- }
1415
- }
1416
-
1417
- if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) {
1418
- $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset;
1419
- $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset;
1420
- //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0;
1421
- }
1422
- //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'];
1423
- //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration'] = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000);
1424
-
1425
- // set offset manually
1426
- $this->current_offset = $element['end'];
1427
-
1428
- return $block_data;
1429
- }
1430
-
1431
- private static function EBML2Int($EBMLstring) {
1432
- // http://matroska.org/specs/
1433
-
1434
- // Element ID coded with an UTF-8 like system:
1435
- // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X)
1436
- // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
1437
- // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
1438
- // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
1439
- // Values with all x at 0 and 1 are reserved (hence the -2).
1440
-
1441
- // Data size, in octets, is also coded with an UTF-8 like system :
1442
- // 1xxx xxxx - value 0 to 2^7-2
1443
- // 01xx xxxx xxxx xxxx - value 0 to 2^14-2
1444
- // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2
1445
- // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2
1446
- // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2
1447
- // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2
1448
- // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2
1449
- // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2
1450
-
1451
- $first_byte_int = ord($EBMLstring[0]);
1452
- if (0x80 & $first_byte_int) {
1453
- $EBMLstring[0] = chr($first_byte_int & 0x7F);
1454
- } elseif (0x40 & $first_byte_int) {
1455
- $EBMLstring[0] = chr($first_byte_int & 0x3F);
1456
- } elseif (0x20 & $first_byte_int) {
1457
- $EBMLstring[0] = chr($first_byte_int & 0x1F);
1458
- } elseif (0x10 & $first_byte_int) {
1459
- $EBMLstring[0] = chr($first_byte_int & 0x0F);
1460
- } elseif (0x08 & $first_byte_int) {
1461
- $EBMLstring[0] = chr($first_byte_int & 0x07);
1462
- } elseif (0x04 & $first_byte_int) {
1463
- $EBMLstring[0] = chr($first_byte_int & 0x03);
1464
- } elseif (0x02 & $first_byte_int) {
1465
- $EBMLstring[0] = chr($first_byte_int & 0x01);
1466
- } elseif (0x01 & $first_byte_int) {
1467
- $EBMLstring[0] = chr($first_byte_int & 0x00);
1468
- }
1469
-
1470
- return getid3_lib::BigEndian2Int($EBMLstring);
1471
- }
1472
-
1473
- private static function EBMLdate2unix($EBMLdatestamp) {
1474
- // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
1475
- // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
1476
- return round(($EBMLdatestamp / 1000000000) + 978307200);
1477
- }
1478
-
1479
- public static function TargetTypeValue($target_type) {
1480
- // http://www.matroska.org/technical/specs/tagging/index.html
1481
- static $TargetTypeValue = array();
1482
- if (empty($TargetTypeValue)) {
1483
- $TargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies
1484
- $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement)
1485
- $TargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie
1486
- $TargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts
1487
- $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series)
1488
- $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together
1489
- $TargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items
1490
- }
1491
- return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type);
1492
- }
1493
-
1494
- public static function BlockLacingType($lacingtype) {
1495
- // http://matroska.org/technical/specs/index.html#block_structure
1496
- static $BlockLacingType = array();
1497
- if (empty($BlockLacingType)) {
1498
- $BlockLacingType[0x00] = 'no lacing';
1499
- $BlockLacingType[0x01] = 'Xiph lacing';
1500
- $BlockLacingType[0x02] = 'fixed-size lacing';
1501
- $BlockLacingType[0x03] = 'EBML lacing';
1502
- }
1503
- return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype);
1504
- }
1505
-
1506
- public static function CodecIDtoCommonName($codecid) {
1507
- // http://www.matroska.org/technical/specs/codecid/index.html
1508
- static $CodecIDlist = array();
1509
- if (empty($CodecIDlist)) {
1510
- $CodecIDlist['A_AAC'] = 'aac';
1511
- $CodecIDlist['A_AAC/MPEG2/LC'] = 'aac';
1512
- $CodecIDlist['A_AC3'] = 'ac3';
1513
- $CodecIDlist['A_DTS'] = 'dts';
1514
- $CodecIDlist['A_FLAC'] = 'flac';
1515
- $CodecIDlist['A_MPEG/L1'] = 'mp1';
1516
- $CodecIDlist['A_MPEG/L2'] = 'mp2';
1517
- $CodecIDlist['A_MPEG/L3'] = 'mp3';
1518
- $CodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian
1519
- $CodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian
1520
- $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
1521
- $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
1522
- $CodecIDlist['A_VORBIS'] = 'vorbis';
1523
- $CodecIDlist['V_MPEG1'] = 'mpeg';
1524
- $CodecIDlist['V_THEORA'] = 'theora';
1525
- $CodecIDlist['V_REAL/RV40'] = 'real';
1526
- $CodecIDlist['V_REAL/RV10'] = 'real';
1527
- $CodecIDlist['V_REAL/RV20'] = 'real';
1528
- $CodecIDlist['V_REAL/RV30'] = 'real';
1529
- $CodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime
1530
- $CodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4';
1531
- $CodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4';
1532
- $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264';
1533
- $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4';
1534
- $CodecIDlist['V_VP8'] = 'vp8';
1535
- $CodecIDlist['V_MS/VFW/FOURCC'] = 'riff';
1536
- $CodecIDlist['A_MS/ACM'] = 'riff';
1537
- }
1538
- return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid);
1539
- }
1540
-
1541
- private static function EBMLidName($value) {
1542
- static $EBMLidList = array();
1543
- if (empty($EBMLidList)) {
1544
- $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType';
1545
- $EBMLidList[EBML_ID_ATTACHEDFILE] = 'AttachedFile';
1546
- $EBMLidList[EBML_ID_ATTACHMENTLINK] = 'AttachmentLink';
1547
- $EBMLidList[EBML_ID_ATTACHMENTS] = 'Attachments';
1548
- $EBMLidList[EBML_ID_AUDIO] = 'Audio';
1549
- $EBMLidList[EBML_ID_BITDEPTH] = 'BitDepth';
1550
- $EBMLidList[EBML_ID_CHANNELPOSITIONS] = 'ChannelPositions';
1551
- $EBMLidList[EBML_ID_CHANNELS] = 'Channels';
1552
- $EBMLidList[EBML_ID_CHAPCOUNTRY] = 'ChapCountry';
1553
- $EBMLidList[EBML_ID_CHAPLANGUAGE] = 'ChapLanguage';
1554
- $EBMLidList[EBML_ID_CHAPPROCESS] = 'ChapProcess';
1555
- $EBMLidList[EBML_ID_CHAPPROCESSCODECID] = 'ChapProcessCodecID';
1556
- $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND] = 'ChapProcessCommand';
1557
- $EBMLidList[EBML_ID_CHAPPROCESSDATA] = 'ChapProcessData';
1558
- $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE] = 'ChapProcessPrivate';
1559
- $EBMLidList[EBML_ID_CHAPPROCESSTIME] = 'ChapProcessTime';
1560
- $EBMLidList[EBML_ID_CHAPSTRING] = 'ChapString';
1561
- $EBMLidList[EBML_ID_CHAPTERATOM] = 'ChapterAtom';
1562
- $EBMLidList[EBML_ID_CHAPTERDISPLAY] = 'ChapterDisplay';
1563
- $EBMLidList[EBML_ID_CHAPTERFLAGENABLED] = 'ChapterFlagEnabled';
1564
- $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN] = 'ChapterFlagHidden';
1565
- $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV] = 'ChapterPhysicalEquiv';
1566
- $EBMLidList[EBML_ID_CHAPTERS] = 'Chapters';
1567
- $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID] = 'ChapterSegmentEditionUID';
1568
- $EBMLidList[EBML_ID_CHAPTERSEGMENTUID] = 'ChapterSegmentUID';
1569
- $EBMLidList[EBML_ID_CHAPTERTIMEEND] = 'ChapterTimeEnd';
1570
- $EBMLidList[EBML_ID_CHAPTERTIMESTART] = 'ChapterTimeStart';
1571
- $EBMLidList[EBML_ID_CHAPTERTRACK] = 'ChapterTrack';
1572
- $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER] = 'ChapterTrackNumber';
1573
- $EBMLidList[EBML_ID_CHAPTERTRANSLATE] = 'ChapterTranslate';
1574
- $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC] = 'ChapterTranslateCodec';
1575
- $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
1576
- $EBMLidList[EBML_ID_CHAPTERTRANSLATEID] = 'ChapterTranslateID';
1577
- $EBMLidList[EBML_ID_CHAPTERUID] = 'ChapterUID';
1578
- $EBMLidList[EBML_ID_CLUSTER] = 'Cluster';
1579
- $EBMLidList[EBML_ID_CLUSTERBLOCK] = 'ClusterBlock';
1580
- $EBMLidList[EBML_ID_CLUSTERBLOCKADDID] = 'ClusterBlockAddID';
1581
- $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL] = 'ClusterBlockAdditional';
1582
- $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID] = 'ClusterBlockAdditionID';
1583
- $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS] = 'ClusterBlockAdditions';
1584
- $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION] = 'ClusterBlockDuration';
1585
- $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP] = 'ClusterBlockGroup';
1586
- $EBMLidList[EBML_ID_CLUSTERBLOCKMORE] = 'ClusterBlockMore';
1587
- $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL] = 'ClusterBlockVirtual';
1588
- $EBMLidList[EBML_ID_CLUSTERCODECSTATE] = 'ClusterCodecState';
1589
- $EBMLidList[EBML_ID_CLUSTERDELAY] = 'ClusterDelay';
1590
- $EBMLidList[EBML_ID_CLUSTERDURATION] = 'ClusterDuration';
1591
- $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK] = 'ClusterEncryptedBlock';
1592
- $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER] = 'ClusterFrameNumber';
1593
- $EBMLidList[EBML_ID_CLUSTERLACENUMBER] = 'ClusterLaceNumber';
1594
- $EBMLidList[EBML_ID_CLUSTERPOSITION] = 'ClusterPosition';
1595
- $EBMLidList[EBML_ID_CLUSTERPREVSIZE] = 'ClusterPrevSize';
1596
- $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK] = 'ClusterReferenceBlock';
1597
- $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY] = 'ClusterReferencePriority';
1598
- $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL] = 'ClusterReferenceVirtual';
1599
- $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER] = 'ClusterSilentTrackNumber';
1600
- $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS] = 'ClusterSilentTracks';
1601
- $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK] = 'ClusterSimpleBlock';
1602
- $EBMLidList[EBML_ID_CLUSTERTIMECODE] = 'ClusterTimecode';
1603
- $EBMLidList[EBML_ID_CLUSTERTIMESLICE] = 'ClusterTimeSlice';
1604
- $EBMLidList[EBML_ID_CODECDECODEALL] = 'CodecDecodeAll';
1605
- $EBMLidList[EBML_ID_CODECDOWNLOADURL] = 'CodecDownloadURL';
1606
- $EBMLidList[EBML_ID_CODECID] = 'CodecID';
1607
- $EBMLidList[EBML_ID_CODECINFOURL] = 'CodecInfoURL';
1608
- $EBMLidList[EBML_ID_CODECNAME] = 'CodecName';
1609
- $EBMLidList[EBML_ID_CODECPRIVATE] = 'CodecPrivate';
1610
- $EBMLidList[EBML_ID_CODECSETTINGS] = 'CodecSettings';
1611
- $EBMLidList[EBML_ID_COLOURSPACE] = 'ColourSpace';
1612
- $EBMLidList[EBML_ID_CONTENTCOMPALGO] = 'ContentCompAlgo';
1613
- $EBMLidList[EBML_ID_CONTENTCOMPRESSION] = 'ContentCompression';
1614
- $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS] = 'ContentCompSettings';
1615
- $EBMLidList[EBML_ID_CONTENTENCALGO] = 'ContentEncAlgo';
1616
- $EBMLidList[EBML_ID_CONTENTENCKEYID] = 'ContentEncKeyID';
1617
- $EBMLidList[EBML_ID_CONTENTENCODING] = 'ContentEncoding';
1618
- $EBMLidList[EBML_ID_CONTENTENCODINGORDER] = 'ContentEncodingOrder';
1619
- $EBMLidList[EBML_ID_CONTENTENCODINGS] = 'ContentEncodings';
1620
- $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE] = 'ContentEncodingScope';
1621
- $EBMLidList[EBML_ID_CONTENTENCODINGTYPE] = 'ContentEncodingType';
1622
- $EBMLidList[EBML_ID_CONTENTENCRYPTION] = 'ContentEncryption';
1623
- $EBMLidList[EBML_ID_CONTENTSIGALGO] = 'ContentSigAlgo';
1624
- $EBMLidList[EBML_ID_CONTENTSIGHASHALGO] = 'ContentSigHashAlgo';
1625
- $EBMLidList[EBML_ID_CONTENTSIGKEYID] = 'ContentSigKeyID';
1626
- $EBMLidList[EBML_ID_CONTENTSIGNATURE] = 'ContentSignature';
1627
- $EBMLidList[EBML_ID_CRC32] = 'CRC32';
1628
- $EBMLidList[EBML_ID_CUEBLOCKNUMBER] = 'CueBlockNumber';
1629
- $EBMLidList[EBML_ID_CUECLUSTERPOSITION] = 'CueClusterPosition';
1630
- $EBMLidList[EBML_ID_CUECODECSTATE] = 'CueCodecState';
1631
- $EBMLidList[EBML_ID_CUEPOINT] = 'CuePoint';
1632
- $EBMLidList[EBML_ID_CUEREFCLUSTER] = 'CueRefCluster';
1633
- $EBMLidList[EBML_ID_CUEREFCODECSTATE] = 'CueRefCodecState';
1634
- $EBMLidList[EBML_ID_CUEREFERENCE] = 'CueReference';
1635
- $EBMLidList[EBML_ID_CUEREFNUMBER] = 'CueRefNumber';
1636
- $EBMLidList[EBML_ID_CUEREFTIME] = 'CueRefTime';
1637
- $EBMLidList[EBML_ID_CUES] = 'Cues';
1638
- $EBMLidList[EBML_ID_CUETIME] = 'CueTime';
1639
- $EBMLidList[EBML_ID_CUETRACK] = 'CueTrack';
1640
- $EBMLidList[EBML_ID_CUETRACKPOSITIONS] = 'CueTrackPositions';
1641
- $EBMLidList[EBML_ID_DATEUTC] = 'DateUTC';
1642
- $EBMLidList[EBML_ID_DEFAULTDURATION] = 'DefaultDuration';
1643
- $EBMLidList[EBML_ID_DISPLAYHEIGHT] = 'DisplayHeight';
1644
- $EBMLidList[EBML_ID_DISPLAYUNIT] = 'DisplayUnit';
1645
- $EBMLidList[EBML_ID_DISPLAYWIDTH] = 'DisplayWidth';
1646
- $EBMLidList[EBML_ID_DOCTYPE] = 'DocType';
1647
- $EBMLidList[EBML_ID_DOCTYPEREADVERSION] = 'DocTypeReadVersion';
1648
- $EBMLidList[EBML_ID_DOCTYPEVERSION] = 'DocTypeVersion';
1649
- $EBMLidList[EBML_ID_DURATION] = 'Duration';
1650
- $EBMLidList[EBML_ID_EBML] = 'EBML';
1651
- $EBMLidList[EBML_ID_EBMLMAXIDLENGTH] = 'EBMLMaxIDLength';
1652
- $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH] = 'EBMLMaxSizeLength';
1653
- $EBMLidList[EBML_ID_EBMLREADVERSION] = 'EBMLReadVersion';
1654
- $EBMLidList[EBML_ID_EBMLVERSION] = 'EBMLVersion';
1655
- $EBMLidList[EBML_ID_EDITIONENTRY] = 'EditionEntry';
1656
- $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT] = 'EditionFlagDefault';
1657
- $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN] = 'EditionFlagHidden';
1658
- $EBMLidList[EBML_ID_EDITIONFLAGORDERED] = 'EditionFlagOrdered';
1659
- $EBMLidList[EBML_ID_EDITIONUID] = 'EditionUID';
1660
- $EBMLidList[EBML_ID_FILEDATA] = 'FileData';
1661
- $EBMLidList[EBML_ID_FILEDESCRIPTION] = 'FileDescription';
1662
- $EBMLidList[EBML_ID_FILEMIMETYPE] = 'FileMimeType';
1663
- $EBMLidList[EBML_ID_FILENAME] = 'FileName';
1664
- $EBMLidList[EBML_ID_FILEREFERRAL] = 'FileReferral';
1665
- $EBMLidList[EBML_ID_FILEUID] = 'FileUID';
1666
- $EBMLidList[EBML_ID_FLAGDEFAULT] = 'FlagDefault';
1667
- $EBMLidList[EBML_ID_FLAGENABLED] = 'FlagEnabled';
1668
- $EBMLidList[EBML_ID_FLAGFORCED] = 'FlagForced';
1669
- $EBMLidList[EBML_ID_FLAGINTERLACED] = 'FlagInterlaced';
1670
- $EBMLidList[EBML_ID_FLAGLACING] = 'FlagLacing';
1671
- $EBMLidList[EBML_ID_GAMMAVALUE] = 'GammaValue';
1672
- $EBMLidList[EBML_ID_INFO] = 'Info';
1673
- $EBMLidList[EBML_ID_LANGUAGE] = 'Language';
1674
- $EBMLidList[EBML_ID_MAXBLOCKADDITIONID] = 'MaxBlockAdditionID';
1675
- $EBMLidList[EBML_ID_MAXCACHE] = 'MaxCache';
1676
- $EBMLidList[EBML_ID_MINCACHE] = 'MinCache';
1677
- $EBMLidList[EBML_ID_MUXINGAPP] = 'MuxingApp';
1678
- $EBMLidList[EBML_ID_NAME] = 'Name';
1679
- $EBMLidList[EBML_ID_NEXTFILENAME] = 'NextFilename';
1680
- $EBMLidList[EBML_ID_NEXTUID] = 'NextUID';
1681
- $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY] = 'OutputSamplingFrequency';
1682
- $EBMLidList[EBML_ID_PIXELCROPBOTTOM] = 'PixelCropBottom';
1683
- $EBMLidList[EBML_ID_PIXELCROPLEFT] = 'PixelCropLeft';
1684
- $EBMLidList[EBML_ID_PIXELCROPRIGHT] = 'PixelCropRight';
1685
- $EBMLidList[EBML_ID_PIXELCROPTOP] = 'PixelCropTop';
1686
- $EBMLidList[EBML_ID_PIXELHEIGHT] = 'PixelHeight';
1687
- $EBMLidList[EBML_ID_PIXELWIDTH] = 'PixelWidth';
1688
- $EBMLidList[EBML_ID_PREVFILENAME] = 'PrevFilename';
1689
- $EBMLidList[EBML_ID_PREVUID] = 'PrevUID';
1690
- $EBMLidList[EBML_ID_SAMPLINGFREQUENCY] = 'SamplingFrequency';
1691
- $EBMLidList[EBML_ID_SEEK] = 'Seek';
1692
- $EBMLidList[EBML_ID_SEEKHEAD] = 'SeekHead';
1693
- $EBMLidList[EBML_ID_SEEKID] = 'SeekID';
1694
- $EBMLidList[EBML_ID_SEEKPOSITION] = 'SeekPosition';
1695
- $EBMLidList[EBML_ID_SEGMENT] = 'Segment';
1696
- $EBMLidList[EBML_ID_SEGMENTFAMILY] = 'SegmentFamily';
1697
- $EBMLidList[EBML_ID_SEGMENTFILENAME] = 'SegmentFilename';
1698
- $EBMLidList[EBML_ID_SEGMENTUID] = 'SegmentUID';
1699
- $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag';
1700
- $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices';
1701
- $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode';
1702
- $EBMLidList[EBML_ID_OLDSTEREOMODE] = 'OldStereoMode';
1703
- $EBMLidList[EBML_ID_TAG] = 'Tag';
1704
- $EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID';
1705
- $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary';
1706
- $EBMLidList[EBML_ID_TAGCHAPTERUID] = 'TagChapterUID';
1707
- $EBMLidList[EBML_ID_TAGDEFAULT] = 'TagDefault';
1708
- $EBMLidList[EBML_ID_TAGEDITIONUID] = 'TagEditionUID';
1709
- $EBMLidList[EBML_ID_TAGLANGUAGE] = 'TagLanguage';
1710
- $EBMLidList[EBML_ID_TAGNAME] = 'TagName';
1711
- $EBMLidList[EBML_ID_TAGTRACKUID] = 'TagTrackUID';
1712
- $EBMLidList[EBML_ID_TAGS] = 'Tags';
1713
- $EBMLidList[EBML_ID_TAGSTRING] = 'TagString';
1714
- $EBMLidList[EBML_ID_TARGETS] = 'Targets';
1715
- $EBMLidList[EBML_ID_TARGETTYPE] = 'TargetType';
1716
- $EBMLidList[EBML_ID_TARGETTYPEVALUE] = 'TargetTypeValue';
1717
- $EBMLidList[EBML_ID_TIMECODESCALE] = 'TimecodeScale';
1718
- $EBMLidList[EBML_ID_TITLE] = 'Title';
1719
- $EBMLidList[EBML_ID_TRACKENTRY] = 'TrackEntry';
1720
- $EBMLidList[EBML_ID_TRACKNUMBER] = 'TrackNumber';
1721
- $EBMLidList[EBML_ID_TRACKOFFSET] = 'TrackOffset';
1722
- $EBMLidList[EBML_ID_TRACKOVERLAY] = 'TrackOverlay';
1723
- $EBMLidList[EBML_ID_TRACKS] = 'Tracks';
1724
- $EBMLidList[EBML_ID_TRACKTIMECODESCALE] = 'TrackTimecodeScale';
1725
- $EBMLidList[EBML_ID_TRACKTRANSLATE] = 'TrackTranslate';
1726
- $EBMLidList[EBML_ID_TRACKTRANSLATECODEC] = 'TrackTranslateCodec';
1727
- $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID] = 'TrackTranslateEditionUID';
1728
- $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID] = 'TrackTranslateTrackID';
1729
- $EBMLidList[EBML_ID_TRACKTYPE] = 'TrackType';
1730
- $EBMLidList[EBML_ID_TRACKUID] = 'TrackUID';
1731
- $EBMLidList[EBML_ID_VIDEO] = 'Video';
1732
- $EBMLidList[EBML_ID_VOID] = 'Void';
1733
- $EBMLidList[EBML_ID_WRITINGAPP] = 'WritingApp';
1734
- }
1735
-
1736
- return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
1737
- }
1738
-
1739
- public static function displayUnit($value) {
1740
- // http://www.matroska.org/technical/specs/index.html#DisplayUnit
1741
- static $units = array(
1742
- 0 => 'pixels',
1743
- 1 => 'centimeters',
1744
- 2 => 'inches',
1745
- 3 => 'Display Aspect Ratio');
1746
-
1747
- return (isset($units[$value]) ? $units[$value] : 'unknown');
1748
- }
1749
-
1750
- private static function getDefaultStreamInfo($streams)
1751
- {
1752
- foreach (array_reverse($streams) as $stream) {
1753
- if ($stream['default']) {
1754
- break;
1755
- }
1756
- }
1757
-
1758
- $unset = array('default', 'name');
1759
- foreach ($unset as $u) {
1760
- if (isset($stream[$u])) {
1761
- unset($stream[$u]);
1762
- }
1763
- }
1764
-
1765
- $info = $stream;
1766
- $info['streams'] = $streams;
1767
-
1768
- return $info;
1769
- }
1770
-
1771
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.mpeg.php DELETED
@@ -1,296 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio-video.mpeg.php //
11
- // module for analyzing MPEG files //
12
- // dependencies: module.audio.mp3.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
17
-
18
- define('GETID3_MPEG_VIDEO_PICTURE_START', "\x00\x00\x01\x00");
19
- define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
20
- define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
21
- define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR', "\x00\x00\x01\xB4");
22
- define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
23
- define('GETID3_MPEG_VIDEO_SEQUENCE_END', "\x00\x00\x01\xB7");
24
- define('GETID3_MPEG_VIDEO_GROUP_START', "\x00\x00\x01\xB8");
25
- define('GETID3_MPEG_AUDIO_START', "\x00\x00\x01\xC0");
26
-
27
-
28
- class getid3_mpeg extends getid3_handler
29
- {
30
-
31
- public function Analyze() {
32
- $info = &$this->getid3->info;
33
-
34
- if ($info['avdataend'] <= $info['avdataoffset']) {
35
- $info['error'][] = '"avdataend" ('.$info['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$info['avdataoffset'].')';
36
- return false;
37
- }
38
- $info['fileformat'] = 'mpeg';
39
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
40
- $MPEGstreamData = fread($this->getid3->fp, min(100000, $info['avdataend'] - $info['avdataoffset']));
41
- $MPEGstreamDataLength = strlen($MPEGstreamData);
42
-
43
- $foundVideo = true;
44
- $VideoChunkOffset = 0;
45
- while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
46
- if ($VideoChunkOffset >= $MPEGstreamDataLength) {
47
- $foundVideo = false;
48
- break;
49
- }
50
- }
51
- if ($foundVideo) {
52
-
53
- // Start code 32 bits
54
- // horizontal frame size 12 bits
55
- // vertical frame size 12 bits
56
- // pixel aspect ratio 4 bits
57
- // frame rate 4 bits
58
- // bitrate 18 bits
59
- // marker bit 1 bit
60
- // VBV buffer size 10 bits
61
- // constrained parameter flag 1 bit
62
- // intra quant. matrix flag 1 bit
63
- // intra quant. matrix values 512 bits (present if matrix flag == 1)
64
- // non-intra quant. matrix flag 1 bit
65
- // non-intra quant. matrix values 512 bits (present if matrix flag == 1)
66
-
67
- $info['video']['dataformat'] = 'mpeg';
68
-
69
- $VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
70
-
71
- $FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
72
- $VideoChunkOffset += 3;
73
-
74
- $AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
75
- $VideoChunkOffset += 1;
76
-
77
- $assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
78
- $VideoChunkOffset += 4;
79
-
80
- $info['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
81
- $info['mpeg']['video']['raw']['framesize_vertical'] = ($FrameSizeDWORD & 0x000FFF); // 12 bits for vertical frame size
82
- $info['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
83
- $info['mpeg']['video']['raw']['frame_rate'] = ($AspectRatioFrameRateDWORD & 0x0F);
84
-
85
- $info['mpeg']['video']['framesize_horizontal'] = $info['mpeg']['video']['raw']['framesize_horizontal'];
86
- $info['mpeg']['video']['framesize_vertical'] = $info['mpeg']['video']['raw']['framesize_vertical'];
87
-
88
- $info['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
89
- $info['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
90
- $info['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($info['mpeg']['video']['raw']['frame_rate']);
91
-
92
- $info['mpeg']['video']['raw']['bitrate'] = getid3_lib::Bin2Dec(substr($assortedinformation, 0, 18));
93
- $info['mpeg']['video']['raw']['marker_bit'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18, 1));
94
- $info['mpeg']['video']['raw']['vbv_buffer_size'] = getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10));
95
- $info['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29, 1));
96
- $info['mpeg']['video']['raw']['intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30, 1));
97
- if ($info['mpeg']['video']['raw']['intra_quant_flag']) {
98
-
99
- // read 512 bits
100
- $info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
101
- $VideoChunkOffset += 64;
102
-
103
- $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($info['mpeg']['video']['raw']['intra_quant'], 511, 1));
104
- $info['mpeg']['video']['raw']['intra_quant'] = getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
105
-
106
- if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
107
- $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
108
- $VideoChunkOffset += 64;
109
- }
110
-
111
- } else {
112
-
113
- $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31, 1));
114
- if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
115
- $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
116
- $VideoChunkOffset += 64;
117
- }
118
-
119
- }
120
-
121
- if ($info['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
122
-
123
- $info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] cannot determine average bitrate of VBR MPEG video files';
124
- $info['mpeg']['video']['bitrate_mode'] = 'vbr';
125
-
126
- } else {
127
-
128
- $info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
129
- $info['mpeg']['video']['bitrate_mode'] = 'cbr';
130
- $info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
131
-
132
- }
133
-
134
- $info['video']['resolution_x'] = $info['mpeg']['video']['framesize_horizontal'];
135
- $info['video']['resolution_y'] = $info['mpeg']['video']['framesize_vertical'];
136
- $info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
137
- $info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
138
- $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
139
- $info['video']['lossless'] = false;
140
- $info['video']['bits_per_sample'] = 24;
141
-
142
- } else {
143
-
144
- $info['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
145
-
146
- }
147
-
148
- //0x000001B3 begins the sequence_header of every MPEG video stream.
149
- //But in MPEG-2, this header must immediately be followed by an
150
- //extension_start_code (0x000001B5) with a sequence_extension ID (1).
151
- //(This extension contains all the additional MPEG-2 stuff.)
152
- //MPEG-1 doesn't have this extension, so that's a sure way to tell the
153
- //difference between MPEG-1 and MPEG-2 video streams.
154
-
155
- if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
156
- $info['video']['codec'] = 'MPEG-2';
157
- } else {
158
- $info['video']['codec'] = 'MPEG-1';
159
- }
160
-
161
-
162
- $AudioChunkOffset = 0;
163
- while (true) {
164
- while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
165
- if ($AudioChunkOffset >= $MPEGstreamDataLength) {
166
- break 2;
167
- }
168
- }
169
-
170
- $getid3_temp = new getID3();
171
- $getid3_temp->openfile($this->getid3->filename);
172
- $getid3_temp->info = $info;
173
- $getid3_mp3 = new getid3_mp3($getid3_temp);
174
- for ($i = 0; $i <= 7; $i++) {
175
- // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
176
- // I have no idea why or what the difference is, so this is a stupid hack.
177
- // If anybody has any better idea of what's going on, please let me know - info@getid3.org
178
- fseek($getid3_temp->fp, ftell($this->getid3->fp), SEEK_SET);
179
- $getid3_temp->info = $info; // only overwrite real data if valid header found
180
- if ($getid3_mp3->decodeMPEGaudioHeader(($AudioChunkOffset + 3) + 8 + $i, $getid3_temp->info, false)) {
181
- $info = $getid3_temp->info;
182
- $info['audio']['bitrate_mode'] = 'cbr';
183
- $info['audio']['lossless'] = false;
184
- unset($getid3_temp, $getid3_mp3);
185
- break 2;
186
- }
187
- }
188
- unset($getid3_temp, $getid3_mp3);
189
- }
190
-
191
- // Temporary hack to account for interleaving overhead:
192
- if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) {
193
- $info['playtime_seconds'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['video']['bitrate'] + $info['audio']['bitrate']);
194
-
195
- // Interleaved MPEG audio/video files have a certain amount of overhead that varies
196
- // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
197
- // Use interpolated lookup tables to approximately guess how much is overhead, because
198
- // playtime is calculated as filesize / total-bitrate
199
- $info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']);
200
-
201
- //switch ($info['video']['bitrate']) {
202
- // case('5000000'):
203
- // $multiplier = 0.93292642112380355828048824319889;
204
- // break;
205
- // case('5500000'):
206
- // $multiplier = 0.93582895375200989965359777343219;
207
- // break;
208
- // case('6000000'):
209
- // $multiplier = 0.93796247714820932532911373859139;
210
- // break;
211
- // case('7000000'):
212
- // $multiplier = 0.9413264083635103463010117778776;
213
- // break;
214
- // default:
215
- // $multiplier = 1;
216
- // break;
217
- //}
218
- //$info['playtime_seconds'] *= $multiplier;
219
- //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
220
- if ($info['video']['bitrate'] < 50000) {
221
- $info['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
222
- }
223
- }
224
-
225
- return true;
226
- }
227
-
228
-
229
- public function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
230
- $OverheadPercentage = 0;
231
-
232
- $AudioBitrate = max(min($AudioBitrate / 1000, 384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
233
- $VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps - 10Mbps (beyond that curves flatten anyways, no big loss)
234
-
235
-
236
- //OMBB[audiobitrate] = array(video-10kbps, video-100kbps, video-1000kbps, video-10000kbps)
237
- $OverheadMultiplierByBitrate[32] = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
238
- $OverheadMultiplierByBitrate[48] = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
239
- $OverheadMultiplierByBitrate[56] = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
240
- $OverheadMultiplierByBitrate[64] = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
241
- $OverheadMultiplierByBitrate[96] = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
242
- $OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
243
- $OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
244
- $OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
245
- $OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
246
- $OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
247
- $OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
248
- $OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
249
-
250
- $BitrateToUseMin = 32;
251
- $BitrateToUseMax = 32;
252
- $previousBitrate = 32;
253
- foreach ($OverheadMultiplierByBitrate as $key => $value) {
254
- if ($AudioBitrate >= $previousBitrate) {
255
- $BitrateToUseMin = $previousBitrate;
256
- }
257
- if ($AudioBitrate < $key) {
258
- $BitrateToUseMax = $key;
259
- break;
260
- }
261
- $previousBitrate = $key;
262
- }
263
- $FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
264
-
265
- $VideoBitrateLog10 = log10($VideoBitrate);
266
- $VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
267
- $VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
268
- $VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
269
- $VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
270
- $FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
271
-
272
- $OverheadPercentage = $VideoFactorMin1 * $FactorA * $FactorV;
273
- $OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) * $FactorV;
274
- $OverheadPercentage += $VideoFactorMax1 * $FactorA * (1 - $FactorV);
275
- $OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
276
-
277
- return $OverheadPercentage;
278
- }
279
-
280
-
281
- public function MPEGvideoFramerateLookup($rawframerate) {
282
- $MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
283
- return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
284
- }
285
-
286
- public function MPEGvideoAspectRatioLookup($rawaspectratio) {
287
- $MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
288
- return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
289
- }
290
-
291
- public function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
292
- $MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
293
- return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
294
- }
295
-
296
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.quicktime.php DELETED
@@ -1,2145 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio-video.quicktime.php //
11
- // module for analyzing Quicktime and MP3-in-MP4 files //
12
- // dependencies: module.audio.mp3.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
17
-
18
- class getid3_quicktime extends getid3_handler
19
- {
20
-
21
- public $ReturnAtomData = true;
22
- public $ParseAllPossibleAtoms = false;
23
-
24
- public function Analyze() {
25
- $info = &$this->getid3->info;
26
-
27
- $info['fileformat'] = 'quicktime';
28
- $info['quicktime']['hinting'] = false;
29
- $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
30
-
31
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
32
-
33
- $offset = 0;
34
- $atomcounter = 0;
35
-
36
- while ($offset < $info['avdataend']) {
37
- if (!getid3_lib::intValueSupported($offset)) {
38
- $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
39
- break;
40
- }
41
- fseek($this->getid3->fp, $offset, SEEK_SET);
42
- $AtomHeader = fread($this->getid3->fp, 8);
43
-
44
- $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
45
- $atomname = substr($AtomHeader, 4, 4);
46
-
47
- // 64-bit MOV patch by jlegate�ktnc*com
48
- if ($atomsize == 1) {
49
- $atomsize = getid3_lib::BigEndian2Int(fread($this->getid3->fp, 8));
50
- }
51
-
52
- $info['quicktime'][$atomname]['name'] = $atomname;
53
- $info['quicktime'][$atomname]['size'] = $atomsize;
54
- $info['quicktime'][$atomname]['offset'] = $offset;
55
-
56
- if (($offset + $atomsize) > $info['avdataend']) {
57
- $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
58
- return false;
59
- }
60
-
61
- if ($atomsize == 0) {
62
- // Furthermore, for historical reasons the list of atoms is optionally
63
- // terminated by a 32-bit integer set to 0. If you are writing a program
64
- // to read user data atoms, you should allow for the terminating 0.
65
- break;
66
- }
67
- switch ($atomname) {
68
- case 'mdat': // Media DATa atom
69
- // 'mdat' contains the actual data for the audio/video
70
- if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
71
-
72
- $info['avdataoffset'] = $info['quicktime'][$atomname]['offset'] + 8;
73
- $OldAVDataEnd = $info['avdataend'];
74
- $info['avdataend'] = $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
75
-
76
- $getid3_temp = new getID3();
77
- $getid3_temp->openfile($this->getid3->filename);
78
- $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
79
- $getid3_temp->info['avdataend'] = $info['avdataend'];
80
- $getid3_mp3 = new getid3_mp3($getid3_temp);
81
- if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode(fread($this->getid3->fp, 4)))) {
82
- $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
83
- if (!empty($getid3_temp->info['warning'])) {
84
- foreach ($getid3_temp->info['warning'] as $value) {
85
- $info['warning'][] = $value;
86
- }
87
- }
88
- if (!empty($getid3_temp->info['mpeg'])) {
89
- $info['mpeg'] = $getid3_temp->info['mpeg'];
90
- if (isset($info['mpeg']['audio'])) {
91
- $info['audio']['dataformat'] = 'mp3';
92
- $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
93
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
94
- $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
95
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
96
- $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
97
- $info['bitrate'] = $info['audio']['bitrate'];
98
- }
99
- }
100
- }
101
- unset($getid3_mp3, $getid3_temp);
102
- $info['avdataend'] = $OldAVDataEnd;
103
- unset($OldAVDataEnd);
104
-
105
- }
106
- break;
107
-
108
- case 'free': // FREE space atom
109
- case 'skip': // SKIP atom
110
- case 'wide': // 64-bit expansion placeholder atom
111
- // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
112
- break;
113
-
114
- default:
115
- $atomHierarchy = array();
116
- $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($this->getid3->fp, $atomsize), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
117
- break;
118
- }
119
-
120
- $offset += $atomsize;
121
- $atomcounter++;
122
- }
123
-
124
- if (!empty($info['avdataend_tmp'])) {
125
- // this value is assigned to a temp value and then erased because
126
- // otherwise any atoms beyond the 'mdat' atom would not get parsed
127
- $info['avdataend'] = $info['avdataend_tmp'];
128
- unset($info['avdataend_tmp']);
129
- }
130
-
131
- if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
132
- $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
133
- }
134
- if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
135
- $info['audio']['bitrate'] = $info['bitrate'];
136
- }
137
- if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
138
- foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
139
- $samples_per_second = $samples_count / $info['playtime_seconds'];
140
- if ($samples_per_second > 240) {
141
- // has to be audio samples
142
- } else {
143
- $info['video']['frame_rate'] = $samples_per_second;
144
- break;
145
- }
146
- }
147
- }
148
- if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) {
149
- $info['fileformat'] = 'mp4';
150
- $info['mime_type'] = 'audio/mp4';
151
- unset($info['video']['dataformat']);
152
- }
153
-
154
- if (!$this->ReturnAtomData) {
155
- unset($info['quicktime']['moov']);
156
- }
157
-
158
- if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
159
- $info['audio']['dataformat'] = 'quicktime';
160
- }
161
- if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
162
- $info['video']['dataformat'] = 'quicktime';
163
- }
164
-
165
- return true;
166
- }
167
-
168
- public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
169
- // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
170
-
171
- $info = &$this->getid3->info;
172
-
173
- $atom_parent = array_pop($atomHierarchy);
174
- array_push($atomHierarchy, $atomname);
175
- $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
176
- $atom_structure['name'] = $atomname;
177
- $atom_structure['size'] = $atomsize;
178
- $atom_structure['offset'] = $baseoffset;
179
- //echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8)).'<br>';
180
- //echo getid3_lib::PrintHexBytes(substr($atom_data, 0, 8), false).'<br><br>';
181
- switch ($atomname) {
182
- case 'moov': // MOVie container atom
183
- case 'trak': // TRAcK container atom
184
- case 'clip': // CLIPping container atom
185
- case 'matt': // track MATTe container atom
186
- case 'edts': // EDiTS container atom
187
- case 'tref': // Track REFerence container atom
188
- case 'mdia': // MeDIA container atom
189
- case 'minf': // Media INFormation container atom
190
- case 'dinf': // Data INFormation container atom
191
- case 'udta': // User DaTA container atom
192
- case 'cmov': // Compressed MOVie container atom
193
- case 'rmra': // Reference Movie Record Atom
194
- case 'rmda': // Reference Movie Descriptor Atom
195
- case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
196
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
197
- break;
198
-
199
- case 'ilst': // Item LiST container atom
200
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
201
-
202
- // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
203
- $allnumericnames = true;
204
- foreach ($atom_structure['subatoms'] as $subatomarray) {
205
- if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
206
- $allnumericnames = false;
207
- break;
208
- }
209
- }
210
- if ($allnumericnames) {
211
- $newData = array();
212
- foreach ($atom_structure['subatoms'] as $subatomarray) {
213
- foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
214
- unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
215
- $newData[$subatomarray['name']] = $newData_subatomarray;
216
- break;
217
- }
218
- }
219
- $atom_structure['data'] = $newData;
220
- unset($atom_structure['subatoms']);
221
- }
222
- break;
223
-
224
- case "\x00\x00\x00\x01":
225
- case "\x00\x00\x00\x02":
226
- case "\x00\x00\x00\x03":
227
- case "\x00\x00\x00\x04":
228
- case "\x00\x00\x00\x05":
229
- $atomname = getid3_lib::BigEndian2Int($atomname);
230
- $atom_structure['name'] = $atomname;
231
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
232
- break;
233
-
234
- case 'stbl': // Sample TaBLe container atom
235
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
236
- $isVideo = false;
237
- $framerate = 0;
238
- $framecount = 0;
239
- foreach ($atom_structure['subatoms'] as $key => $value_array) {
240
- if (isset($value_array['sample_description_table'])) {
241
- foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
242
- if (isset($value_array2['data_format'])) {
243
- switch ($value_array2['data_format']) {
244
- case 'avc1':
245
- case 'mp4v':
246
- // video data
247
- $isVideo = true;
248
- break;
249
- case 'mp4a':
250
- // audio data
251
- break;
252
- }
253
- }
254
- }
255
- } elseif (isset($value_array['time_to_sample_table'])) {
256
- foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
257
- if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
258
- $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
259
- $framecount = $value_array2['sample_count'];
260
- }
261
- }
262
- }
263
- }
264
- if ($isVideo && $framerate) {
265
- $info['quicktime']['video']['frame_rate'] = $framerate;
266
- $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
267
- }
268
- if ($isVideo && $framecount) {
269
- $info['quicktime']['video']['frame_count'] = $framecount;
270
- }
271
- break;
272
-
273
-
274
- case 'aART': // Album ARTist
275
- case 'catg': // CaTeGory
276
- case 'covr': // COVeR artwork
277
- case 'cpil': // ComPILation
278
- case 'cprt': // CoPyRighT
279
- case 'desc': // DESCription
280
- case 'disk': // DISK number
281
- case 'egid': // Episode Global ID
282
- case 'gnre': // GeNRE
283
- case 'keyw': // KEYWord
284
- case 'ldes':
285
- case 'pcst': // PodCaST
286
- case 'pgap': // GAPless Playback
287
- case 'purd': // PURchase Date
288
- case 'purl': // Podcast URL
289
- case 'rati':
290
- case 'rndu':
291
- case 'rpdu':
292
- case 'rtng': // RaTiNG
293
- case 'stik':
294
- case 'tmpo': // TeMPO (BPM)
295
- case 'trkn': // TRacK Number
296
- case 'tves': // TV EpiSode
297
- case 'tvnn': // TV Network Name
298
- case 'tvsh': // TV SHow Name
299
- case 'tvsn': // TV SeasoN
300
- case 'akID': // iTunes store account type
301
- case 'apID':
302
- case 'atID':
303
- case 'cmID':
304
- case 'cnID':
305
- case 'geID':
306
- case 'plID':
307
- case 'sfID': // iTunes store country
308
- case '�alb': // ALBum
309
- case '�art': // ARTist
310
- case '�ART':
311
- case '�aut':
312
- case '�cmt': // CoMmenT
313
- case '�com': // COMposer
314
- case '�cpy':
315
- case '�day': // content created year
316
- case '�dir':
317
- case '�ed1':
318
- case '�ed2':
319
- case '�ed3':
320
- case '�ed4':
321
- case '�ed5':
322
- case '�ed6':
323
- case '�ed7':
324
- case '�ed8':
325
- case '�ed9':
326
- case '�enc':
327
- case '�fmt':
328
- case '�gen': // GENre
329
- case '�grp': // GRouPing
330
- case '�hst':
331
- case '�inf':
332
- case '�lyr': // LYRics
333
- case '�mak':
334
- case '�mod':
335
- case '�nam': // full NAMe
336
- case '�ope':
337
- case '�PRD':
338
- case '�prd':
339
- case '�prf':
340
- case '�req':
341
- case '�src':
342
- case '�swr':
343
- case '�too': // encoder
344
- case '�trk': // TRacK
345
- case '�url':
346
- case '�wrn':
347
- case '�wrt': // WRiTer
348
- case '----': // itunes specific
349
- if ($atom_parent == 'udta') {
350
- // User data atom handler
351
- $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
352
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
353
- $atom_structure['data'] = substr($atom_data, 4);
354
-
355
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
356
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
357
- $info['comments']['language'][] = $atom_structure['language'];
358
- }
359
- } else {
360
- // Apple item list box atom handler
361
- $atomoffset = 0;
362
- if (substr($atom_data, 2, 2) == "\x10\xB5") {
363
- // not sure what it means, but observed on iPhone4 data.
364
- // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
365
- while ($atomoffset < strlen($atom_data)) {
366
- $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
367
- $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
368
- $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
369
- switch ($boxsmalltype) {
370
- case "\x10\xB5":
371
- $atom_structure['data'] = $boxsmalldata;
372
- break;
373
- default:
374
- $info['warning'][] = 'Unknown QuickTime smallbox type: "'.getid3_lib::PrintHexBytes($boxsmalltype).'" at offset '.$baseoffset;
375
- $atom_structure['data'] = $atom_data;
376
- break;
377
- }
378
- $atomoffset += (4 + $boxsmallsize);
379
- }
380
- } else {
381
- while ($atomoffset < strlen($atom_data)) {
382
- $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
383
- $boxtype = substr($atom_data, $atomoffset + 4, 4);
384
- $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
385
- if ($boxsize <= 1) {
386
- $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.$atomname.'" at offset: '.($atom_structure['offset'] + $atomoffset);
387
- $atom_structure['data'] = null;
388
- $atomoffset = strlen($atom_data);
389
- break;
390
- }
391
- $atomoffset += $boxsize;
392
-
393
- switch ($boxtype) {
394
- case 'mean':
395
- case 'name':
396
- $atom_structure[$boxtype] = substr($boxdata, 4);
397
- break;
398
-
399
- case 'data':
400
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
401
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
402
- switch ($atom_structure['flags_raw']) {
403
- case 0: // data flag
404
- case 21: // tmpo/cpil flag
405
- switch ($atomname) {
406
- case 'cpil':
407
- case 'pcst':
408
- case 'pgap':
409
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
410
- break;
411
-
412
- case 'tmpo':
413
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
414
- break;
415
-
416
- case 'disk':
417
- case 'trkn':
418
- $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
419
- $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
420
- $atom_structure['data'] = empty($num) ? '' : $num;
421
- $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
422
- break;
423
-
424
- case 'gnre':
425
- $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
426
- $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
427
- break;
428
-
429
- case 'rtng':
430
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
431
- $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
432
- break;
433
-
434
- case 'stik':
435
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
436
- $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
437
- break;
438
-
439
- case 'sfID':
440
- $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
441
- $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
442
- break;
443
-
444
- case 'egid':
445
- case 'purl':
446
- $atom_structure['data'] = substr($boxdata, 8);
447
- break;
448
-
449
- default:
450
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
451
- }
452
- break;
453
-
454
- case 1: // text flag
455
- case 13: // image flag
456
- default:
457
- $atom_structure['data'] = substr($boxdata, 8);
458
- break;
459
-
460
- }
461
- break;
462
-
463
- default:
464
- $info['warning'][] = 'Unknown QuickTime box type: "'.getid3_lib::PrintHexBytes($boxtype).'" at offset '.$baseoffset;
465
- $atom_structure['data'] = $atom_data;
466
-
467
- }
468
- }
469
- }
470
- }
471
- $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
472
- break;
473
-
474
-
475
- case 'play': // auto-PLAY atom
476
- $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
477
-
478
- $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
479
- break;
480
-
481
-
482
- case 'WLOC': // Window LOCation atom
483
- $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
484
- $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
485
- break;
486
-
487
-
488
- case 'LOOP': // LOOPing atom
489
- case 'SelO': // play SELection Only atom
490
- case 'AllF': // play ALL Frames atom
491
- $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
492
- break;
493
-
494
-
495
- case 'name': //
496
- case 'MCPS': // Media Cleaner PRo
497
- case '@PRM': // adobe PReMiere version
498
- case '@PRQ': // adobe PRemiere Quicktime version
499
- $atom_structure['data'] = $atom_data;
500
- break;
501
-
502
-
503
- case 'cmvd': // Compressed MooV Data atom
504
- // Code by ubergeek�ubergeek*tv based on information from
505
- // http://developer.apple.com/quicktime/icefloe/dispatch012.html
506
- $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
507
-
508
- $CompressedFileData = substr($atom_data, 4);
509
- if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
510
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
511
- } else {
512
- $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset'];
513
- }
514
- break;
515
-
516
-
517
- case 'dcom': // Data COMpression atom
518
- $atom_structure['compression_id'] = $atom_data;
519
- $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
520
- break;
521
-
522
-
523
- case 'rdrf': // Reference movie Data ReFerence atom
524
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
525
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
526
- $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
527
-
528
- $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
529
- $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
530
- switch ($atom_structure['reference_type_name']) {
531
- case 'url ':
532
- $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
533
- break;
534
-
535
- case 'alis':
536
- $atom_structure['file_alias'] = substr($atom_data, 12);
537
- break;
538
-
539
- case 'rsrc':
540
- $atom_structure['resource_alias'] = substr($atom_data, 12);
541
- break;
542
-
543
- default:
544
- $atom_structure['data'] = substr($atom_data, 12);
545
- break;
546
- }
547
- break;
548
-
549
-
550
- case 'rmqu': // Reference Movie QUality atom
551
- $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
552
- break;
553
-
554
-
555
- case 'rmcs': // Reference Movie Cpu Speed atom
556
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
557
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
558
- $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
559
- break;
560
-
561
-
562
- case 'rmvc': // Reference Movie Version Check atom
563
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
564
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
565
- $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
566
- $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
567
- $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
568
- $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
569
- break;
570
-
571
-
572
- case 'rmcd': // Reference Movie Component check atom
573
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
574
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
575
- $atom_structure['component_type'] = substr($atom_data, 4, 4);
576
- $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
577
- $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
578
- $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
579
- $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
580
- $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
581
- break;
582
-
583
-
584
- case 'rmdr': // Reference Movie Data Rate atom
585
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
586
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
587
- $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
588
-
589
- $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
590
- break;
591
-
592
-
593
- case 'rmla': // Reference Movie Language Atom
594
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
595
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
596
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
597
-
598
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
599
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
600
- $info['comments']['language'][] = $atom_structure['language'];
601
- }
602
- break;
603
-
604
-
605
- case 'rmla': // Reference Movie Language Atom
606
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
607
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
608
- $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
609
- break;
610
-
611
-
612
- case 'ptv ': // Print To Video - defines a movie's full screen mode
613
- // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
614
- $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
615
- $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
616
- $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
617
- $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
618
- $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
619
-
620
- $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
621
- $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
622
-
623
- $ptv_lookup[0] = 'normal';
624
- $ptv_lookup[1] = 'double';
625
- $ptv_lookup[2] = 'half';
626
- $ptv_lookup[3] = 'full';
627
- $ptv_lookup[4] = 'current';
628
- if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
629
- $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
630
- } else {
631
- $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')';
632
- }
633
- break;
634
-
635
-
636
- case 'stsd': // Sample Table Sample Description atom
637
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
638
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
639
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
640
- $stsdEntriesDataOffset = 8;
641
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
642
- $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
643
- $stsdEntriesDataOffset += 4;
644
- $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
645
- $stsdEntriesDataOffset += 4;
646
- $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
647
- $stsdEntriesDataOffset += 6;
648
- $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
649
- $stsdEntriesDataOffset += 2;
650
- $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
651
- $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
652
-
653
- $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
654
- $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
655
- $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
656
-
657
- switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
658
-
659
- case "\x00\x00\x00\x00":
660
- // audio atom
661
- $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
662
- $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
663
- $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
664
- $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
665
- $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
666
-
667
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
668
- case 'avc1':
669
- case 'mp4v':
670
- $info['fileformat'] = 'mp4';
671
- $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
672
- //$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] does not fully support MPEG-4 audio/video streams'; // 2011-02-18: why am I warning about this again? What's not supported?
673
- break;
674
-
675
- case 'qtvr':
676
- $info['video']['dataformat'] = 'quicktimevr';
677
- break;
678
-
679
- case 'mp4a':
680
- default:
681
- $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
682
- $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
683
- $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
684
- $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
685
- $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
686
- $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
687
- $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
688
- $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
689
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
690
- case 'raw ': // PCM
691
- case 'alac': // Apple Lossless Audio Codec
692
- $info['audio']['lossless'] = true;
693
- break;
694
- default:
695
- $info['audio']['lossless'] = false;
696
- break;
697
- }
698
- break;
699
- }
700
- break;
701
-
702
- default:
703
- switch ($atom_structure['sample_description_table'][$i]['data_format']) {
704
- case 'mp4s':
705
- $info['fileformat'] = 'mp4';
706
- break;
707
-
708
- default:
709
- // video atom
710
- $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
711
- $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
712
- $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
713
- $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
714
- $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
715
- $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
716
- $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
717
- $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
718
- $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
719
- $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
720
- $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
721
- $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
722
-
723
- $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
724
- $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
725
-
726
- if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
727
- $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
728
- $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
729
- $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
730
- $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
731
- $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
732
-
733
- $info['video']['codec'] = $info['quicktime']['video']['codec'];
734
- $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
735
- }
736
- $info['video']['lossless'] = false;
737
- $info['video']['pixel_aspect_ratio'] = (float) 1;
738
- break;
739
- }
740
- break;
741
- }
742
- switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
743
- case 'mp4a':
744
- $info['audio']['dataformat'] = 'mp4';
745
- $info['quicktime']['audio']['codec'] = 'mp4';
746
- break;
747
-
748
- case '3ivx':
749
- case '3iv1':
750
- case '3iv2':
751
- $info['video']['dataformat'] = '3ivx';
752
- break;
753
-
754
- case 'xvid':
755
- $info['video']['dataformat'] = 'xvid';
756
- break;
757
-
758
- case 'mp4v':
759
- $info['video']['dataformat'] = 'mpeg4';
760
- break;
761
-
762
- case 'divx':
763
- case 'div1':
764
- case 'div2':
765
- case 'div3':
766
- case 'div4':
767
- case 'div5':
768
- case 'div6':
769
- $info['video']['dataformat'] = 'divx';
770
- break;
771
-
772
- default:
773
- // do nothing
774
- break;
775
- }
776
- unset($atom_structure['sample_description_table'][$i]['data']);
777
- }
778
- break;
779
-
780
-
781
- case 'stts': // Sample Table Time-to-Sample atom
782
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
783
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
784
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
785
- $sttsEntriesDataOffset = 8;
786
- //$FrameRateCalculatorArray = array();
787
- $frames_count = 0;
788
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
789
- $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
790
- $sttsEntriesDataOffset += 4;
791
- $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
792
- $sttsEntriesDataOffset += 4;
793
-
794
- $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
795
-
796
- // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
797
- //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
798
- // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
799
- // if ($stts_new_framerate <= 60) {
800
- // // some atoms have durations of "1" giving a very large framerate, which probably is not right
801
- // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
802
- // }
803
- //}
804
- //
805
- //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
806
- }
807
- $info['quicktime']['stts_framecount'][] = $frames_count;
808
- //$sttsFramesTotal = 0;
809
- //$sttsSecondsTotal = 0;
810
- //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
811
- // if (($frames_per_second > 60) || ($frames_per_second < 1)) {
812
- // // not video FPS information, probably audio information
813
- // $sttsFramesTotal = 0;
814
- // $sttsSecondsTotal = 0;
815
- // break;
816
- // }
817
- // $sttsFramesTotal += $frame_count;
818
- // $sttsSecondsTotal += $frame_count / $frames_per_second;
819
- //}
820
- //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
821
- // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
822
- // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
823
- // }
824
- //}
825
- break;
826
-
827
-
828
- case 'stss': // Sample Table Sync Sample (key frames) atom
829
- if ($ParseAllPossibleAtoms) {
830
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
831
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
832
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
833
- $stssEntriesDataOffset = 8;
834
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
835
- $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
836
- $stssEntriesDataOffset += 4;
837
- }
838
- }
839
- break;
840
-
841
-
842
- case 'stsc': // Sample Table Sample-to-Chunk atom
843
- if ($ParseAllPossibleAtoms) {
844
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
845
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
846
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
847
- $stscEntriesDataOffset = 8;
848
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
849
- $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
850
- $stscEntriesDataOffset += 4;
851
- $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
852
- $stscEntriesDataOffset += 4;
853
- $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
854
- $stscEntriesDataOffset += 4;
855
- }
856
- }
857
- break;
858
-
859
-
860
- case 'stsz': // Sample Table SiZe atom
861
- if ($ParseAllPossibleAtoms) {
862
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
863
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
864
- $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
865
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
866
- $stszEntriesDataOffset = 12;
867
- if ($atom_structure['sample_size'] == 0) {
868
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
869
- $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
870
- $stszEntriesDataOffset += 4;
871
- }
872
- }
873
- }
874
- break;
875
-
876
-
877
- case 'stco': // Sample Table Chunk Offset atom
878
- if ($ParseAllPossibleAtoms) {
879
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
880
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
881
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
882
- $stcoEntriesDataOffset = 8;
883
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
884
- $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
885
- $stcoEntriesDataOffset += 4;
886
- }
887
- }
888
- break;
889
-
890
-
891
- case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
892
- if ($ParseAllPossibleAtoms) {
893
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
894
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
895
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
896
- $stcoEntriesDataOffset = 8;
897
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
898
- $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
899
- $stcoEntriesDataOffset += 8;
900
- }
901
- }
902
- break;
903
-
904
-
905
- case 'dref': // Data REFerence atom
906
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
907
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
908
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
909
- $drefDataOffset = 8;
910
- for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
911
- $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
912
- $drefDataOffset += 4;
913
- $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
914
- $drefDataOffset += 4;
915
- $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
916
- $drefDataOffset += 1;
917
- $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
918
- $drefDataOffset += 3;
919
- $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
920
- $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
921
-
922
- $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
923
- }
924
- break;
925
-
926
-
927
- case 'gmin': // base Media INformation atom
928
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
929
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
930
- $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
931
- $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
932
- $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
933
- $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
934
- $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
935
- $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
936
- break;
937
-
938
-
939
- case 'smhd': // Sound Media information HeaDer atom
940
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
941
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
942
- $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
943
- $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
944
- break;
945
-
946
-
947
- case 'vmhd': // Video Media information HeaDer atom
948
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
949
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
950
- $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
951
- $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
952
- $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
953
- $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
954
-
955
- $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
956
- break;
957
-
958
-
959
- case 'hdlr': // HanDLeR reference atom
960
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
961
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
962
- $atom_structure['component_type'] = substr($atom_data, 4, 4);
963
- $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
964
- $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
965
- $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
966
- $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
967
- $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
968
-
969
- if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
970
- $info['video']['dataformat'] = 'quicktimevr';
971
- }
972
- break;
973
-
974
-
975
- case 'mdhd': // MeDia HeaDer atom
976
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
977
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
978
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
979
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
980
- $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
981
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
982
- $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
983
- $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
984
-
985
- if ($atom_structure['time_scale'] == 0) {
986
- $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
987
- return false;
988
- }
989
- $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
990
-
991
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
992
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
993
- $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
994
- $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
995
- if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
996
- $info['comments']['language'][] = $atom_structure['language'];
997
- }
998
- break;
999
-
1000
-
1001
- case 'pnot': // Preview atom
1002
- $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format"
1003
- $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00
1004
- $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT'
1005
- $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
1006
-
1007
- $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
1008
- break;
1009
-
1010
-
1011
- case 'crgn': // Clipping ReGioN atom
1012
- $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
1013
- $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
1014
- $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region.
1015
- break;
1016
-
1017
-
1018
- case 'load': // track LOAD settings atom
1019
- $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1020
- $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1021
- $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1022
- $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1023
-
1024
- $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1025
- $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1026
- break;
1027
-
1028
-
1029
- case 'tmcd': // TiMe CoDe atom
1030
- case 'chap': // CHAPter list atom
1031
- case 'sync': // SYNChronization atom
1032
- case 'scpt': // tranSCriPT atom
1033
- case 'ssrc': // non-primary SouRCe atom
1034
- for ($i = 0; $i < (strlen($atom_data) % 4); $i++) {
1035
- $atom_structure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $i * 4, 4));
1036
- }
1037
- break;
1038
-
1039
-
1040
- case 'elst': // Edit LiST atom
1041
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1042
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1043
- $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1044
- for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1045
- $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1046
- $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1047
- $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1048
- }
1049
- break;
1050
-
1051
-
1052
- case 'kmat': // compressed MATte atom
1053
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1054
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1055
- $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1056
- break;
1057
-
1058
-
1059
- case 'ctab': // Color TABle atom
1060
- $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
1061
- $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
1062
- $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
1063
- for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1064
- $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1065
- $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1066
- $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1067
- $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1068
- }
1069
- break;
1070
-
1071
-
1072
- case 'mvhd': // MoVie HeaDer atom
1073
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1074
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1075
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1076
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1077
- $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1078
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1079
- $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
1080
- $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
1081
- $atom_structure['reserved'] = substr($atom_data, 26, 10);
1082
- $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
1083
- $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1084
- $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
1085
- $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1086
- $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1087
- $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
1088
- $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1089
- $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1090
- $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1091
- $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
1092
- $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
1093
- $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
1094
- $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
1095
- $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
1096
- $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
1097
- $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
1098
-
1099
- if ($atom_structure['time_scale'] == 0) {
1100
- $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
1101
- return false;
1102
- }
1103
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1104
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1105
- $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1106
- $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
1107
- $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1108
- break;
1109
-
1110
-
1111
- case 'tkhd': // TracK HeaDer atom
1112
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1113
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1114
- $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1115
- $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1116
- $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1117
- $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1118
- $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1119
- $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
1120
- $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
1121
- $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
1122
- $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
1123
- $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1124
- $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1125
- $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
1126
- $atom_structure['matrix_u'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1127
- $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1128
- $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
1129
- $atom_structure['matrix_v'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1130
- $atom_structure['matrix_x'] = getid3_lib::FixedPoint2_30(substr($atom_data, 64, 4));
1131
- $atom_structure['matrix_y'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1132
- $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
1133
- $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
1134
- $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
1135
-
1136
- $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1137
- $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1138
- $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1139
- $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1140
- $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1141
- $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1142
-
1143
- if ($atom_structure['flags']['enabled'] == 1) {
1144
- if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1145
- $info['video']['resolution_x'] = $atom_structure['width'];
1146
- $info['video']['resolution_y'] = $atom_structure['height'];
1147
- }
1148
- $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1149
- $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1150
- $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
1151
- $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
1152
- } else {
1153
- // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
1154
- //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
1155
- //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
1156
- //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); }
1157
- }
1158
- break;
1159
-
1160
-
1161
- case 'iods': // Initial Object DeScriptor atom
1162
- // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
1163
- // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
1164
- $offset = 0;
1165
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1166
- $offset += 1;
1167
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
1168
- $offset += 3;
1169
- $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1170
- $offset += 1;
1171
- $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1172
- //$offset already adjusted by quicktime_read_mp4_descr_length()
1173
- $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
1174
- $offset += 2;
1175
- $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1176
- $offset += 1;
1177
- $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1178
- $offset += 1;
1179
- $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1180
- $offset += 1;
1181
- $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1182
- $offset += 1;
1183
- $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1184
- $offset += 1;
1185
-
1186
- $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
1187
- for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1188
- $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1189
- $offset += 1;
1190
- $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1191
- //$offset already adjusted by quicktime_read_mp4_descr_length()
1192
- $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
1193
- $offset += 4;
1194
- }
1195
-
1196
- $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
1197
- $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
1198
- break;
1199
-
1200
- case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
1201
- $atom_structure['signature'] = substr($atom_data, 0, 4);
1202
- $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1203
- $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1204
- break;
1205
-
1206
- case 'mdat': // Media DATa atom
1207
- case 'free': // FREE space atom
1208
- case 'skip': // SKIP atom
1209
- case 'wide': // 64-bit expansion placeholder atom
1210
- // 'mdat' data is too big to deal with, contains no useful metadata
1211
- // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1212
-
1213
- // When writing QuickTime files, it is sometimes necessary to update an atom's size.
1214
- // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1215
- // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1216
- // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1217
- // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1218
- // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1219
- // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1220
- break;
1221
-
1222
-
1223
- case 'nsav': // NoSAVe atom
1224
- // http://developer.apple.com/technotes/tn/tn2038.html
1225
- $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1226
- break;
1227
-
1228
- case 'ctyp': // Controller TYPe atom (seen on QTVR)
1229
- // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1230
- // some controller names are:
1231
- // 0x00 + 'std' for linear movie
1232
- // 'none' for no controls
1233
- $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1234
- $info['quicktime']['controller'] = $atom_structure['ctyp'];
1235
- switch ($atom_structure['ctyp']) {
1236
- case 'qtvr':
1237
- $info['video']['dataformat'] = 'quicktimevr';
1238
- break;
1239
- }
1240
- break;
1241
-
1242
- case 'pano': // PANOrama track (seen on QTVR)
1243
- $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1244
- break;
1245
-
1246
- case 'hint': // HINT track
1247
- case 'hinf': //
1248
- case 'hinv': //
1249
- case 'hnti': //
1250
- $info['quicktime']['hinting'] = true;
1251
- break;
1252
-
1253
- case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1254
- for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1255
- $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1256
- }
1257
- break;
1258
-
1259
-
1260
- // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
1261
- case 'FXTC': // Something to do with Adobe After Effects (?)
1262
- case 'PrmA':
1263
- case 'code':
1264
- case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1265
- case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
1266
- // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
1267
- // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
1268
- // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
1269
- case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1270
- case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1271
- case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1272
- case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1273
- //$atom_structure['data'] = $atom_data;
1274
- break;
1275
-
1276
- case '�xyz': // GPS latitude+longitude+altitude
1277
- $atom_structure['data'] = $atom_data;
1278
- if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1279
- @list($all, $latitude, $longitude, $altitude) = $matches;
1280
- $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
1281
- $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
1282
- if (!empty($altitude)) {
1283
- $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
1284
- }
1285
- } else {
1286
- $info['warning'][] = 'QuickTime atom "�xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.';
1287
- }
1288
- break;
1289
-
1290
- case 'NCDT':
1291
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1292
- // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1293
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
1294
- break;
1295
- case 'NCTH': // Nikon Camera THumbnail image
1296
- case 'NCVW': // Nikon Camera preVieW image
1297
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1298
- if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1299
- $atom_structure['data'] = $atom_data;
1300
- $atom_structure['image_mime'] = 'image/jpeg';
1301
- $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1302
- $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
1303
- }
1304
- break;
1305
- case 'NCHD': // MakerNoteVersion
1306
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1307
- $atom_structure['data'] = $atom_data;
1308
- break;
1309
- case 'NCTG': // NikonTags
1310
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1311
- $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
1312
- break;
1313
- case 'NCDB': // NikonTags
1314
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1315
- $atom_structure['data'] = $atom_data;
1316
- break;
1317
-
1318
- case "\x00\x00\x00\x00":
1319
- case 'meta': // METAdata atom
1320
- // some kind of metacontainer, may contain a big data dump such as:
1321
- // mdta keys mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst data DEApple 0 (data DE2011-05-11T17:54:04+0200 2 *data DE+52.4936+013.3897+040.247/ data DE4.3.1 data DEiPhone 4
1322
- // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
1323
-
1324
- $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1325
- $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1326
- $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1327
- //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1328
- break;
1329
-
1330
- case 'data': // metaDATA atom
1331
- // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
1332
- $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1333
- $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
1334
- $atom_structure['data'] = substr($atom_data, 4 + 4);
1335
- break;
1336
-
1337
- default:
1338
- $info['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset;
1339
- $atom_structure['data'] = $atom_data;
1340
- break;
1341
- }
1342
- array_pop($atomHierarchy);
1343
- return $atom_structure;
1344
- }
1345
-
1346
- public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
1347
- //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
1348
- $atom_structure = false;
1349
- $subatomoffset = 0;
1350
- $subatomcounter = 0;
1351
- if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
1352
- return false;
1353
- }
1354
- while ($subatomoffset < strlen($atom_data)) {
1355
- $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
1356
- $subatomname = substr($atom_data, $subatomoffset + 4, 4);
1357
- $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
1358
- if ($subatomsize == 0) {
1359
- // Furthermore, for historical reasons the list of atoms is optionally
1360
- // terminated by a 32-bit integer set to 0. If you are writing a program
1361
- // to read user data atoms, you should allow for the terminating 0.
1362
- return $atom_structure;
1363
- }
1364
-
1365
- $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
1366
-
1367
- $subatomoffset += $subatomsize;
1368
- $subatomcounter++;
1369
- }
1370
- return $atom_structure;
1371
- }
1372
-
1373
-
1374
- public function quicktime_read_mp4_descr_length($data, &$offset) {
1375
- // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
1376
- $num_bytes = 0;
1377
- $length = 0;
1378
- do {
1379
- $b = ord(substr($data, $offset++, 1));
1380
- $length = ($length << 7) | ($b & 0x7F);
1381
- } while (($b & 0x80) && ($num_bytes++ < 4));
1382
- return $length;
1383
- }
1384
-
1385
-
1386
- public function QuicktimeLanguageLookup($languageid) {
1387
- static $QuicktimeLanguageLookup = array();
1388
- if (empty($QuicktimeLanguageLookup)) {
1389
- $QuicktimeLanguageLookup[0] = 'English';
1390
- $QuicktimeLanguageLookup[1] = 'French';
1391
- $QuicktimeLanguageLookup[2] = 'German';
1392
- $QuicktimeLanguageLookup[3] = 'Italian';
1393
- $QuicktimeLanguageLookup[4] = 'Dutch';
1394
- $QuicktimeLanguageLookup[5] = 'Swedish';
1395
- $QuicktimeLanguageLookup[6] = 'Spanish';
1396
- $QuicktimeLanguageLookup[7] = 'Danish';
1397
- $QuicktimeLanguageLookup[8] = 'Portuguese';
1398
- $QuicktimeLanguageLookup[9] = 'Norwegian';
1399
- $QuicktimeLanguageLookup[10] = 'Hebrew';
1400
- $QuicktimeLanguageLookup[11] = 'Japanese';
1401
- $QuicktimeLanguageLookup[12] = 'Arabic';
1402
- $QuicktimeLanguageLookup[13] = 'Finnish';
1403
- $QuicktimeLanguageLookup[14] = 'Greek';
1404
- $QuicktimeLanguageLookup[15] = 'Icelandic';
1405
- $QuicktimeLanguageLookup[16] = 'Maltese';
1406
- $QuicktimeLanguageLookup[17] = 'Turkish';
1407
- $QuicktimeLanguageLookup[18] = 'Croatian';
1408
- $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)';
1409
- $QuicktimeLanguageLookup[20] = 'Urdu';
1410
- $QuicktimeLanguageLookup[21] = 'Hindi';
1411
- $QuicktimeLanguageLookup[22] = 'Thai';
1412
- $QuicktimeLanguageLookup[23] = 'Korean';
1413
- $QuicktimeLanguageLookup[24] = 'Lithuanian';
1414
- $QuicktimeLanguageLookup[25] = 'Polish';
1415
- $QuicktimeLanguageLookup[26] = 'Hungarian';
1416
- $QuicktimeLanguageLookup[27] = 'Estonian';
1417
- $QuicktimeLanguageLookup[28] = 'Lettish';
1418
- $QuicktimeLanguageLookup[28] = 'Latvian';
1419
- $QuicktimeLanguageLookup[29] = 'Saamisk';
1420
- $QuicktimeLanguageLookup[29] = 'Lappish';
1421
- $QuicktimeLanguageLookup[30] = 'Faeroese';
1422
- $QuicktimeLanguageLookup[31] = 'Farsi';
1423
- $QuicktimeLanguageLookup[31] = 'Persian';
1424
- $QuicktimeLanguageLookup[32] = 'Russian';
1425
- $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)';
1426
- $QuicktimeLanguageLookup[34] = 'Flemish';
1427
- $QuicktimeLanguageLookup[35] = 'Irish';
1428
- $QuicktimeLanguageLookup[36] = 'Albanian';
1429
- $QuicktimeLanguageLookup[37] = 'Romanian';
1430
- $QuicktimeLanguageLookup[38] = 'Czech';
1431
- $QuicktimeLanguageLookup[39] = 'Slovak';
1432
- $QuicktimeLanguageLookup[40] = 'Slovenian';
1433
- $QuicktimeLanguageLookup[41] = 'Yiddish';
1434
- $QuicktimeLanguageLookup[42] = 'Serbian';
1435
- $QuicktimeLanguageLookup[43] = 'Macedonian';
1436
- $QuicktimeLanguageLookup[44] = 'Bulgarian';
1437
- $QuicktimeLanguageLookup[45] = 'Ukrainian';
1438
- $QuicktimeLanguageLookup[46] = 'Byelorussian';
1439
- $QuicktimeLanguageLookup[47] = 'Uzbek';
1440
- $QuicktimeLanguageLookup[48] = 'Kazakh';
1441
- $QuicktimeLanguageLookup[49] = 'Azerbaijani';
1442
- $QuicktimeLanguageLookup[50] = 'AzerbaijanAr';
1443
- $QuicktimeLanguageLookup[51] = 'Armenian';
1444
- $QuicktimeLanguageLookup[52] = 'Georgian';
1445
- $QuicktimeLanguageLookup[53] = 'Moldavian';
1446
- $QuicktimeLanguageLookup[54] = 'Kirghiz';
1447
- $QuicktimeLanguageLookup[55] = 'Tajiki';
1448
- $QuicktimeLanguageLookup[56] = 'Turkmen';
1449
- $QuicktimeLanguageLookup[57] = 'Mongolian';
1450
- $QuicktimeLanguageLookup[58] = 'MongolianCyr';
1451
- $QuicktimeLanguageLookup[59] = 'Pashto';
1452
- $QuicktimeLanguageLookup[60] = 'Kurdish';
1453
- $QuicktimeLanguageLookup[61] = 'Kashmiri';
1454
- $QuicktimeLanguageLookup[62] = 'Sindhi';
1455
- $QuicktimeLanguageLookup[63] = 'Tibetan';
1456
- $QuicktimeLanguageLookup[64] = 'Nepali';
1457
- $QuicktimeLanguageLookup[65] = 'Sanskrit';
1458
- $QuicktimeLanguageLookup[66] = 'Marathi';
1459
- $QuicktimeLanguageLookup[67] = 'Bengali';
1460
- $QuicktimeLanguageLookup[68] = 'Assamese';
1461
- $QuicktimeLanguageLookup[69] = 'Gujarati';
1462
- $QuicktimeLanguageLookup[70] = 'Punjabi';
1463
- $QuicktimeLanguageLookup[71] = 'Oriya';
1464
- $QuicktimeLanguageLookup[72] = 'Malayalam';
1465
- $QuicktimeLanguageLookup[73] = 'Kannada';
1466
- $QuicktimeLanguageLookup[74] = 'Tamil';
1467
- $QuicktimeLanguageLookup[75] = 'Telugu';
1468
- $QuicktimeLanguageLookup[76] = 'Sinhalese';
1469
- $QuicktimeLanguageLookup[77] = 'Burmese';
1470
- $QuicktimeLanguageLookup[78] = 'Khmer';
1471
- $QuicktimeLanguageLookup[79] = 'Lao';
1472
- $QuicktimeLanguageLookup[80] = 'Vietnamese';
1473
- $QuicktimeLanguageLookup[81] = 'Indonesian';
1474
- $QuicktimeLanguageLookup[82] = 'Tagalog';
1475
- $QuicktimeLanguageLookup[83] = 'MalayRoman';
1476
- $QuicktimeLanguageLookup[84] = 'MalayArabic';
1477
- $QuicktimeLanguageLookup[85] = 'Amharic';
1478
- $QuicktimeLanguageLookup[86] = 'Tigrinya';
1479
- $QuicktimeLanguageLookup[87] = 'Galla';
1480
- $QuicktimeLanguageLookup[87] = 'Oromo';
1481
- $QuicktimeLanguageLookup[88] = 'Somali';
1482
- $QuicktimeLanguageLookup[89] = 'Swahili';
1483
- $QuicktimeLanguageLookup[90] = 'Ruanda';
1484
- $QuicktimeLanguageLookup[91] = 'Rundi';
1485
- $QuicktimeLanguageLookup[92] = 'Chewa';
1486
- $QuicktimeLanguageLookup[93] = 'Malagasy';
1487
- $QuicktimeLanguageLookup[94] = 'Esperanto';
1488
- $QuicktimeLanguageLookup[128] = 'Welsh';
1489
- $QuicktimeLanguageLookup[129] = 'Basque';
1490
- $QuicktimeLanguageLookup[130] = 'Catalan';
1491
- $QuicktimeLanguageLookup[131] = 'Latin';
1492
- $QuicktimeLanguageLookup[132] = 'Quechua';
1493
- $QuicktimeLanguageLookup[133] = 'Guarani';
1494
- $QuicktimeLanguageLookup[134] = 'Aymara';
1495
- $QuicktimeLanguageLookup[135] = 'Tatar';
1496
- $QuicktimeLanguageLookup[136] = 'Uighur';
1497
- $QuicktimeLanguageLookup[137] = 'Dzongkha';
1498
- $QuicktimeLanguageLookup[138] = 'JavaneseRom';
1499
- }
1500
- return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
1501
- }
1502
-
1503
- public function QuicktimeVideoCodecLookup($codecid) {
1504
- static $QuicktimeVideoCodecLookup = array();
1505
- if (empty($QuicktimeVideoCodecLookup)) {
1506
- $QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
1507
- $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
1508
- $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
1509
- $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
1510
- $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
1511
- $QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
1512
- $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
1513
- $QuicktimeVideoCodecLookup['b16g'] = '16Gray';
1514
- $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
1515
- $QuicktimeVideoCodecLookup['b48r'] = '48RGB';
1516
- $QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
1517
- $QuicktimeVideoCodecLookup['base'] = 'Base';
1518
- $QuicktimeVideoCodecLookup['clou'] = 'Cloud';
1519
- $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
1520
- $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
1521
- $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
1522
- $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
1523
- $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
1524
- $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
1525
- $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
1526
- $QuicktimeVideoCodecLookup['fire'] = 'Fire';
1527
- $QuicktimeVideoCodecLookup['flic'] = 'FLC';
1528
- $QuicktimeVideoCodecLookup['gif '] = 'GIF';
1529
- $QuicktimeVideoCodecLookup['h261'] = 'H261';
1530
- $QuicktimeVideoCodecLookup['h263'] = 'H263';
1531
- $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
1532
- $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
1533
- $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
1534
- $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
1535
- $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
1536
- $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
1537
- $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
1538
- $QuicktimeVideoCodecLookup['path'] = 'Vector';
1539
- $QuicktimeVideoCodecLookup['png '] = 'PNG';
1540
- $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
1541
- $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
1542
- $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
1543
- $QuicktimeVideoCodecLookup['raw '] = 'RAW';
1544
- $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
1545
- $QuicktimeVideoCodecLookup['rpza'] = 'Video';
1546
- $QuicktimeVideoCodecLookup['smc '] = 'Graphics';
1547
- $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
1548
- $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
1549
- $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
1550
- $QuicktimeVideoCodecLookup['tga '] = 'Targa';
1551
- $QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
1552
- $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
1553
- $QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
1554
- $QuicktimeVideoCodecLookup['y420'] = 'YUV420';
1555
- $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
1556
- $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
1557
- $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
1558
- }
1559
- return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
1560
- }
1561
-
1562
- public function QuicktimeAudioCodecLookup($codecid) {
1563
- static $QuicktimeAudioCodecLookup = array();
1564
- if (empty($QuicktimeAudioCodecLookup)) {
1565
- $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias';
1566
- $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC';
1567
- $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1';
1568
- $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec';
1569
- $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1';
1570
- $QuicktimeAudioCodecLookup['conv'] = 'Sample Format';
1571
- $QuicktimeAudioCodecLookup['dvca'] = 'DV';
1572
- $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1';
1573
- $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer';
1574
- $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point';
1575
- $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point';
1576
- $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1';
1577
- $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer';
1578
- $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer';
1579
- $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1';
1580
- $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
1581
- $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
1582
- $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer';
1583
- $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer';
1584
- $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC';
1585
- $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
1586
- $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
1587
- $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
1588
- $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding';
1589
- $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice';
1590
- $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2';
1591
- $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1';
1592
- $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate';
1593
- $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate';
1594
- $QuicktimeAudioCodecLookup['raw '] = 'raw PCM';
1595
- $QuicktimeAudioCodecLookup['sour'] = 'Sound Source';
1596
- $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)';
1597
- $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II';
1598
- $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II';
1599
- $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II';
1600
- $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II';
1601
- $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)';
1602
- $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1';
1603
- }
1604
- return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
1605
- }
1606
-
1607
- public function QuicktimeDCOMLookup($compressionid) {
1608
- static $QuicktimeDCOMLookup = array();
1609
- if (empty($QuicktimeDCOMLookup)) {
1610
- $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
1611
- $QuicktimeDCOMLookup['adec'] = 'Apple Compression';
1612
- }
1613
- return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
1614
- }
1615
-
1616
- public function QuicktimeColorNameLookup($colordepthid) {
1617
- static $QuicktimeColorNameLookup = array();
1618
- if (empty($QuicktimeColorNameLookup)) {
1619
- $QuicktimeColorNameLookup[1] = '2-color (monochrome)';
1620
- $QuicktimeColorNameLookup[2] = '4-color';
1621
- $QuicktimeColorNameLookup[4] = '16-color';
1622
- $QuicktimeColorNameLookup[8] = '256-color';
1623
- $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
1624
- $QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
1625
- $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
1626
- $QuicktimeColorNameLookup[33] = 'black & white';
1627
- $QuicktimeColorNameLookup[34] = '4-gray';
1628
- $QuicktimeColorNameLookup[36] = '16-gray';
1629
- $QuicktimeColorNameLookup[40] = '256-gray';
1630
- }
1631
- return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
1632
- }
1633
-
1634
- public function QuicktimeSTIKLookup($stik) {
1635
- static $QuicktimeSTIKLookup = array();
1636
- if (empty($QuicktimeSTIKLookup)) {
1637
- $QuicktimeSTIKLookup[0] = 'Movie';
1638
- $QuicktimeSTIKLookup[1] = 'Normal';
1639
- $QuicktimeSTIKLookup[2] = 'Audiobook';
1640
- $QuicktimeSTIKLookup[5] = 'Whacked Bookmark';
1641
- $QuicktimeSTIKLookup[6] = 'Music Video';
1642
- $QuicktimeSTIKLookup[9] = 'Short Film';
1643
- $QuicktimeSTIKLookup[10] = 'TV Show';
1644
- $QuicktimeSTIKLookup[11] = 'Booklet';
1645
- $QuicktimeSTIKLookup[14] = 'Ringtone';
1646
- $QuicktimeSTIKLookup[21] = 'Podcast';
1647
- }
1648
- return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
1649
- }
1650
-
1651
- public function QuicktimeIODSaudioProfileName($audio_profile_id) {
1652
- static $QuicktimeIODSaudioProfileNameLookup = array();
1653
- if (empty($QuicktimeIODSaudioProfileNameLookup)) {
1654
- $QuicktimeIODSaudioProfileNameLookup = array(
1655
- 0x00 => 'ISO Reserved (0x00)',
1656
- 0x01 => 'Main Audio Profile @ Level 1',
1657
- 0x02 => 'Main Audio Profile @ Level 2',
1658
- 0x03 => 'Main Audio Profile @ Level 3',
1659
- 0x04 => 'Main Audio Profile @ Level 4',
1660
- 0x05 => 'Scalable Audio Profile @ Level 1',
1661
- 0x06 => 'Scalable Audio Profile @ Level 2',
1662
- 0x07 => 'Scalable Audio Profile @ Level 3',
1663
- 0x08 => 'Scalable Audio Profile @ Level 4',
1664
- 0x09 => 'Speech Audio Profile @ Level 1',
1665
- 0x0A => 'Speech Audio Profile @ Level 2',
1666
- 0x0B => 'Synthetic Audio Profile @ Level 1',
1667
- 0x0C => 'Synthetic Audio Profile @ Level 2',
1668
- 0x0D => 'Synthetic Audio Profile @ Level 3',
1669
- 0x0E => 'High Quality Audio Profile @ Level 1',
1670
- 0x0F => 'High Quality Audio Profile @ Level 2',
1671
- 0x10 => 'High Quality Audio Profile @ Level 3',
1672
- 0x11 => 'High Quality Audio Profile @ Level 4',
1673
- 0x12 => 'High Quality Audio Profile @ Level 5',
1674
- 0x13 => 'High Quality Audio Profile @ Level 6',
1675
- 0x14 => 'High Quality Audio Profile @ Level 7',
1676
- 0x15 => 'High Quality Audio Profile @ Level 8',
1677
- 0x16 => 'Low Delay Audio Profile @ Level 1',
1678
- 0x17 => 'Low Delay Audio Profile @ Level 2',
1679
- 0x18 => 'Low Delay Audio Profile @ Level 3',
1680
- 0x19 => 'Low Delay Audio Profile @ Level 4',
1681
- 0x1A => 'Low Delay Audio Profile @ Level 5',
1682
- 0x1B => 'Low Delay Audio Profile @ Level 6',
1683
- 0x1C => 'Low Delay Audio Profile @ Level 7',
1684
- 0x1D => 'Low Delay Audio Profile @ Level 8',
1685
- 0x1E => 'Natural Audio Profile @ Level 1',
1686
- 0x1F => 'Natural Audio Profile @ Level 2',
1687
- 0x20 => 'Natural Audio Profile @ Level 3',
1688
- 0x21 => 'Natural Audio Profile @ Level 4',
1689
- 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
1690
- 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
1691
- 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
1692
- 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
1693
- 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
1694
- 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
1695
- 0x28 => 'AAC Profile @ Level 1',
1696
- 0x29 => 'AAC Profile @ Level 2',
1697
- 0x2A => 'AAC Profile @ Level 4',
1698
- 0x2B => 'AAC Profile @ Level 5',
1699
- 0x2C => 'High Efficiency AAC Profile @ Level 2',
1700
- 0x2D => 'High Efficiency AAC Profile @ Level 3',
1701
- 0x2E => 'High Efficiency AAC Profile @ Level 4',
1702
- 0x2F => 'High Efficiency AAC Profile @ Level 5',
1703
- 0xFE => 'Not part of MPEG-4 audio profiles',
1704
- 0xFF => 'No audio capability required',
1705
- );
1706
- }
1707
- return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
1708
- }
1709
-
1710
-
1711
- public function QuicktimeIODSvideoProfileName($video_profile_id) {
1712
- static $QuicktimeIODSvideoProfileNameLookup = array();
1713
- if (empty($QuicktimeIODSvideoProfileNameLookup)) {
1714
- $QuicktimeIODSvideoProfileNameLookup = array(
1715
- 0x00 => 'Reserved (0x00) Profile',
1716
- 0x01 => 'Simple Profile @ Level 1',
1717
- 0x02 => 'Simple Profile @ Level 2',
1718
- 0x03 => 'Simple Profile @ Level 3',
1719
- 0x08 => 'Simple Profile @ Level 0',
1720
- 0x10 => 'Simple Scalable Profile @ Level 0',
1721
- 0x11 => 'Simple Scalable Profile @ Level 1',
1722
- 0x12 => 'Simple Scalable Profile @ Level 2',
1723
- 0x15 => 'AVC/H264 Profile',
1724
- 0x21 => 'Core Profile @ Level 1',
1725
- 0x22 => 'Core Profile @ Level 2',
1726
- 0x32 => 'Main Profile @ Level 2',
1727
- 0x33 => 'Main Profile @ Level 3',
1728
- 0x34 => 'Main Profile @ Level 4',
1729
- 0x42 => 'N-bit Profile @ Level 2',
1730
- 0x51 => 'Scalable Texture Profile @ Level 1',
1731
- 0x61 => 'Simple Face Animation Profile @ Level 1',
1732
- 0x62 => 'Simple Face Animation Profile @ Level 2',
1733
- 0x63 => 'Simple FBA Profile @ Level 1',
1734
- 0x64 => 'Simple FBA Profile @ Level 2',
1735
- 0x71 => 'Basic Animated Texture Profile @ Level 1',
1736
- 0x72 => 'Basic Animated Texture Profile @ Level 2',
1737
- 0x81 => 'Hybrid Profile @ Level 1',
1738
- 0x82 => 'Hybrid Profile @ Level 2',
1739
- 0x91 => 'Advanced Real Time Simple Profile @ Level 1',
1740
- 0x92 => 'Advanced Real Time Simple Profile @ Level 2',
1741
- 0x93 => 'Advanced Real Time Simple Profile @ Level 3',
1742
- 0x94 => 'Advanced Real Time Simple Profile @ Level 4',
1743
- 0xA1 => 'Core Scalable Profile @ Level1',
1744
- 0xA2 => 'Core Scalable Profile @ Level2',
1745
- 0xA3 => 'Core Scalable Profile @ Level3',
1746
- 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
1747
- 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
1748
- 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
1749
- 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
1750
- 0xC1 => 'Advanced Core Profile @ Level 1',
1751
- 0xC2 => 'Advanced Core Profile @ Level 2',
1752
- 0xD1 => 'Advanced Scalable Texture @ Level1',
1753
- 0xD2 => 'Advanced Scalable Texture @ Level2',
1754
- 0xE1 => 'Simple Studio Profile @ Level 1',
1755
- 0xE2 => 'Simple Studio Profile @ Level 2',
1756
- 0xE3 => 'Simple Studio Profile @ Level 3',
1757
- 0xE4 => 'Simple Studio Profile @ Level 4',
1758
- 0xE5 => 'Core Studio Profile @ Level 1',
1759
- 0xE6 => 'Core Studio Profile @ Level 2',
1760
- 0xE7 => 'Core Studio Profile @ Level 3',
1761
- 0xE8 => 'Core Studio Profile @ Level 4',
1762
- 0xF0 => 'Advanced Simple Profile @ Level 0',
1763
- 0xF1 => 'Advanced Simple Profile @ Level 1',
1764
- 0xF2 => 'Advanced Simple Profile @ Level 2',
1765
- 0xF3 => 'Advanced Simple Profile @ Level 3',
1766
- 0xF4 => 'Advanced Simple Profile @ Level 4',
1767
- 0xF5 => 'Advanced Simple Profile @ Level 5',
1768
- 0xF7 => 'Advanced Simple Profile @ Level 3b',
1769
- 0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
1770
- 0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
1771
- 0xFA => 'Fine Granularity Scalable Profile @ Level 2',
1772
- 0xFB => 'Fine Granularity Scalable Profile @ Level 3',
1773
- 0xFC => 'Fine Granularity Scalable Profile @ Level 4',
1774
- 0xFD => 'Fine Granularity Scalable Profile @ Level 5',
1775
- 0xFE => 'Not part of MPEG-4 Visual profiles',
1776
- 0xFF => 'No visual capability required',
1777
- );
1778
- }
1779
- return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
1780
- }
1781
-
1782
-
1783
- public function QuicktimeContentRatingLookup($rtng) {
1784
- static $QuicktimeContentRatingLookup = array();
1785
- if (empty($QuicktimeContentRatingLookup)) {
1786
- $QuicktimeContentRatingLookup[0] = 'None';
1787
- $QuicktimeContentRatingLookup[2] = 'Clean';
1788
- $QuicktimeContentRatingLookup[4] = 'Explicit';
1789
- }
1790
- return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
1791
- }
1792
-
1793
- public function QuicktimeStoreAccountTypeLookup($akid) {
1794
- static $QuicktimeStoreAccountTypeLookup = array();
1795
- if (empty($QuicktimeStoreAccountTypeLookup)) {
1796
- $QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
1797
- $QuicktimeStoreAccountTypeLookup[1] = 'AOL';
1798
- }
1799
- return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
1800
- }
1801
-
1802
- public function QuicktimeStoreFrontCodeLookup($sfid) {
1803
- static $QuicktimeStoreFrontCodeLookup = array();
1804
- if (empty($QuicktimeStoreFrontCodeLookup)) {
1805
- $QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
1806
- $QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
1807
- $QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
1808
- $QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
1809
- $QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
1810
- $QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
1811
- $QuicktimeStoreFrontCodeLookup[143442] = 'France';
1812
- $QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
1813
- $QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
1814
- $QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
1815
- $QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
1816
- $QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
1817
- $QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
1818
- $QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
1819
- $QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
1820
- $QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
1821
- $QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
1822
- $QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
1823
- $QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
1824
- $QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
1825
- $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
1826
- $QuicktimeStoreFrontCodeLookup[143441] = 'United States';
1827
- }
1828
- return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
1829
- }
1830
-
1831
- public function QuicktimeParseNikonNCTG($atom_data) {
1832
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1833
- // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1834
- // Data is stored as records of:
1835
- // * 4 bytes record type
1836
- // * 2 bytes size of data field type:
1837
- // 0x0001 = flag (size field *= 1-byte)
1838
- // 0x0002 = char (size field *= 1-byte)
1839
- // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
1840
- // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
1841
- // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
1842
- // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
1843
- // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
1844
- // * 2 bytes data size field
1845
- // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
1846
- // all integers are stored BigEndian
1847
-
1848
- $NCTGtagName = array(
1849
- 0x00000001 => 'Make',
1850
- 0x00000002 => 'Model',
1851
- 0x00000003 => 'Software',
1852
- 0x00000011 => 'CreateDate',
1853
- 0x00000012 => 'DateTimeOriginal',
1854
- 0x00000013 => 'FrameCount',
1855
- 0x00000016 => 'FrameRate',
1856
- 0x00000022 => 'FrameWidth',
1857
- 0x00000023 => 'FrameHeight',
1858
- 0x00000032 => 'AudioChannels',
1859
- 0x00000033 => 'AudioBitsPerSample',
1860
- 0x00000034 => 'AudioSampleRate',
1861
- 0x02000001 => 'MakerNoteVersion',
1862
- 0x02000005 => 'WhiteBalance',
1863
- 0x0200000b => 'WhiteBalanceFineTune',
1864
- 0x0200001e => 'ColorSpace',
1865
- 0x02000023 => 'PictureControlData',
1866
- 0x02000024 => 'WorldTime',
1867
- 0x02000032 => 'UnknownInfo',
1868
- 0x02000083 => 'LensType',
1869
- 0x02000084 => 'Lens',
1870
- );
1871
-
1872
- $offset = 0;
1873
- $datalength = strlen($atom_data);
1874
- $parsed = array();
1875
- while ($offset < $datalength) {
1876
- //echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
1877
- $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4;
1878
- $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
1879
- $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
1880
- switch ($data_size_type) {
1881
- case 0x0001: // 0x0001 = flag (size field *= 1-byte)
1882
- $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
1883
- $offset += ($data_size * 1);
1884
- break;
1885
- case 0x0002: // 0x0002 = char (size field *= 1-byte)
1886
- $data = substr($atom_data, $offset, $data_size * 1);
1887
- $offset += ($data_size * 1);
1888
- $data = rtrim($data, "\x00");
1889
- break;
1890
- case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
1891
- $data = '';
1892
- for ($i = $data_size - 1; $i >= 0; $i--) {
1893
- $data .= substr($atom_data, $offset + ($i * 2), 2);
1894
- }
1895
- $data = getid3_lib::BigEndian2Int($data);
1896
- $offset += ($data_size * 2);
1897
- break;
1898
- case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
1899
- $data = '';
1900
- for ($i = $data_size - 1; $i >= 0; $i--) {
1901
- $data .= substr($atom_data, $offset + ($i * 4), 4);
1902
- }
1903
- $data = getid3_lib::BigEndian2Int($data);
1904
- $offset += ($data_size * 4);
1905
- break;
1906
- case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
1907
- $data = array();
1908
- for ($i = 0; $i < $data_size; $i++) {
1909
- $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
1910
- $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
1911
- if ($denomninator == 0) {
1912
- $data[$i] = false;
1913
- } else {
1914
- $data[$i] = (double) $numerator / $denomninator;
1915
- }
1916
- }
1917
- $offset += (8 * $data_size);
1918
- if (count($data) == 1) {
1919
- $data = $data[0];
1920
- }
1921
- break;
1922
- case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
1923
- $data = substr($atom_data, $offset, $data_size * 1);
1924
- $offset += ($data_size * 1);
1925
- break;
1926
- case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
1927
- $data = substr($atom_data, $offset, $data_size * 2);
1928
- $offset += ($data_size * 2);
1929
- break;
1930
- default:
1931
- echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
1932
- break 2;
1933
- }
1934
-
1935
- switch ($record_type) {
1936
- case 0x00000011: // CreateDate
1937
- case 0x00000012: // DateTimeOriginal
1938
- $data = strtotime($data);
1939
- break;
1940
- case 0x0200001e: // ColorSpace
1941
- switch ($data) {
1942
- case 1:
1943
- $data = 'sRGB';
1944
- break;
1945
- case 2:
1946
- $data = 'Adobe RGB';
1947
- break;
1948
- }
1949
- break;
1950
- case 0x02000023: // PictureControlData
1951
- $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
1952
- $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a');
1953
- $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
1954
- $data = array(
1955
- 'PictureControlVersion' => substr($data, 0, 4),
1956
- 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"),
1957
- 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"),
1958
- //'?' => substr($data, 44, 4),
1959
- 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))],
1960
- 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)),
1961
- 'Sharpness' => ord(substr($data, 50, 1)),
1962
- 'Contrast' => ord(substr($data, 51, 1)),
1963
- 'Brightness' => ord(substr($data, 52, 1)),
1964
- 'Saturation' => ord(substr($data, 53, 1)),
1965
- 'HueAdjustment' => ord(substr($data, 54, 1)),
1966
- 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))],
1967
- 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))],
1968
- 'ToningSaturation' => ord(substr($data, 57, 1)),
1969
- );
1970
- break;
1971
- case 0x02000024: // WorldTime
1972
- // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
1973
- // timezone is stored as offset from GMT in minutes
1974
- $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
1975
- if ($timezone & 0x8000) {
1976
- $timezone = 0 - (0x10000 - $timezone);
1977
- }
1978
- $timezone /= 60;
1979
-
1980
- $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
1981
- switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
1982
- case 2:
1983
- $datedisplayformat = 'D/M/Y'; break;
1984
- case 1:
1985
- $datedisplayformat = 'M/D/Y'; break;
1986
- case 0:
1987
- default:
1988
- $datedisplayformat = 'Y/M/D'; break;
1989
- }
1990
-
1991
- $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
1992
- break;
1993
- case 0x02000083: // LensType
1994
- $data = array(
1995
- //'_' => $data,
1996
- 'mf' => (bool) ($data & 0x01),
1997
- 'd' => (bool) ($data & 0x02),
1998
- 'g' => (bool) ($data & 0x04),
1999
- 'vr' => (bool) ($data & 0x08),
2000
- );
2001
- break;
2002
- }
2003
- $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
2004
- $parsed[$tag_name] = $data;
2005
- }
2006
- return $parsed;
2007
- }
2008
-
2009
-
2010
- public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
2011
- static $handyatomtranslatorarray = array();
2012
- if (empty($handyatomtranslatorarray)) {
2013
- $handyatomtranslatorarray['�cpy'] = 'copyright';
2014
- $handyatomtranslatorarray['�day'] = 'creation_date'; // iTunes 4.0
2015
- $handyatomtranslatorarray['�dir'] = 'director';
2016
- $handyatomtranslatorarray['�ed1'] = 'edit1';
2017
- $handyatomtranslatorarray['�ed2'] = 'edit2';
2018
- $handyatomtranslatorarray['�ed3'] = 'edit3';
2019
- $handyatomtranslatorarray['�ed4'] = 'edit4';
2020
- $handyatomtranslatorarray['�ed5'] = 'edit5';
2021
- $handyatomtranslatorarray['�ed6'] = 'edit6';
2022
- $handyatomtranslatorarray['�ed7'] = 'edit7';
2023
- $handyatomtranslatorarray['�ed8'] = 'edit8';
2024
- $handyatomtranslatorarray['�ed9'] = 'edit9';
2025
- $handyatomtranslatorarray['�fmt'] = 'format';
2026
- $handyatomtranslatorarray['�inf'] = 'information';
2027
- $handyatomtranslatorarray['�prd'] = 'producer';
2028
- $handyatomtranslatorarray['�prf'] = 'performers';
2029
- $handyatomtranslatorarray['�req'] = 'system_requirements';
2030
- $handyatomtranslatorarray['�src'] = 'source_credit';
2031
- $handyatomtranslatorarray['�wrt'] = 'writer';
2032
-
2033
- // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
2034
- $handyatomtranslatorarray['�nam'] = 'title'; // iTunes 4.0
2035
- $handyatomtranslatorarray['�cmt'] = 'comment'; // iTunes 4.0
2036
- $handyatomtranslatorarray['�wrn'] = 'warning';
2037
- $handyatomtranslatorarray['�hst'] = 'host_computer';
2038
- $handyatomtranslatorarray['�mak'] = 'make';
2039
- $handyatomtranslatorarray['�mod'] = 'model';
2040
- $handyatomtranslatorarray['�PRD'] = 'product';
2041
- $handyatomtranslatorarray['�swr'] = 'software';
2042
- $handyatomtranslatorarray['�aut'] = 'author';
2043
- $handyatomtranslatorarray['�ART'] = 'artist';
2044
- $handyatomtranslatorarray['�trk'] = 'track';
2045
- $handyatomtranslatorarray['�alb'] = 'album'; // iTunes 4.0
2046
- $handyatomtranslatorarray['�com'] = 'comment';
2047
- $handyatomtranslatorarray['�gen'] = 'genre'; // iTunes 4.0
2048
- $handyatomtranslatorarray['�ope'] = 'composer';
2049
- $handyatomtranslatorarray['�url'] = 'url';
2050
- $handyatomtranslatorarray['�enc'] = 'encoder';
2051
-
2052
- // http://atomicparsley.sourceforge.net/mpeg-4files.html
2053
- $handyatomtranslatorarray['�art'] = 'artist'; // iTunes 4.0
2054
- $handyatomtranslatorarray['aART'] = 'album_artist';
2055
- $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
2056
- $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
2057
- $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
2058
- $handyatomtranslatorarray['�too'] = 'encoder'; // iTunes 4.0
2059
- $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
2060
- $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
2061
- $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
2062
- $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
2063
- $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
2064
- $handyatomtranslatorarray['�grp'] = 'grouping'; // iTunes 4.2
2065
- $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
2066
- $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
2067
- $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
2068
- $handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
2069
- $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
2070
- $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
2071
- $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
2072
- $handyatomtranslatorarray['�lyr'] = 'lyrics'; // iTunes 5.0
2073
- $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
2074
- $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
2075
- $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
2076
- $handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
2077
- $handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
2078
- $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
2079
-
2080
- // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
2081
-
2082
-
2083
-
2084
- // boxnames:
2085
- /*
2086
- $handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB';
2087
- $handyatomtranslatorarray['iTunNORM'] = 'iTunNORM';
2088
- $handyatomtranslatorarray['Encoding Params'] = 'Encoding Params';
2089
- $handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain';
2090
- $handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak';
2091
- $handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax';
2092
- $handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID';
2093
- $handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id';
2094
- $handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id';
2095
- $handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
2096
- $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id';
2097
- $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id';
2098
-
2099
- // http://age.hobba.nl/audio/tag_frame_reference.html
2100
- $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2101
- $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2102
- */
2103
- }
2104
- $info = &$this->getid3->info;
2105
- $comment_key = '';
2106
- if ($boxname && ($boxname != $keyname)) {
2107
- $comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname);
2108
- } elseif (isset($handyatomtranslatorarray[$keyname])) {
2109
- $comment_key = $handyatomtranslatorarray[$keyname];
2110
- }
2111
- if ($comment_key) {
2112
- if ($comment_key == 'picture') {
2113
- if (!is_array($data)) {
2114
- $image_mime = '';
2115
- if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
2116
- $image_mime = 'image/png';
2117
- } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
2118
- $image_mime = 'image/jpeg';
2119
- } elseif (preg_match('#^GIF#', $data)) {
2120
- $image_mime = 'image/gif';
2121
- } elseif (preg_match('#^BM#', $data)) {
2122
- $image_mime = 'image/bmp';
2123
- }
2124
- $data = array('data'=>$data, 'image_mime'=>$image_mime);
2125
- }
2126
- }
2127
- $info['quicktime']['comments'][$comment_key][] = $data;
2128
- }
2129
- return true;
2130
- }
2131
-
2132
- public function NoNullString($nullterminatedstring) {
2133
- // remove the single null terminator on null terminated strings
2134
- if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
2135
- return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
2136
- }
2137
- return $nullterminatedstring;
2138
- }
2139
-
2140
- public function Pascal2String($pascalstring) {
2141
- // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2142
- return substr($pascalstring, 1);
2143
- }
2144
-
2145
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.real.php DELETED
@@ -1,527 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio-video.real.php //
11
- // module for analyzing Real Audio/Video files //
12
- // dependencies: module.audio-video.riff.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
17
-
18
- class getid3_real extends getid3_handler
19
- {
20
-
21
- public function Analyze() {
22
- $info = &$this->getid3->info;
23
-
24
- $info['fileformat'] = 'real';
25
- $info['bitrate'] = 0;
26
- $info['playtime_seconds'] = 0;
27
-
28
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
29
- $ChunkCounter = 0;
30
- while (ftell($this->getid3->fp) < $info['avdataend']) {
31
- $ChunkData = fread($this->getid3->fp, 8);
32
- $ChunkName = substr($ChunkData, 0, 4);
33
- $ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
34
-
35
- if ($ChunkName == '.ra'."\xFD") {
36
- $ChunkData .= fread($this->getid3->fp, $ChunkSize - 8);
37
- if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $info['real']['old_ra_header'])) {
38
- $info['audio']['dataformat'] = 'real';
39
- $info['audio']['lossless'] = false;
40
- $info['audio']['sample_rate'] = $info['real']['old_ra_header']['sample_rate'];
41
- $info['audio']['bits_per_sample'] = $info['real']['old_ra_header']['bits_per_sample'];
42
- $info['audio']['channels'] = $info['real']['old_ra_header']['channels'];
43
-
44
- $info['playtime_seconds'] = 60 * ($info['real']['old_ra_header']['audio_bytes'] / $info['real']['old_ra_header']['bytes_per_minute']);
45
- $info['audio']['bitrate'] = 8 * ($info['real']['old_ra_header']['audio_bytes'] / $info['playtime_seconds']);
46
- $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($info['real']['old_ra_header']['fourcc'], $info['audio']['bitrate']);
47
-
48
- foreach ($info['real']['old_ra_header']['comments'] as $key => $valuearray) {
49
- if (strlen(trim($valuearray[0])) > 0) {
50
- $info['real']['comments'][$key][] = trim($valuearray[0]);
51
- }
52
- }
53
- return true;
54
- }
55
- $info['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to info@getid3.org';
56
- unset($info['bitrate']);
57
- unset($info['playtime_seconds']);
58
- return false;
59
- }
60
-
61
- // shortcut
62
- $info['real']['chunks'][$ChunkCounter] = array();
63
- $thisfile_real_chunks_currentchunk = &$info['real']['chunks'][$ChunkCounter];
64
-
65
- $thisfile_real_chunks_currentchunk['name'] = $ChunkName;
66
- $thisfile_real_chunks_currentchunk['offset'] = ftell($this->getid3->fp) - 8;
67
- $thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
68
- if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $info['avdataend']) {
69
- $info['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file';
70
- return false;
71
- }
72
-
73
- if ($ChunkSize > ($this->getid3->fread_buffer_size() + 8)) {
74
-
75
- $ChunkData .= fread($this->getid3->fp, $this->getid3->fread_buffer_size() - 8);
76
- fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET);
77
-
78
- } elseif(($ChunkSize - 8) > 0) {
79
-
80
- $ChunkData .= fread($this->getid3->fp, $ChunkSize - 8);
81
-
82
- }
83
- $offset = 8;
84
-
85
- switch ($ChunkName) {
86
-
87
- case '.RMF': // RealMedia File Header
88
- $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
89
- $offset += 2;
90
- switch ($thisfile_real_chunks_currentchunk['object_version']) {
91
-
92
- case 0:
93
- $thisfile_real_chunks_currentchunk['file_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
94
- $offset += 4;
95
- $thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
96
- $offset += 4;
97
- break;
98
-
99
- default:
100
- //$info['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)';
101
- break;
102
-
103
- }
104
- break;
105
-
106
-
107
- case 'PROP': // Properties Header
108
- $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
109
- $offset += 2;
110
- if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
111
- $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
112
- $offset += 4;
113
- $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
114
- $offset += 4;
115
- $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
116
- $offset += 4;
117
- $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
118
- $offset += 4;
119
- $thisfile_real_chunks_currentchunk['num_packets'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
120
- $offset += 4;
121
- $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
122
- $offset += 4;
123
- $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
124
- $offset += 4;
125
- $thisfile_real_chunks_currentchunk['index_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
126
- $offset += 4;
127
- $thisfile_real_chunks_currentchunk['data_offset'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
128
- $offset += 4;
129
- $thisfile_real_chunks_currentchunk['num_streams'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
130
- $offset += 2;
131
- $thisfile_real_chunks_currentchunk['flags_raw'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
132
- $offset += 2;
133
- $info['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
134
- if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
135
- $info['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
136
- }
137
- $thisfile_real_chunks_currentchunk['flags']['save_enabled'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
138
- $thisfile_real_chunks_currentchunk['flags']['perfect_play'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
139
- $thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
140
- }
141
- break;
142
-
143
- case 'MDPR': // Media Properties Header
144
- $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
145
- $offset += 2;
146
- if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
147
- $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
148
- $offset += 2;
149
- $thisfile_real_chunks_currentchunk['max_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
150
- $offset += 4;
151
- $thisfile_real_chunks_currentchunk['avg_bit_rate'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
152
- $offset += 4;
153
- $thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
154
- $offset += 4;
155
- $thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
156
- $offset += 4;
157
- $thisfile_real_chunks_currentchunk['start_time'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
158
- $offset += 4;
159
- $thisfile_real_chunks_currentchunk['preroll'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
160
- $offset += 4;
161
- $thisfile_real_chunks_currentchunk['duration'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
162
- $offset += 4;
163
- $thisfile_real_chunks_currentchunk['stream_name_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
164
- $offset += 1;
165
- $thisfile_real_chunks_currentchunk['stream_name'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
166
- $offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
167
- $thisfile_real_chunks_currentchunk['mime_type_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
168
- $offset += 1;
169
- $thisfile_real_chunks_currentchunk['mime_type'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
170
- $offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
171
- $thisfile_real_chunks_currentchunk['type_specific_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
172
- $offset += 4;
173
- $thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
174
- $offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
175
-
176
- // shortcut
177
- $thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
178
-
179
- switch ($thisfile_real_chunks_currentchunk['mime_type']) {
180
- case 'video/x-pn-realvideo':
181
- case 'video/x-pn-multirate-realvideo':
182
- // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
183
-
184
- // shortcut
185
- $thisfile_real_chunks_currentchunk['video_info'] = array();
186
- $thisfile_real_chunks_currentchunk_videoinfo = &$thisfile_real_chunks_currentchunk['video_info'];
187
-
188
- $thisfile_real_chunks_currentchunk_videoinfo['dwSize'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 0, 4));
189
- $thisfile_real_chunks_currentchunk_videoinfo['fourcc1'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 4, 4);
190
- $thisfile_real_chunks_currentchunk_videoinfo['fourcc2'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 8, 4);
191
- $thisfile_real_chunks_currentchunk_videoinfo['width'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
192
- $thisfile_real_chunks_currentchunk_videoinfo['height'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
193
- $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
194
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
195
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
196
- $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
197
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown3'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
198
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown4'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
199
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown5'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
200
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown6'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
201
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown7'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
202
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown8'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
203
- //$thisfile_real_chunks_currentchunk_videoinfo['unknown9'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
204
-
205
- $thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::fourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
206
-
207
- $info['video']['resolution_x'] = $thisfile_real_chunks_currentchunk_videoinfo['width'];
208
- $info['video']['resolution_y'] = $thisfile_real_chunks_currentchunk_videoinfo['height'];
209
- $info['video']['frame_rate'] = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
210
- $info['video']['codec'] = $thisfile_real_chunks_currentchunk_videoinfo['codec'];
211
- $info['video']['bits_per_sample'] = $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
212
- break;
213
-
214
- case 'audio/x-pn-realaudio':
215
- case 'audio/x-pn-multirate-realaudio':
216
- $this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']);
217
-
218
- $info['audio']['sample_rate'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
219
- $info['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
220
- $info['audio']['channels'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
221
- if (!empty($info['audio']['dataformat'])) {
222
- foreach ($info['audio'] as $key => $value) {
223
- if ($key != 'streams') {
224
- $info['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
225
- }
226
- }
227
- }
228
- break;
229
-
230
- case 'logical-fileinfo':
231
- // shortcut
232
- $thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
233
- $thisfile_real_chunks_currentchunk_logicalfileinfo = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
234
-
235
- $thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
236
- $thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
237
- $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
238
-
239
- //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
240
- $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
241
-
242
- $thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
243
- $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
244
-
245
- //$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
246
- $thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
247
-
248
- //$thisfile_real_chunks_currentchunk_logicalfileinfo['d'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
249
-
250
- //$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
251
- //$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
252
- //$thisfile_real_chunks_currentchunk_logicalfileinfo['one'] = substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
253
- //$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
254
-
255
- break;
256
-
257
- }
258
-
259
-
260
- if (empty($info['playtime_seconds'])) {
261
- $info['playtime_seconds'] = max($info['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
262
- }
263
- if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
264
- switch ($thisfile_real_chunks_currentchunk['mime_type']) {
265
- case 'audio/x-pn-realaudio':
266
- case 'audio/x-pn-multirate-realaudio':
267
- $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
268
- $info['audio']['codec'] = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $info['audio']['bitrate']);
269
- $info['audio']['dataformat'] = 'real';
270
- $info['audio']['lossless'] = false;
271
- break;
272
-
273
- case 'video/x-pn-realvideo':
274
- case 'video/x-pn-multirate-realvideo':
275
- $info['video']['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
276
- $info['video']['bitrate_mode'] = 'cbr';
277
- $info['video']['dataformat'] = 'real';
278
- $info['video']['lossless'] = false;
279
- $info['video']['pixel_aspect_ratio'] = (float) 1;
280
- break;
281
-
282
- case 'audio/x-ralf-mpeg4-generic':
283
- $info['audio']['bitrate'] = (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
284
- $info['audio']['codec'] = 'RealAudio Lossless';
285
- $info['audio']['dataformat'] = 'real';
286
- $info['audio']['lossless'] = true;
287
- break;
288
- }
289
- $info['bitrate'] = (isset($info['video']['bitrate']) ? $info['video']['bitrate'] : 0) + (isset($info['audio']['bitrate']) ? $info['audio']['bitrate'] : 0);
290
- }
291
- }
292
- break;
293
-
294
- case 'CONT': // Content Description Header (text comments)
295
- $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
296
- $offset += 2;
297
- if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
298
- $thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
299
- $offset += 2;
300
- $thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
301
- $offset += $thisfile_real_chunks_currentchunk['title_len'];
302
-
303
- $thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
304
- $offset += 2;
305
- $thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
306
- $offset += $thisfile_real_chunks_currentchunk['artist_len'];
307
-
308
- $thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
309
- $offset += 2;
310
- $thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
311
- $offset += $thisfile_real_chunks_currentchunk['copyright_len'];
312
-
313
- $thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
314
- $offset += 2;
315
- $thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
316
- $offset += $thisfile_real_chunks_currentchunk['comment_len'];
317
-
318
-
319
- $commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment');
320
- foreach ($commentkeystocopy as $key => $val) {
321
- if ($thisfile_real_chunks_currentchunk[$key]) {
322
- $info['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
323
- }
324
- }
325
-
326
- }
327
- break;
328
-
329
-
330
- case 'DATA': // Data Chunk Header
331
- // do nothing
332
- break;
333
-
334
- case 'INDX': // Index Section Header
335
- $thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
336
- $offset += 2;
337
- if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
338
- $thisfile_real_chunks_currentchunk['num_indices'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
339
- $offset += 4;
340
- $thisfile_real_chunks_currentchunk['stream_number'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
341
- $offset += 2;
342
- $thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
343
- $offset += 4;
344
-
345
- if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
346
- // last index chunk found, ignore rest of file
347
- break 2;
348
- } else {
349
- // non-last index chunk, seek to next index chunk (skipping actual index data)
350
- fseek($this->getid3->fp, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET);
351
- }
352
- }
353
- break;
354
-
355
- default:
356
- $info['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset'];
357
- break;
358
- }
359
- $ChunkCounter++;
360
- }
361
-
362
- if (!empty($info['audio']['streams'])) {
363
- $info['audio']['bitrate'] = 0;
364
- foreach ($info['audio']['streams'] as $key => $valuearray) {
365
- $info['audio']['bitrate'] += $valuearray['bitrate'];
366
- }
367
- }
368
-
369
- return true;
370
- }
371
-
372
-
373
- public function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
374
- // http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
375
-
376
- $ParsedArray = array();
377
- $ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
378
- if ($ParsedArray['magic'] != '.ra'."\xFD") {
379
- return false;
380
- }
381
- $ParsedArray['version1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 4, 2));
382
-
383
- if ($ParsedArray['version1'] < 3) {
384
-
385
- return false;
386
-
387
- } elseif ($ParsedArray['version1'] == 3) {
388
-
389
- $ParsedArray['fourcc1'] = '.ra3';
390
- $ParsedArray['bits_per_sample'] = 16; // hard-coded for old versions?
391
- $ParsedArray['sample_rate'] = 8000; // hard-coded for old versions?
392
-
393
- $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
394
- $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 8, 2)); // always 1 (?)
395
- //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
396
- //$ParsedArray['unknown2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
397
- //$ParsedArray['unknown3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
398
- $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
399
- $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
400
- $ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
401
-
402
- $commentoffset = 0;
403
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
404
- $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
405
- $commentoffset += $commentlength;
406
-
407
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
408
- $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
409
- $commentoffset += $commentlength;
410
-
411
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
412
- $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
413
- $commentoffset += $commentlength;
414
-
415
- $commentoffset++; // final null terminator (?)
416
- $commentoffset++; // fourcc length (?) should be 4
417
- $ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
418
-
419
- } elseif ($ParsedArray['version1'] <= 5) {
420
-
421
- //$ParsedArray['unknown1'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 6, 2));
422
- $ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
423
- $ParsedArray['file_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
424
- $ParsedArray['version2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
425
- $ParsedArray['header_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
426
- $ParsedArray['codec_flavor_id'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
427
- $ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
428
- $ParsedArray['audio_bytes'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
429
- $ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
430
- //$ParsedArray['unknown5'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
431
- $ParsedArray['sub_packet_h'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
432
- $ParsedArray['frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
433
- $ParsedArray['sub_packet_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
434
- //$ParsedArray['unknown6'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
435
-
436
- switch ($ParsedArray['version1']) {
437
-
438
- case 4:
439
- $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
440
- //$ParsedArray['unknown8'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
441
- $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
442
- $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
443
- $ParsedArray['length_fourcc2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
444
- $ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
445
- $ParsedArray['length_fourcc3'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
446
- $ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
447
- //$ParsedArray['unknown9'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
448
- //$ParsedArray['unknown10'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
449
- $ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
450
-
451
- $commentoffset = 0;
452
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
453
- $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
454
- $commentoffset += $commentlength;
455
-
456
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
457
- $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
458
- $commentoffset += $commentlength;
459
-
460
- $commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
461
- $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
462
- $commentoffset += $commentlength;
463
- break;
464
-
465
- case 5:
466
- $ParsedArray['sample_rate'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
467
- $ParsedArray['sample_rate2'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
468
- $ParsedArray['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
469
- $ParsedArray['channels'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
470
- $ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
471
- $ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
472
- $ParsedArray['comments'] = array();
473
- break;
474
- }
475
- $ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
476
-
477
- }
478
- foreach ($ParsedArray['comments'] as $key => $value) {
479
- if ($ParsedArray['comments'][$key][0] === false) {
480
- $ParsedArray['comments'][$key][0] = '';
481
- }
482
- }
483
-
484
- return true;
485
- }
486
-
487
- public function RealAudioCodecFourCClookup($fourcc, $bitrate) {
488
- static $RealAudioCodecFourCClookup = array();
489
- if (empty($RealAudioCodecFourCClookup)) {
490
- // http://www.its.msstate.edu/net/real/reports/config/tags.stats
491
- // http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
492
-
493
- $RealAudioCodecFourCClookup['14_4'][8000] = 'RealAudio v2 (14.4kbps)';
494
- $RealAudioCodecFourCClookup['14.4'][8000] = 'RealAudio v2 (14.4kbps)';
495
- $RealAudioCodecFourCClookup['lpcJ'][8000] = 'RealAudio v2 (14.4kbps)';
496
- $RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
497
- $RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
498
- $RealAudioCodecFourCClookup['sipr'][4933] = 'RealAudio v4 (5kbps Voice)';
499
- $RealAudioCodecFourCClookup['sipr'][6444] = 'RealAudio v4 (6.5kbps Voice)';
500
- $RealAudioCodecFourCClookup['sipr'][8444] = 'RealAudio v4 (8.5kbps Voice)';
501
- $RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
502
- $RealAudioCodecFourCClookup['dnet'][8000] = 'RealAudio v3 (8kbps Music)';
503
- $RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
504
- $RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
505
- $RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
506
- $RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
507
- $RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
508
- $RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
509
- $RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
510
- $RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
511
- $RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
512
-
513
- $RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
514
- $RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
515
- $RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
516
- $RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
517
- }
518
- $roundbitrate = intval(round($bitrate));
519
- if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
520
- return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
521
- } elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
522
- return $RealAudioCodecFourCClookup[$fourcc][0];
523
- }
524
- return $fourcc;
525
- }
526
-
527
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio-video.swf.php DELETED
@@ -1,139 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio-video.swf.php //
11
- // module for analyzing Shockwave Flash files //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- class getid3_swf extends getid3_handler
18
- {
19
- public $ReturnAllTagData = false;
20
-
21
- public function Analyze() {
22
- $info = &$this->getid3->info;
23
-
24
- $info['fileformat'] = 'swf';
25
- $info['video']['dataformat'] = 'swf';
26
-
27
- // http://www.openswf.org/spec/SWFfileformat.html
28
-
29
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
30
-
31
- $SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
32
-
33
- $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
34
- switch ($info['swf']['header']['signature']) {
35
- case 'FWS':
36
- $info['swf']['header']['compressed'] = false;
37
- break;
38
-
39
- case 'CWS':
40
- $info['swf']['header']['compressed'] = true;
41
- break;
42
-
43
- default:
44
- $info['error'][] = 'Expecting "FWS" or "CWS" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['swf']['header']['signature']).'"';
45
- unset($info['swf']);
46
- unset($info['fileformat']);
47
- return false;
48
- break;
49
- }
50
- $info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
51
- $info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
52
-
53
- if ($info['swf']['header']['compressed']) {
54
- $SWFHead = substr($SWFfileData, 0, 8);
55
- $SWFfileData = substr($SWFfileData, 8);
56
- if ($decompressed = @gzuncompress($SWFfileData)) {
57
- $SWFfileData = $SWFHead.$decompressed;
58
- } else {
59
- $info['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($info['swf']['header']['length'] - 8).' bytes uncompressed)';
60
- return false;
61
- }
62
- }
63
-
64
- $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
65
- $FrameSizeDataLength = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
66
- $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT);
67
- for ($i = 1; $i < $FrameSizeDataLength; $i++) {
68
- $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
69
- }
70
- list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
71
- $info['swf']['header']['frame_width'] = getid3_lib::Bin2Dec($X2);
72
- $info['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
73
-
74
- // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
75
- // Next in the header is the frame rate, which is kind of weird.
76
- // It is supposed to be stored as a 16bit integer, but the first byte
77
- // (or last depending on how you look at it) is completely ignored.
78
- // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps.
79
-
80
- // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
81
- $info['swf']['header']['frame_rate'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
82
- $info['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
83
-
84
- $info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
85
- $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
86
- $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
87
- $info['video']['pixel_aspect_ratio'] = (float) 1;
88
-
89
- if (($info['swf']['header']['frame_count'] > 0) && ($info['swf']['header']['frame_rate'] > 0)) {
90
- $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
91
- }
92
- //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
93
-
94
-
95
- // SWF tags
96
-
97
- $CurrentOffset = 12 + $FrameSizeDataLength;
98
- $SWFdataLength = strlen($SWFfileData);
99
-
100
- while ($CurrentOffset < $SWFdataLength) {
101
- //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
102
-
103
- $TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
104
- $TagID = ($TagIDTagLength & 0xFFFC) >> 6;
105
- $TagLength = ($TagIDTagLength & 0x003F);
106
- $CurrentOffset += 2;
107
- if ($TagLength == 0x3F) {
108
- $TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
109
- $CurrentOffset += 4;
110
- }
111
-
112
- unset($TagData);
113
- $TagData['offset'] = $CurrentOffset;
114
- $TagData['size'] = $TagLength;
115
- $TagData['id'] = $TagID;
116
- $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
117
- switch ($TagID) {
118
- case 0: // end of movie
119
- break 2;
120
-
121
- case 9: // Set background color
122
- //$info['swf']['tags'][] = $TagData;
123
- $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
124
- break;
125
-
126
- default:
127
- if ($this->ReturnAllTagData) {
128
- $info['swf']['tags'][] = $TagData;
129
- }
130
- break;
131
- }
132
-
133
- $CurrentOffset += $TagLength;
134
- }
135
-
136
- return true;
137
- }
138
-
139
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio.ac3.php DELETED
@@ -1,473 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio.ac3.php //
11
- // module for analyzing AC-3 (aka Dolby Digital) audio files //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- class getid3_ac3 extends getid3_handler
18
- {
19
- private $AC3header = array();
20
- private $BSIoffset = 0;
21
-
22
- const syncword = "\x0B\x77";
23
-
24
- public function Analyze() {
25
- $info = &$this->getid3->info;
26
-
27
- ///AH
28
- $info['ac3']['raw']['bsi'] = array();
29
- $thisfile_ac3 = &$info['ac3'];
30
- $thisfile_ac3_raw = &$thisfile_ac3['raw'];
31
- $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi'];
32
-
33
-
34
- // http://www.atsc.org/standards/a_52a.pdf
35
-
36
- $info['fileformat'] = 'ac3';
37
-
38
- // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
39
- // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
40
- // new audio samples per channel. A synchronization information (SI) header at the beginning
41
- // of each frame contains information needed to acquire and maintain synchronization. A
42
- // bit stream information (BSI) header follows SI, and contains parameters describing the coded
43
- // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
44
- // end of each frame is an error check field that includes a CRC word for error detection. An
45
- // additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
46
- //
47
- // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
48
-
49
- // syncinfo() {
50
- // syncword 16
51
- // crc1 16
52
- // fscod 2
53
- // frmsizecod 6
54
- // } /* end of syncinfo */
55
-
56
- $this->fseek($info['avdataoffset']);
57
- $this->AC3header['syncinfo'] = $this->fread(5);
58
-
59
- if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) {
60
- $thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword;
61
- $offset = 2;
62
- } else {
63
- if (!$this->isDependencyFor('matroska')) {
64
- unset($info['fileformat'], $info['ac3']);
65
- return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)).'"');
66
- }
67
- $offset = 0;
68
- $this->fseek(-2, SEEK_CUR);
69
- }
70
-
71
- $info['audio']['dataformat'] = 'ac3';
72
- $info['audio']['bitrate_mode'] = 'cbr';
73
- $info['audio']['lossless'] = false;
74
-
75
- $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2));
76
- $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], ($offset + 2), 1));
77
- $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
78
- $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
79
-
80
- $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
81
- if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
82
- $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
83
- }
84
-
85
- $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
86
- $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
87
- $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
88
-
89
- $this->AC3header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15));
90
- $ac3_bsi_offset = 0;
91
-
92
- $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
93
- if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
94
- // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
95
- // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
96
- // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
97
- $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8');
98
- unset($info['ac3']);
99
- return false;
100
- }
101
-
102
- $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
103
- $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
104
-
105
- $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
106
- $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
107
- foreach($ac3_coding_mode as $key => $value) {
108
- $thisfile_ac3[$key] = $value;
109
- }
110
- switch ($thisfile_ac3_raw_bsi['acmod']) {
111
- case 0:
112
- case 1:
113
- $info['audio']['channelmode'] = 'mono';
114
- break;
115
- case 3:
116
- case 4:
117
- $info['audio']['channelmode'] = 'stereo';
118
- break;
119
- default:
120
- $info['audio']['channelmode'] = 'surround';
121
- break;
122
- }
123
- $info['audio']['channels'] = $thisfile_ac3['num_channels'];
124
-
125
- if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
126
- // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
127
- $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
128
- $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
129
- }
130
-
131
- if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
132
- // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
133
- $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
134
- $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
135
- }
136
-
137
- if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
138
- // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
139
- $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
140
- $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
141
- }
142
-
143
- $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
144
- $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
145
- if ($thisfile_ac3_raw_bsi['lfeon']) {
146
- //$info['audio']['channels']++;
147
- $info['audio']['channels'] .= '.1';
148
- }
149
-
150
- $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
151
-
152
- // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
153
- // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
154
- $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
155
- $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
156
-
157
- $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
158
- if ($thisfile_ac3_raw_bsi['compre_flag']) {
159
- $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
160
- $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
161
- }
162
-
163
- $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
164
- if ($thisfile_ac3_raw_bsi['langcode_flag']) {
165
- $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
166
- }
167
-
168
- $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
169
- if ($thisfile_ac3_raw_bsi['audprodie']) {
170
- $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
171
- $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
172
-
173
- $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
174
- $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
175
- }
176
-
177
- if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
178
- // If acmod is 0, then two completely independent program channels (dual mono)
179
- // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
180
- // a number of additional items are present in BSI or audblk to fully describe Ch2.
181
-
182
- // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
183
- // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
184
- $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
185
- $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
186
-
187
- $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
188
- if ($thisfile_ac3_raw_bsi['compre_flag2']) {
189
- $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
190
- $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
191
- }
192
-
193
- $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
194
- if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
195
- $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
196
- }
197
-
198
- $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
199
- if ($thisfile_ac3_raw_bsi['audprodie2']) {
200
- $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
201
- $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
202
-
203
- $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
204
- $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
205
- }
206
-
207
- }
208
-
209
- $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
210
-
211
- $thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1);
212
-
213
- $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
214
- if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
215
- $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
216
- }
217
-
218
- $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
219
- if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
220
- $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
221
- }
222
-
223
- $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
224
- if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
225
- $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
226
-
227
- $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
228
-
229
- $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
230
- $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
231
- }
232
-
233
- return true;
234
- }
235
-
236
- private function readHeaderBSI($length) {
237
- $data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
238
- $this->BSIoffset += $length;
239
-
240
- return bindec($data);
241
- }
242
-
243
- public static function sampleRateCodeLookup($fscod) {
244
- static $sampleRateCodeLookup = array(
245
- 0 => 48000,
246
- 1 => 44100,
247
- 2 => 32000,
248
- 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
249
- );
250
- return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
251
- }
252
-
253
- public static function serviceTypeLookup($bsmod, $acmod) {
254
- static $serviceTypeLookup = array();
255
- if (empty($serviceTypeLookup)) {
256
- for ($i = 0; $i <= 7; $i++) {
257
- $serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
258
- $serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
259
- $serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
260
- $serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
261
- $serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
262
- $serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
263
- $serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
264
- }
265
-
266
- $serviceTypeLookup[7][1] = 'associated service: voice over (VO)';
267
- for ($i = 2; $i <= 7; $i++) {
268
- $serviceTypeLookup[7][$i] = 'main audio service: karaoke';
269
- }
270
- }
271
- return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false);
272
- }
273
-
274
- public static function audioCodingModeLookup($acmod) {
275
- // array(channel configuration, # channels (not incl LFE), channel order)
276
- static $audioCodingModeLookup = array (
277
- 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
278
- 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
279
- 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
280
- 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
281
- 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
282
- 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
283
- 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
284
- 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'),
285
- );
286
- return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
287
- }
288
-
289
- public static function centerMixLevelLookup($cmixlev) {
290
- static $centerMixLevelLookup;
291
- if (empty($centerMixLevelLookup)) {
292
- $centerMixLevelLookup = array(
293
- 0 => pow(2, -3.0 / 6), // 0.707 (-3.0 dB)
294
- 1 => pow(2, -4.5 / 6), // 0.595 (-4.5 dB)
295
- 2 => pow(2, -6.0 / 6), // 0.500 (-6.0 dB)
296
- 3 => 'reserved'
297
- );
298
- }
299
- return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false);
300
- }
301
-
302
- public static function surroundMixLevelLookup($surmixlev) {
303
- static $surroundMixLevelLookup;
304
- if (empty($surroundMixLevelLookup)) {
305
- $surroundMixLevelLookup = array(
306
- 0 => pow(2, -3.0 / 6),
307
- 1 => pow(2, -6.0 / 6),
308
- 2 => 0,
309
- 3 => 'reserved'
310
- );
311
- }
312
- return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false);
313
- }
314
-
315
- public static function dolbySurroundModeLookup($dsurmod) {
316
- static $dolbySurroundModeLookup = array(
317
- 0 => 'not indicated',
318
- 1 => 'Not Dolby Surround encoded',
319
- 2 => 'Dolby Surround encoded',
320
- 3 => 'reserved'
321
- );
322
- return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false);
323
- }
324
-
325
- public static function channelsEnabledLookup($acmod, $lfeon) {
326
- $lookup = array(
327
- 'ch1'=>(bool) ($acmod == 0),
328
- 'ch2'=>(bool) ($acmod == 0),
329
- 'left'=>(bool) ($acmod > 1),
330
- 'right'=>(bool) ($acmod > 1),
331
- 'center'=>(bool) ($acmod & 0x01),
332
- 'surround_mono'=>false,
333
- 'surround_left'=>false,
334
- 'surround_right'=>false,
335
- 'lfe'=>$lfeon);
336
- switch ($acmod) {
337
- case 4:
338
- case 5:
339
- $lookup['surround_mono'] = true;
340
- break;
341
- case 6:
342
- case 7:
343
- $lookup['surround_left'] = true;
344
- $lookup['surround_right'] = true;
345
- break;
346
- }
347
- return $lookup;
348
- }
349
-
350
- public static function heavyCompression($compre) {
351
- // The first four bits indicate gain changes in 6.02dB increments which can be
352
- // implemented with an arithmetic shift operation. The following four bits
353
- // indicate linear gain changes, and require a 5-bit multiply.
354
- // We will represent the two 4-bit fields of compr as follows:
355
- // X0 X1 X2 X3 . Y4 Y5 Y6 Y7
356
- // The meaning of the X values is most simply described by considering X to represent a 4-bit
357
- // signed integer with values from -8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
358
- // following table shows this in detail.
359
-
360
- // Meaning of 4 msb of compr
361
- // 7 +48.16 dB
362
- // 6 +42.14 dB
363
- // 5 +36.12 dB
364
- // 4 +30.10 dB
365
- // 3 +24.08 dB
366
- // 2 +18.06 dB
367
- // 1 +12.04 dB
368
- // 0 +6.02 dB
369
- // -1 0 dB
370
- // -2 -6.02 dB
371
- // -3 -12.04 dB
372
- // -4 -18.06 dB
373
- // -5 -24.08 dB
374
- // -6 -30.10 dB
375
- // -7 -36.12 dB
376
- // -8 -42.14 dB
377
-
378
- $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
379
- if ($fourbit{0} == '1') {
380
- $log_gain = -8 + bindec(substr($fourbit, 1));
381
- } else {
382
- $log_gain = bindec(substr($fourbit, 1));
383
- }
384
- $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
385
-
386
- // The value of Y is a linear representation of a gain change of up to -6 dB. Y is considered to
387
- // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
388
- // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
389
- // changes from -0.28 dB to -6.02 dB.
390
-
391
- $lin_gain = (16 + ($compre & 0x0F)) / 32;
392
-
393
- // The combination of X and Y values allows compr to indicate gain changes from
394
- // 48.16 - 0.28 = +47.89 dB, to
395
- // -42.14 - 6.02 = -48.16 dB.
396
-
397
- return $log_gain - $lin_gain;
398
- }
399
-
400
- public static function roomTypeLookup($roomtyp) {
401
- static $roomTypeLookup = array(
402
- 0 => 'not indicated',
403
- 1 => 'large room, X curve monitor',
404
- 2 => 'small room, flat monitor',
405
- 3 => 'reserved'
406
- );
407
- return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false);
408
- }
409
-
410
- public static function frameSizeLookup($frmsizecod, $fscod) {
411
- $padding = (bool) ($frmsizecod % 2);
412
- $framesizeid = floor($frmsizecod / 2);
413
-
414
- static $frameSizeLookup = array();
415
- if (empty($frameSizeLookup)) {
416
- $frameSizeLookup = array (
417
- 0 => array(128, 138, 192),
418
- 1 => array(40, 160, 174, 240),
419
- 2 => array(48, 192, 208, 288),
420
- 3 => array(56, 224, 242, 336),
421
- 4 => array(64, 256, 278, 384),
422
- 5 => array(80, 320, 348, 480),
423
- 6 => array(96, 384, 416, 576),
424
- 7 => array(112, 448, 486, 672),
425
- 8 => array(128, 512, 556, 768),
426
- 9 => array(160, 640, 696, 960),
427
- 10 => array(192, 768, 834, 1152),
428
- 11 => array(224, 896, 974, 1344),
429
- 12 => array(256, 1024, 1114, 1536),
430
- 13 => array(320, 1280, 1392, 1920),
431
- 14 => array(384, 1536, 1670, 2304),
432
- 15 => array(448, 1792, 1950, 2688),
433
- 16 => array(512, 2048, 2228, 3072),
434
- 17 => array(576, 2304, 2506, 3456),
435
- 18 => array(640, 2560, 2786, 3840)
436
- );
437
- }
438
- if (($fscod == 1) && $padding) {
439
- // frame lengths are padded by 1 word (16 bits) at 44100
440
- $frameSizeLookup[$frmsizecod] += 2;
441
- }
442
- return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
443
- }
444
-
445
- public static function bitrateLookup($frmsizecod) {
446
- $framesizeid = floor($frmsizecod / 2);
447
-
448
- static $bitrateLookup = array(
449
- 0 => 32000,
450
- 1 => 40000,
451
- 2 => 48000,
452
- 3 => 56000,
453
- 4 => 64000,
454
- 5 => 80000,
455
- 6 => 96000,
456
- 7 => 112000,
457
- 8 => 128000,
458
- 9 => 160000,
459
- 10 => 192000,
460
- 11 => 224000,
461
- 12 => 256000,
462
- 13 => 320000,
463
- 14 => 384000,
464
- 15 => 448000,
465
- 16 => 512000,
466
- 17 => 576000,
467
- 18 => 640000
468
- );
469
- return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
470
- }
471
-
472
-
473
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio.flac.php DELETED
@@ -1,442 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio.flac.php //
11
- // module for analyzing FLAC and OggFLAC audio files //
12
- // dependencies: module.audio.ogg.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
18
-
19
- /**
20
- * @tutorial http://flac.sourceforge.net/format.html
21
- */
22
- class getid3_flac extends getid3_handler
23
- {
24
- const syncword = 'fLaC';
25
-
26
- public function Analyze() {
27
- $info = &$this->getid3->info;
28
-
29
- $this->fseek($info['avdataoffset']);
30
- $StreamMarker = $this->fread(4);
31
- if ($StreamMarker != self::syncword) {
32
- return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"');
33
- }
34
- $info['fileformat'] = 'flac';
35
- $info['audio']['dataformat'] = 'flac';
36
- $info['audio']['bitrate_mode'] = 'vbr';
37
- $info['audio']['lossless'] = true;
38
-
39
- // parse flac container
40
- return $this->parseMETAdata();
41
- }
42
-
43
- public function parseMETAdata() {
44
- $info = &$this->getid3->info;
45
- do {
46
- $BlockOffset = $this->ftell();
47
- $BlockHeader = $this->fread(4);
48
- $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1));
49
- $LastBlockFlag = (bool) ($LBFBT & 0x80);
50
- $BlockType = ($LBFBT & 0x7F);
51
- $BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3));
52
- $BlockTypeText = self::metaBlockTypeLookup($BlockType);
53
-
54
- if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) {
55
- $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file');
56
- break;
57
- }
58
- if ($BlockLength < 1) {
59
- $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid');
60
- break;
61
- }
62
-
63
- $info['flac'][$BlockTypeText]['raw'] = array();
64
- $BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw'];
65
-
66
- $BlockTypeText_raw['offset'] = $BlockOffset;
67
- $BlockTypeText_raw['last_meta_block'] = $LastBlockFlag;
68
- $BlockTypeText_raw['block_type'] = $BlockType;
69
- $BlockTypeText_raw['block_type_text'] = $BlockTypeText;
70
- $BlockTypeText_raw['block_length'] = $BlockLength;
71
- if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically
72
- $BlockTypeText_raw['block_data'] = $this->fread($BlockLength);
73
- }
74
-
75
- switch ($BlockTypeText) {
76
- case 'STREAMINFO': // 0x00
77
- if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) {
78
- return false;
79
- }
80
- break;
81
-
82
- case 'PADDING': // 0x01
83
- unset($info['flac']['PADDING']); // ignore
84
- break;
85
-
86
- case 'APPLICATION': // 0x02
87
- if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) {
88
- return false;
89
- }
90
- break;
91
-
92
- case 'SEEKTABLE': // 0x03
93
- if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) {
94
- return false;
95
- }
96
- break;
97
-
98
- case 'VORBIS_COMMENT': // 0x04
99
- if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) {
100
- return false;
101
- }
102
- break;
103
-
104
- case 'CUESHEET': // 0x05
105
- if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) {
106
- return false;
107
- }
108
- break;
109
-
110
- case 'PICTURE': // 0x06
111
- if (!$this->parsePICTURE()) {
112
- return false;
113
- }
114
- break;
115
-
116
- default:
117
- $this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset);
118
- }
119
-
120
- unset($info['flac'][$BlockTypeText]['raw']);
121
- $info['avdataoffset'] = $this->ftell();
122
- }
123
- while ($LastBlockFlag === false);
124
-
125
- // handle tags
126
- if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) {
127
- $info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments'];
128
- }
129
- if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) {
130
- $info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']);
131
- }
132
-
133
- // copy attachments to 'comments' array if nesesary
134
- if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
135
- foreach ($info['flac']['PICTURE'] as $entry) {
136
- if (!empty($entry['data'])) {
137
- $info['flac']['comments']['picture'][] = array('image_mime'=>$entry['image_mime'], 'data'=>$entry['data']);
138
- }
139
- }
140
- }
141
-
142
- if (isset($info['flac']['STREAMINFO'])) {
143
- if (!$this->isDependencyFor('matroska')) {
144
- $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset'];
145
- }
146
- $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8);
147
- if ($info['flac']['uncompressed_audio_bytes'] == 0) {
148
- return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero');
149
- }
150
- if (!empty($info['flac']['compressed_audio_bytes'])) {
151
- $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes'];
152
- }
153
- }
154
-
155
- // set md5_data_source - built into flac 0.5+
156
- if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
157
-
158
- if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
159
- $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
160
- }
161
- else {
162
- $info['md5_data_source'] = '';
163
- $md5 = $info['flac']['STREAMINFO']['audio_signature'];
164
- for ($i = 0; $i < strlen($md5); $i++) {
165
- $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
166
- }
167
- if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
168
- unset($info['md5_data_source']);
169
- }
170
- }
171
- }
172
-
173
- if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) {
174
- $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample'];
175
- if ($info['audio']['bits_per_sample'] == 8) {
176
- // special case
177
- // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
178
- // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
179
- $this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
180
- }
181
- }
182
-
183
- return true;
184
- }
185
-
186
- private function parseSTREAMINFO($BlockData) {
187
- $info = &$this->getid3->info;
188
-
189
- $info['flac']['STREAMINFO'] = array();
190
- $streaminfo = &$info['flac']['STREAMINFO'];
191
-
192
- $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2));
193
- $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2));
194
- $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3));
195
- $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3));
196
-
197
- $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8));
198
- $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20));
199
- $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1;
200
- $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1;
201
- $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36));
202
-
203
- $streaminfo['audio_signature'] = substr($BlockData, 18, 16);
204
-
205
- if (!empty($streaminfo['sample_rate'])) {
206
-
207
- $info['audio']['bitrate_mode'] = 'vbr';
208
- $info['audio']['sample_rate'] = $streaminfo['sample_rate'];
209
- $info['audio']['channels'] = $streaminfo['channels'];
210
- $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample'];
211
- $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate'];
212
- if ($info['playtime_seconds'] > 0) {
213
- if (!$this->isDependencyFor('matroska')) {
214
- $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
215
- }
216
- else {
217
- $this->warning('Cannot determine audio bitrate because total stream size is unknown');
218
- }
219
- }
220
-
221
- } else {
222
- return $this->error('Corrupt METAdata block: STREAMINFO');
223
- }
224
-
225
- return true;
226
- }
227
-
228
- private function parseAPPLICATION($BlockData) {
229
- $info = &$this->getid3->info;
230
-
231
- $ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4));
232
- $info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID);
233
- $info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4);
234
-
235
- return true;
236
- }
237
-
238
- private function parseSEEKTABLE($BlockData) {
239
- $info = &$this->getid3->info;
240
-
241
- $offset = 0;
242
- $BlockLength = strlen($BlockData);
243
- $placeholderpattern = str_repeat("\xFF", 8);
244
- while ($offset < $BlockLength) {
245
- $SampleNumberString = substr($BlockData, $offset, 8);
246
- $offset += 8;
247
- if ($SampleNumberString == $placeholderpattern) {
248
-
249
- // placeholder point
250
- getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
251
- $offset += 10;
252
-
253
- } else {
254
-
255
- $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString);
256
- $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
257
- $offset += 8;
258
- $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2));
259
- $offset += 2;
260
-
261
- }
262
- }
263
-
264
- return true;
265
- }
266
-
267
- private function parseVORBIS_COMMENT($BlockData) {
268
- $info = &$this->getid3->info;
269
-
270
- $getid3_ogg = new getid3_ogg($this->getid3);
271
- if ($this->isDependencyFor('matroska')) {
272
- $getid3_ogg->setStringMode($this->data_string);
273
- }
274
- $getid3_ogg->ParseVorbisComments();
275
- if (isset($info['ogg'])) {
276
- unset($info['ogg']['comments_raw']);
277
- $info['flac']['VORBIS_COMMENT'] = $info['ogg'];
278
- unset($info['ogg']);
279
- }
280
-
281
- unset($getid3_ogg);
282
-
283
- return true;
284
- }
285
-
286
- private function parseCUESHEET($BlockData) {
287
- $info = &$this->getid3->info;
288
- $offset = 0;
289
- $info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0");
290
- $offset += 128;
291
- $info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
292
- $offset += 8;
293
- $info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80);
294
- $offset += 1;
295
-
296
- $offset += 258; // reserved
297
-
298
- $info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
299
- $offset += 1;
300
-
301
- for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) {
302
- $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
303
- $offset += 8;
304
- $TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
305
- $offset += 1;
306
-
307
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset;
308
-
309
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12);
310
- $offset += 12;
311
-
312
- $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
313
- $offset += 1;
314
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80);
315
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
316
-
317
- $offset += 13; // reserved
318
-
319
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
320
- $offset += 1;
321
-
322
- for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
323
- $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8));
324
- $offset += 8;
325
- $IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1));
326
- $offset += 1;
327
-
328
- $offset += 3; // reserved
329
-
330
- $info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
331
- }
332
- }
333
-
334
- return true;
335
- }
336
-
337
- /**
338
- * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment
339
- * External usage: audio.ogg
340
- */
341
- public function parsePICTURE() {
342
- $info = &$this->getid3->info;
343
-
344
- $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
345
- $picture['type'] = self::pictureTypeLookup($picture['typeid']);
346
- $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
347
- $descr_length = getid3_lib::BigEndian2Int($this->fread(4));
348
- if ($descr_length) {
349
- $picture['description'] = $this->fread($descr_length);
350
- }
351
- $picture['width'] = getid3_lib::BigEndian2Int($this->fread(4));
352
- $picture['height'] = getid3_lib::BigEndian2Int($this->fread(4));
353
- $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
354
- $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
355
- $data_length = getid3_lib::BigEndian2Int($this->fread(4));
356
-
357
- if ($picture['image_mime'] == '-->') {
358
- $picture['data'] = $this->fread($data_length);
359
- } else {
360
- $picture['data'] = $this->saveAttachment(
361
- str_replace('/', '_', $picture['type']).'_'.$this->ftell(),
362
- $this->ftell(),
363
- $data_length,
364
- $picture['image_mime']);
365
- }
366
-
367
- $info['flac']['PICTURE'][] = $picture;
368
-
369
- return true;
370
- }
371
-
372
- public static function metaBlockTypeLookup($blocktype) {
373
- static $lookup = array(
374
- 0 => 'STREAMINFO',
375
- 1 => 'PADDING',
376
- 2 => 'APPLICATION',
377
- 3 => 'SEEKTABLE',
378
- 4 => 'VORBIS_COMMENT',
379
- 5 => 'CUESHEET',
380
- 6 => 'PICTURE',
381
- );
382
- return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved');
383
- }
384
-
385
- public static function applicationIDLookup($applicationid) {
386
- // http://flac.sourceforge.net/id.html
387
- static $lookup = array(
388
- 0x41544348 => 'FlacFile', // "ATCH"
389
- 0x42534F4C => 'beSolo', // "BSOL"
390
- 0x42554753 => 'Bugs Player', // "BUGS"
391
- 0x43756573 => 'GoldWave cue points (specification)', // "Cues"
392
- 0x46696361 => 'CUE Splitter', // "Fica"
393
- 0x46746F6C => 'flac-tools', // "Ftol"
394
- 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB"
395
- 0x4D505345 => 'MP3 Stream Editor', // "MPSE"
396
- 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML"
397
- 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF"
398
- 0x5346464C => 'Sound Font FLAC', // "SFFL"
399
- 0x534F4E59 => 'Sony Creative Software', // "SONY"
400
- 0x5351455A => 'flacsqueeze', // "SQEZ"
401
- 0x54745776 => 'TwistedWave', // "TtWv"
402
- 0x55495453 => 'UITS Embedding tools', // "UITS"
403
- 0x61696666 => 'FLAC AIFF chunk storage', // "aiff"
404
- 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag"
405
- 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem"
406
- 0x71667374 => 'QFLAC Studio', // "qfst"
407
- 0x72696666 => 'FLAC RIFF chunk storage', // "riff"
408
- 0x74756E65 => 'TagTuner', // "tune"
409
- 0x78626174 => 'XBAT', // "xbat"
410
- 0x786D6364 => 'xmcd', // "xmcd"
411
- );
412
- return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved');
413
- }
414
-
415
- public static function pictureTypeLookup($type_id) {
416
- static $lookup = array (
417
- 0 => 'Other',
418
- 1 => '32x32 pixels \'file icon\' (PNG only)',
419
- 2 => 'Other file icon',
420
- 3 => 'Cover (front)',
421
- 4 => 'Cover (back)',
422
- 5 => 'Leaflet page',
423
- 6 => 'Media (e.g. label side of CD)',
424
- 7 => 'Lead artist/lead performer/soloist',
425
- 8 => 'Artist/performer',
426
- 9 => 'Conductor',
427
- 10 => 'Band/Orchestra',
428
- 11 => 'Composer',
429
- 12 => 'Lyricist/text writer',
430
- 13 => 'Recording Location',
431
- 14 => 'During recording',
432
- 15 => 'During performance',
433
- 16 => 'Movie/video screen capture',
434
- 17 => 'A bright coloured fish',
435
- 18 => 'Illustration',
436
- 19 => 'Band/artist logotype',
437
- 20 => 'Publisher/Studio logotype',
438
- );
439
- return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
440
- }
441
-
442
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio.mp3.php DELETED
@@ -1,2009 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio.mp3.php //
11
- // module for analyzing MP3 files //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- // number of frames to scan to determine if MPEG-audio sequence is valid
18
- // Lower this number to 5-20 for faster scanning
19
- // Increase this number to 50+ for most accurate detection of valid VBR/CBR
20
- // mpeg-audio streams
21
- define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
22
-
23
-
24
- class getid3_mp3 extends getid3_handler
25
- {
26
-
27
- public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
28
-
29
- public function Analyze() {
30
- $info = &$this->getid3->info;
31
-
32
- $initialOffset = $info['avdataoffset'];
33
-
34
- if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
35
- if ($this->allow_bruteforce) {
36
- $info['error'][] = 'Rescanning file in BruteForce mode';
37
- $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
38
- }
39
- }
40
-
41
-
42
- if (isset($info['mpeg']['audio']['bitrate_mode'])) {
43
- $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
44
- }
45
-
46
- if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
47
-
48
- $synchoffsetwarning = 'Unknown data before synch ';
49
- if (isset($info['id3v2']['headerlength'])) {
50
- $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
51
- } elseif ($initialOffset > 0) {
52
- $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
53
- } else {
54
- $synchoffsetwarning .= '(should be at beginning of file, ';
55
- }
56
- $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
57
- if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
58
-
59
- if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
60
-
61
- $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
62
- $info['audio']['codec'] = 'LAME';
63
- $CurrentDataLAMEversionString = 'LAME3.';
64
-
65
- } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
66
-
67
- $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
68
- $info['audio']['codec'] = 'LAME';
69
- $CurrentDataLAMEversionString = 'LAME3.';
70
-
71
- }
72
-
73
- }
74
- $info['warning'][] = $synchoffsetwarning;
75
-
76
- }
77
-
78
- if (isset($info['mpeg']['audio']['LAME'])) {
79
- $info['audio']['codec'] = 'LAME';
80
- if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
81
- $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
82
- } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
83
- $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
84
- }
85
- }
86
-
87
- $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
88
- if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
89
- // a version number of LAME that does not end with a number like "LAME3.92"
90
- // or with a closing parenthesis like "LAME3.88 (alpha)"
91
- // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
92
-
93
- // not sure what the actual last frame length will be, but will be less than or equal to 1441
94
- $PossiblyLongerLAMEversion_FrameLength = 1441;
95
-
96
- // Not sure what version of LAME this is - look in padding of last frame for longer version string
97
- $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
98
- fseek($this->getid3->fp, $PossibleLAMEversionStringOffset);
99
- $PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength);
100
- switch (substr($CurrentDataLAMEversionString, -1)) {
101
- case 'a':
102
- case 'b':
103
- // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
104
- // need to trim off "a" to match longer string
105
- $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
106
- break;
107
- }
108
- if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
109
- if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
110
- $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
111
- if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
112
- $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
113
- }
114
- }
115
- }
116
- }
117
- if (!empty($info['audio']['encoder'])) {
118
- $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
119
- }
120
-
121
- switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
122
- case 1:
123
- case 2:
124
- $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
125
- break;
126
- }
127
- if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
128
- switch ($info['audio']['dataformat']) {
129
- case 'mp1':
130
- case 'mp2':
131
- case 'mp3':
132
- $info['fileformat'] = $info['audio']['dataformat'];
133
- break;
134
-
135
- default:
136
- $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
137
- break;
138
- }
139
- }
140
-
141
- if (empty($info['fileformat'])) {
142
- unset($info['fileformat']);
143
- unset($info['audio']['bitrate_mode']);
144
- unset($info['avdataoffset']);
145
- unset($info['avdataend']);
146
- return false;
147
- }
148
-
149
- $info['mime_type'] = 'audio/mpeg';
150
- $info['audio']['lossless'] = false;
151
-
152
- // Calculate playtime
153
- if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
154
- $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
155
- }
156
-
157
- $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
158
-
159
- return true;
160
- }
161
-
162
-
163
- public function GuessEncoderOptions() {
164
- // shortcuts
165
- $info = &$this->getid3->info;
166
- if (!empty($info['mpeg']['audio'])) {
167
- $thisfile_mpeg_audio = &$info['mpeg']['audio'];
168
- if (!empty($thisfile_mpeg_audio['LAME'])) {
169
- $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
170
- }
171
- }
172
-
173
- $encoder_options = '';
174
- static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
175
-
176
- if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
177
-
178
- $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
179
-
180
- } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
181
-
182
- $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
183
-
184
- } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
185
-
186
- static $KnownEncoderValues = array();
187
- if (empty($KnownEncoderValues)) {
188
-
189
- //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
190
- $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
191
- $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
192
- $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
193
- $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
194
- $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
195
- $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
196
- $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
197
- $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
198
- $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
199
- $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
200
- $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
201
- $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
202
- $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
203
- $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
204
- $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
205
- $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
206
- $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
207
-
208
- $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
209
- $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
210
- $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
211
- $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
212
- $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
213
- $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
214
- $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
215
- $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
216
- $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
217
- $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
218
- $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
219
- $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
220
- $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
221
- $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
222
- $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
223
- $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
224
- $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
225
- $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
226
- $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
227
- $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
228
- $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
229
- $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
230
- $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
231
- $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
232
- $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
233
- $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
234
- $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
235
- $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
236
- $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
237
- $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
238
- $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
239
- $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
240
- $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
241
- }
242
-
243
- if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
244
-
245
- $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
246
-
247
- } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
248
-
249
- $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
250
-
251
- } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
252
-
253
- // http://gabriel.mp3-tech.org/mp3infotag.html
254
- // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
255
-
256
-
257
- $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
258
- $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
259
- $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
260
-
261
- } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
262
-
263
- $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
264
-
265
- } else {
266
-
267
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
268
-
269
- }
270
-
271
- } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
272
-
273
- $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
274
-
275
- } elseif (!empty($info['audio']['bitrate'])) {
276
-
277
- if ($info['audio']['bitrate_mode'] == 'cbr') {
278
- $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
279
- } else {
280
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
281
- }
282
-
283
- }
284
- if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
285
- $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
286
- }
287
-
288
- if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
289
- $encoder_options .= ' --nogap';
290
- }
291
-
292
- if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
293
- $ExplodedOptions = explode(' ', $encoder_options, 4);
294
- if ($ExplodedOptions[0] == '--r3mix') {
295
- $ExplodedOptions[1] = 'r3mix';
296
- }
297
- switch ($ExplodedOptions[0]) {
298
- case '--preset':
299
- case '--alt-preset':
300
- case '--r3mix':
301
- if ($ExplodedOptions[1] == 'fast') {
302
- $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
303
- }
304
- switch ($ExplodedOptions[1]) {
305
- case 'portable':
306
- case 'medium':
307
- case 'standard':
308
- case 'extreme':
309
- case 'insane':
310
- case 'fast portable':
311
- case 'fast medium':
312
- case 'fast standard':
313
- case 'fast extreme':
314
- case 'fast insane':
315
- case 'r3mix':
316
- static $ExpectedLowpass = array(
317
- 'insane|20500' => 20500,
318
- 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
319
- 'medium|18000' => 18000,
320
- 'fast medium|18000' => 18000,
321
- 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
322
- 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
323
- 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
324
- 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
325
- 'standard|19000' => 19000,
326
- 'fast standard|19000' => 19000,
327
- 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
328
- 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
329
- 'r3mix|18000' => 18000, // 3.94, 3.95
330
- );
331
- if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
332
- $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
333
- }
334
- break;
335
-
336
- default:
337
- break;
338
- }
339
- break;
340
- }
341
- }
342
-
343
- if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
344
- if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
345
- $encoder_options .= ' --resample 44100';
346
- } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
347
- $encoder_options .= ' --resample 48000';
348
- } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
349
- switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
350
- case 0: // <= 32000
351
- // may or may not be same as source frequency - ignore
352
- break;
353
- case 1: // 44100
354
- case 2: // 48000
355
- case 3: // 48000+
356
- $ExplodedOptions = explode(' ', $encoder_options, 4);
357
- switch ($ExplodedOptions[0]) {
358
- case '--preset':
359
- case '--alt-preset':
360
- switch ($ExplodedOptions[1]) {
361
- case 'fast':
362
- case 'portable':
363
- case 'medium':
364
- case 'standard':
365
- case 'extreme':
366
- case 'insane':
367
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
368
- break;
369
-
370
- default:
371
- static $ExpectedResampledRate = array(
372
- 'phon+/lw/mw-eu/sw|16000' => 16000,
373
- 'mw-us|24000' => 24000, // 3.95
374
- 'mw-us|32000' => 32000, // 3.93
375
- 'mw-us|16000' => 16000, // 3.92
376
- 'phone|16000' => 16000,
377
- 'phone|11025' => 11025, // 3.94a15
378
- 'radio|32000' => 32000, // 3.94a15
379
- 'fm/radio|32000' => 32000, // 3.92
380
- 'fm|32000' => 32000, // 3.90
381
- 'voice|32000' => 32000);
382
- if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
383
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
384
- }
385
- break;
386
- }
387
- break;
388
-
389
- case '--r3mix':
390
- default:
391
- $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
392
- break;
393
- }
394
- break;
395
- }
396
- }
397
- }
398
- if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
399
- //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
400
- $encoder_options = strtoupper($info['audio']['bitrate_mode']);
401
- }
402
-
403
- return $encoder_options;
404
- }
405
-
406
-
407
- public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
408
- static $MPEGaudioVersionLookup;
409
- static $MPEGaudioLayerLookup;
410
- static $MPEGaudioBitrateLookup;
411
- static $MPEGaudioFrequencyLookup;
412
- static $MPEGaudioChannelModeLookup;
413
- static $MPEGaudioModeExtensionLookup;
414
- static $MPEGaudioEmphasisLookup;
415
- if (empty($MPEGaudioVersionLookup)) {
416
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
417
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
418
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
419
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
420
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
421
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
422
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
423
- }
424
-
425
- if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) {
426
- $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
427
- return false;
428
- }
429
- //$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
430
- $headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
431
-
432
- // MP3 audio frame structure:
433
- // $aa $aa $aa $aa [$bb $bb] $cc...
434
- // where $aa..$aa is the four-byte mpeg-audio header (below)
435
- // $bb $bb is the optional 2-byte CRC
436
- // and $cc... is the audio data
437
-
438
- $head4 = substr($headerstring, 0, 4);
439
-
440
- static $MPEGaudioHeaderDecodeCache = array();
441
- if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
442
- $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
443
- } else {
444
- $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
445
- $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
446
- }
447
-
448
- static $MPEGaudioHeaderValidCache = array();
449
- if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
450
- //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
451
- $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
452
- }
453
-
454
- // shortcut
455
- if (!isset($info['mpeg']['audio'])) {
456
- $info['mpeg']['audio'] = array();
457
- }
458
- $thisfile_mpeg_audio = &$info['mpeg']['audio'];
459
-
460
-
461
- if ($MPEGaudioHeaderValidCache[$head4]) {
462
- $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
463
- } else {
464
- $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
465
- return false;
466
- }
467
-
468
- if (!$FastMPEGheaderScan) {
469
- $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
470
- $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
471
-
472
- $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
473
- $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
474
- $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
475
- $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection'];
476
- $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
477
- $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
478
- $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
479
- $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
480
- $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
481
-
482
- $info['audio']['channels'] = $thisfile_mpeg_audio['channels'];
483
- $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
484
-
485
- if ($thisfile_mpeg_audio['protection']) {
486
- $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
487
- }
488
- }
489
-
490
- if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
491
- // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
492
- $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
493
- $thisfile_mpeg_audio['raw']['bitrate'] = 0;
494
- }
495
- $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
496
- $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
497
-
498
- if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
499
- // only skip multiple frame check if free-format bitstream found at beginning of file
500
- // otherwise is quite possibly simply corrupted data
501
- $recursivesearch = false;
502
- }
503
-
504
- // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
505
- if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
506
-
507
- $info['audio']['dataformat'] = 'mp2';
508
- switch ($thisfile_mpeg_audio['channelmode']) {
509
-
510
- case 'mono':
511
- if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
512
- // these are ok
513
- } else {
514
- $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
515
- return false;
516
- }
517
- break;
518
-
519
- case 'stereo':
520
- case 'joint stereo':
521
- case 'dual channel':
522
- if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
523
- // these are ok
524
- } else {
525
- $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
526
- return false;
527
- }
528
- break;
529
-
530
- }
531
-
532
- }
533
-
534
-
535
- if ($info['audio']['sample_rate'] > 0) {
536
- $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
537
- }
538
-
539
- $nextframetestoffset = $offset + 1;
540
- if ($thisfile_mpeg_audio['bitrate'] != 'free') {
541
-
542
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
543
-
544
- if (isset($thisfile_mpeg_audio['framelength'])) {
545
- $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
546
- } else {
547
- $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
548
- return false;
549
- }
550
-
551
- }
552
-
553
- $ExpectedNumberOfAudioBytes = 0;
554
-
555
- ////////////////////////////////////////////////////////////////////////////////////
556
- // Variable-bitrate headers
557
-
558
- if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
559
- // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
560
- // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
561
-
562
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
563
- $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
564
- $info['audio']['codec'] = 'Fraunhofer';
565
-
566
- $SideInfoData = substr($headerstring, 4 + 2, 32);
567
-
568
- $FraunhoferVBROffset = 36;
569
-
570
- $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion
571
- $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay
572
- $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality
573
- $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
574
- $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
575
- $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
576
- $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
577
- $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
578
- $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
579
-
580
- $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
581
-
582
- $previousbyteoffset = $offset;
583
- for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
584
- $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
585
- $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
586
- $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
587
- $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
588
- $previousbyteoffset += $Fraunhofer_OffsetN;
589
- }
590
-
591
-
592
- } else {
593
-
594
- // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
595
- // depending on MPEG layer and number of channels
596
-
597
- $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
598
- $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
599
-
600
- if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
601
- // 'Xing' is traditional Xing VBR frame
602
- // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
603
- // 'Info' *can* legally be used to specify a VBR file as well, however.
604
-
605
- // http://www.multiweb.cz/twoinches/MP3inside.htm
606
- //00..03 = "Xing" or "Info"
607
- //04..07 = Flags:
608
- // 0x01 Frames Flag set if value for number of frames in file is stored
609
- // 0x02 Bytes Flag set if value for filesize in bytes is stored
610
- // 0x04 TOC Flag set if values for TOC are stored
611
- // 0x08 VBR Scale Flag set if values for VBR scale is stored
612
- //08..11 Frames: Number of frames in file (including the first Xing/Info one)
613
- //12..15 Bytes: File length in Bytes
614
- //16..115 TOC (Table of Contents):
615
- // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
616
- // Each Byte has a value according this formula:
617
- // (TOC[i] / 256) * fileLenInBytes
618
- // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
619
- // TOC[(60/240)*100] = TOC[25]
620
- // and corresponding Byte in file is then approximately at:
621
- // (TOC[25]/256) * 5000000
622
- //116..119 VBR Scale
623
-
624
-
625
- // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
626
- // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
627
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
628
- $thisfile_mpeg_audio['VBR_method'] = 'Xing';
629
- // } else {
630
- // $ScanAsCBR = true;
631
- // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
632
- // }
633
-
634
- $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
635
-
636
- $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
637
- $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
638
- $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
639
- $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
640
-
641
- if ($thisfile_mpeg_audio['xing_flags']['frames']) {
642
- $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4));
643
- //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
644
- }
645
- if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
646
- $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
647
- }
648
-
649
- //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
650
- if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
651
-
652
- $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
653
-
654
- if ($thisfile_mpeg_audio['layer'] == '1') {
655
- // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
656
- //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
657
- $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
658
- } else {
659
- // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
660
- //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
661
- $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
662
- }
663
- $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
664
- }
665
-
666
- if ($thisfile_mpeg_audio['xing_flags']['toc']) {
667
- $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
668
- for ($i = 0; $i < 100; $i++) {
669
- $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
670
- }
671
- }
672
- if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
673
- $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
674
- }
675
-
676
-
677
- // http://gabriel.mp3-tech.org/mp3infotag.html
678
- if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
679
-
680
- // shortcut
681
- $thisfile_mpeg_audio['LAME'] = array();
682
- $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
683
-
684
-
685
- $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
686
- $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
687
-
688
- if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
689
-
690
- // extra 11 chars are not part of version string when LAMEtag present
691
- unset($thisfile_mpeg_audio_lame['long_version']);
692
-
693
- // It the LAME tag was only introduced in LAME v3.90
694
- // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
695
-
696
- // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
697
- // are assuming a 'Xing' identifier offset of 0x24, which is the case for
698
- // MPEG-1 non-mono, but not for other combinations
699
- $LAMEtagOffsetContant = $VBRidOffset - 0x24;
700
-
701
- // shortcuts
702
- $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array());
703
- $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
704
- $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
705
- $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
706
- $thisfile_mpeg_audio_lame['raw'] = array();
707
- $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
708
-
709
- // byte $9B VBR Quality
710
- // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
711
- // Actually overwrites original Xing bytes
712
- unset($thisfile_mpeg_audio['VBR_scale']);
713
- $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
714
-
715
- // bytes $9C-$A4 Encoder short VersionString
716
- $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
717
-
718
- // byte $A5 Info Tag revision + VBR method
719
- $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
720
-
721
- $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
722
- $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
723
- $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
724
- $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
725
-
726
- // byte $A6 Lowpass filter value
727
- $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
728
-
729
- // bytes $A7-$AE Replay Gain
730
- // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
731
- // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
732
- if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
733
- // LAME 3.94a16 and later - 9.23 fixed point
734
- // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
735
- $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
736
- } else {
737
- // LAME 3.94a15 and earlier - 32-bit floating point
738
- // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
739
- $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
740
- }
741
- if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
742
- unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
743
- } else {
744
- $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
745
- }
746
-
747
- $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
748
- $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
749
-
750
-
751
- if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
752
-
753
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
754
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
755
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
756
- $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
757
- $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
758
- $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
759
- $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
760
-
761
- if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
762
- $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
763
- }
764
- $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
765
- $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
766
- } else {
767
- unset($thisfile_mpeg_audio_lame_RGAD['track']);
768
- }
769
- if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
770
-
771
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
772
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
773
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
774
- $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
775
- $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
776
- $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
777
- $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
778
-
779
- if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
780
- $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
781
- }
782
- $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
783
- $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
784
- } else {
785
- unset($thisfile_mpeg_audio_lame_RGAD['album']);
786
- }
787
- if (empty($thisfile_mpeg_audio_lame_RGAD)) {
788
- unset($thisfile_mpeg_audio_lame['RGAD']);
789
- }
790
-
791
-
792
- // byte $AF Encoding flags + ATH Type
793
- $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
794
- $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
795
- $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
796
- $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
797
- $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
798
- $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
799
-
800
- // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
801
- $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
802
- if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
803
- $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
804
- } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
805
- // ignore
806
- } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
807
- $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
808
- }
809
-
810
- // bytes $B1-$B3 Encoder delays
811
- $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
812
- $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
813
- $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
814
-
815
- // byte $B4 Misc
816
- $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
817
- $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
818
- $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
819
- $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
820
- $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
821
- $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
822
- $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
823
- $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
824
- $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
825
-
826
- // byte $B5 MP3 Gain
827
- $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
828
- $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
829
- $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
830
-
831
- // bytes $B6-$B7 Preset and surround info
832
- $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
833
- // Reserved = ($PresetSurroundBytes & 0xC000);
834
- $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
835
- $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
836
- $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
837
- $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
838
- if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
839
- $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
840
- }
841
- if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
842
- // this may change if 3.90.4 ever comes out
843
- $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
844
- }
845
-
846
- // bytes $B8-$BB MusicLength
847
- $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
848
- $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
849
-
850
- // bytes $BC-$BD MusicCRC
851
- $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
852
-
853
- // bytes $BE-$BF CRC-16 of Info Tag
854
- $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
855
-
856
-
857
- // LAME CBR
858
- if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
859
-
860
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
861
- $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
862
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
863
- //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
864
- // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
865
- //}
866
-
867
- }
868
-
869
- }
870
- }
871
-
872
- } else {
873
-
874
- // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
875
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
876
- if ($recursivesearch) {
877
- $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
878
- if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
879
- $recursivesearch = false;
880
- $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
881
- }
882
- if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
883
- $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
884
- }
885
- }
886
-
887
- }
888
-
889
- }
890
-
891
- if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
892
- if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
893
- if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) {
894
- // ignore, audio data is broken into chunks so will always be data "missing"
895
- } elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
896
- $info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
897
- } else {
898
- $info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)';
899
- }
900
- } else {
901
- if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
902
- // $prenullbytefileoffset = ftell($this->getid3->fp);
903
- // fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
904
- // $PossibleNullByte = fread($this->getid3->fp, 1);
905
- // fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET);
906
- // if ($PossibleNullByte === "\x00") {
907
- $info['avdataend']--;
908
- // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
909
- // } else {
910
- // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
911
- // }
912
- } else {
913
- $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
914
- }
915
- }
916
- }
917
-
918
- if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
919
- if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
920
- $framebytelength = $this->FreeFormatFrameLength($offset, true);
921
- if ($framebytelength > 0) {
922
- $thisfile_mpeg_audio['framelength'] = $framebytelength;
923
- if ($thisfile_mpeg_audio['layer'] == '1') {
924
- // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
925
- $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
926
- } else {
927
- // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
928
- $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
929
- }
930
- } else {
931
- $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
932
- }
933
- }
934
- }
935
-
936
- if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
937
- switch ($thisfile_mpeg_audio['bitrate_mode']) {
938
- case 'vbr':
939
- case 'abr':
940
- $bytes_per_frame = 1152;
941
- if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
942
- $bytes_per_frame = 384;
943
- } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
944
- $bytes_per_frame = 576;
945
- }
946
- $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
947
- if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
948
- $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
949
- $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
950
- }
951
- break;
952
- }
953
- }
954
-
955
- // End variable-bitrate headers
956
- ////////////////////////////////////////////////////////////////////////////////////
957
-
958
- if ($recursivesearch) {
959
-
960
- if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
961
- return false;
962
- }
963
-
964
- }
965
-
966
-
967
- //if (false) {
968
- // // experimental side info parsing section - not returning anything useful yet
969
- //
970
- // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
971
- // $SideInfoOffset = 0;
972
- //
973
- // if ($thisfile_mpeg_audio['version'] == '1') {
974
- // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
975
- // // MPEG-1 (mono)
976
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
977
- // $SideInfoOffset += 9;
978
- // $SideInfoOffset += 5;
979
- // } else {
980
- // // MPEG-1 (stereo, joint-stereo, dual-channel)
981
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
982
- // $SideInfoOffset += 9;
983
- // $SideInfoOffset += 3;
984
- // }
985
- // } else { // 2 or 2.5
986
- // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
987
- // // MPEG-2, MPEG-2.5 (mono)
988
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
989
- // $SideInfoOffset += 8;
990
- // $SideInfoOffset += 1;
991
- // } else {
992
- // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
993
- // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
994
- // $SideInfoOffset += 8;
995
- // $SideInfoOffset += 2;
996
- // }
997
- // }
998
- //
999
- // if ($thisfile_mpeg_audio['version'] == '1') {
1000
- // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1001
- // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1002
- // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1003
- // $SideInfoOffset += 2;
1004
- // }
1005
- // }
1006
- // }
1007
- // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1008
- // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1009
- // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1010
- // $SideInfoOffset += 12;
1011
- // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1012
- // $SideInfoOffset += 9;
1013
- // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1014
- // $SideInfoOffset += 8;
1015
- // if ($thisfile_mpeg_audio['version'] == '1') {
1016
- // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1017
- // $SideInfoOffset += 4;
1018
- // } else {
1019
- // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1020
- // $SideInfoOffset += 9;
1021
- // }
1022
- // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1023
- // $SideInfoOffset += 1;
1024
- //
1025
- // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1026
- //
1027
- // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1028
- // $SideInfoOffset += 2;
1029
- // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1030
- // $SideInfoOffset += 1;
1031
- //
1032
- // for ($region = 0; $region < 2; $region++) {
1033
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1034
- // $SideInfoOffset += 5;
1035
- // }
1036
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1037
- //
1038
- // for ($window = 0; $window < 3; $window++) {
1039
- // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1040
- // $SideInfoOffset += 3;
1041
- // }
1042
- //
1043
- // } else {
1044
- //
1045
- // for ($region = 0; $region < 3; $region++) {
1046
- // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1047
- // $SideInfoOffset += 5;
1048
- // }
1049
- //
1050
- // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1051
- // $SideInfoOffset += 4;
1052
- // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1053
- // $SideInfoOffset += 3;
1054
- // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1055
- // }
1056
- //
1057
- // if ($thisfile_mpeg_audio['version'] == '1') {
1058
- // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1059
- // $SideInfoOffset += 1;
1060
- // }
1061
- // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1062
- // $SideInfoOffset += 1;
1063
- // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1064
- // $SideInfoOffset += 1;
1065
- // }
1066
- // }
1067
- //}
1068
-
1069
- return true;
1070
- }
1071
-
1072
- public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
1073
- $info = &$this->getid3->info;
1074
- $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1075
- $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1076
-
1077
- for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1078
- // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1079
- if (($nextframetestoffset + 4) >= $info['avdataend']) {
1080
- // end of file
1081
- return true;
1082
- }
1083
-
1084
- $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1085
- if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
1086
- if ($ScanAsCBR) {
1087
- // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1088
- if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1089
- return false;
1090
- }
1091
- }
1092
-
1093
-
1094
- // next frame is OK, get ready to check the one after that
1095
- if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1096
- $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1097
- } else {
1098
- $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
1099
- return false;
1100
- }
1101
-
1102
- } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1103
-
1104
- // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
1105
- return true;
1106
-
1107
- } else {
1108
-
1109
- // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1110
- $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
1111
-
1112
- return false;
1113
- }
1114
- }
1115
- return true;
1116
- }
1117
-
1118
- public function FreeFormatFrameLength($offset, $deepscan=false) {
1119
- $info = &$this->getid3->info;
1120
-
1121
- fseek($this->getid3->fp, $offset, SEEK_SET);
1122
- $MPEGaudioData = fread($this->getid3->fp, 32768);
1123
-
1124
- $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1125
- // may be different pattern due to padding
1126
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
1127
- if ($SyncPattern2 === $SyncPattern1) {
1128
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
1129
- }
1130
-
1131
- $framelength = false;
1132
- $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1133
- $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1134
- if ($framelength1 > 4) {
1135
- $framelength = $framelength1;
1136
- }
1137
- if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1138
- $framelength = $framelength2;
1139
- }
1140
- if (!$framelength) {
1141
-
1142
- // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1143
- $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
1144
- $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
1145
-
1146
- if ($framelength1 > 4) {
1147
- $framelength = $framelength1;
1148
- }
1149
- if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1150
- $framelength = $framelength2;
1151
- }
1152
- if (!$framelength) {
1153
- $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
1154
- return false;
1155
- } else {
1156
- $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1157
- $info['audio']['codec'] = 'LAME';
1158
- $info['audio']['encoder'] = 'LAME3.88';
1159
- $SyncPattern1 = substr($SyncPattern1, 0, 3);
1160
- $SyncPattern2 = substr($SyncPattern2, 0, 3);
1161
- }
1162
- }
1163
-
1164
- if ($deepscan) {
1165
-
1166
- $ActualFrameLengthValues = array();
1167
- $nextoffset = $offset + $framelength;
1168
- while ($nextoffset < ($info['avdataend'] - 6)) {
1169
- fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET);
1170
- $NextSyncPattern = fread($this->getid3->fp, 6);
1171
- if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
1172
- // good - found where expected
1173
- $ActualFrameLengthValues[] = $framelength;
1174
- } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
1175
- // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1176
- $ActualFrameLengthValues[] = ($framelength - 1);
1177
- $nextoffset--;
1178
- } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
1179
- // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1180
- $ActualFrameLengthValues[] = ($framelength + 1);
1181
- $nextoffset++;
1182
- } else {
1183
- $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
1184
- return false;
1185
- }
1186
- $nextoffset += $framelength;
1187
- }
1188
- if (count($ActualFrameLengthValues) > 0) {
1189
- $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1190
- }
1191
- }
1192
- return $framelength;
1193
- }
1194
-
1195
- public function getOnlyMPEGaudioInfoBruteForce() {
1196
- $MPEGaudioHeaderDecodeCache = array();
1197
- $MPEGaudioHeaderValidCache = array();
1198
- $MPEGaudioHeaderLengthCache = array();
1199
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1200
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1201
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1202
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1203
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1204
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1205
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1206
- $LongMPEGversionLookup = array();
1207
- $LongMPEGlayerLookup = array();
1208
- $LongMPEGbitrateLookup = array();
1209
- $LongMPEGpaddingLookup = array();
1210
- $LongMPEGfrequencyLookup = array();
1211
- $Distribution['bitrate'] = array();
1212
- $Distribution['frequency'] = array();
1213
- $Distribution['layer'] = array();
1214
- $Distribution['version'] = array();
1215
- $Distribution['padding'] = array();
1216
-
1217
- $info = &$this->getid3->info;
1218
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
1219
-
1220
- $max_frames_scan = 5000;
1221
- $frames_scanned = 0;
1222
-
1223
- $previousvalidframe = $info['avdataoffset'];
1224
- while (ftell($this->getid3->fp) < $info['avdataend']) {
1225
- set_time_limit(30);
1226
- $head4 = fread($this->getid3->fp, 4);
1227
- if (strlen($head4) < 4) {
1228
- break;
1229
- }
1230
- if ($head4{0} != "\xFF") {
1231
- for ($i = 1; $i < 4; $i++) {
1232
- if ($head4{$i} == "\xFF") {
1233
- fseek($this->getid3->fp, $i - 4, SEEK_CUR);
1234
- continue 2;
1235
- }
1236
- }
1237
- continue;
1238
- }
1239
- if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1240
- $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1241
- }
1242
- if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1243
- $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
1244
- }
1245
- if ($MPEGaudioHeaderValidCache[$head4]) {
1246
-
1247
- if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1248
- $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1249
- $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1250
- $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1251
- $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1252
- $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1253
- $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1254
- $LongMPEGbitrateLookup[$head4],
1255
- $LongMPEGversionLookup[$head4],
1256
- $LongMPEGlayerLookup[$head4],
1257
- $LongMPEGpaddingLookup[$head4],
1258
- $LongMPEGfrequencyLookup[$head4]);
1259
- }
1260
- if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1261
- $WhereWeWere = ftell($this->getid3->fp);
1262
- fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1263
- $next4 = fread($this->getid3->fp, 4);
1264
- if ($next4{0} == "\xFF") {
1265
- if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1266
- $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1267
- }
1268
- if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1269
- $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
1270
- }
1271
- if ($MPEGaudioHeaderValidCache[$next4]) {
1272
- fseek($this->getid3->fp, -4, SEEK_CUR);
1273
-
1274
- getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1275
- getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1276
- getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1277
- getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1278
- getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1279
- if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1280
- $pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1281
- $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1282
- foreach ($Distribution as $key1 => $value1) {
1283
- foreach ($value1 as $key2 => $value2) {
1284
- $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1285
- }
1286
- }
1287
- break;
1288
- }
1289
- continue;
1290
- }
1291
- }
1292
- unset($next4);
1293
- fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET);
1294
- }
1295
-
1296
- }
1297
- }
1298
- foreach ($Distribution as $key => $value) {
1299
- ksort($Distribution[$key], SORT_NUMERIC);
1300
- }
1301
- ksort($Distribution['version'], SORT_STRING);
1302
- $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
1303
- $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1304
- $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
1305
- $info['mpeg']['audio']['version_distribution'] = $Distribution['version'];
1306
- $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
1307
- if (count($Distribution['version']) > 1) {
1308
- $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1309
- }
1310
- if (count($Distribution['layer']) > 1) {
1311
- $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1312
- }
1313
- if (count($Distribution['frequency']) > 1) {
1314
- $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1315
- }
1316
-
1317
-
1318
- $bittotal = 0;
1319
- foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1320
- if ($bitratevalue != 'free') {
1321
- $bittotal += ($bitratevalue * $bitratecount);
1322
- }
1323
- }
1324
- $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
1325
- if ($info['mpeg']['audio']['frame_count'] == 0) {
1326
- $info['error'][] = 'no MPEG audio frames found';
1327
- return false;
1328
- }
1329
- $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']);
1330
- $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1331
- $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true);
1332
-
1333
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1334
- $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1335
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1336
- $info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
1337
- $info['fileformat'] = $info['audio']['dataformat'];
1338
-
1339
- return true;
1340
- }
1341
-
1342
-
1343
- public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
1344
- // looks for synch, decodes MPEG audio header
1345
-
1346
- $info = &$this->getid3->info;
1347
-
1348
- static $MPEGaudioVersionLookup;
1349
- static $MPEGaudioLayerLookup;
1350
- static $MPEGaudioBitrateLookup;
1351
- if (empty($MPEGaudioVersionLookup)) {
1352
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1353
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1354
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1355
-
1356
- }
1357
-
1358
- fseek($this->getid3->fp, $avdataoffset, SEEK_SET);
1359
- $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
1360
- if ($sync_seek_buffer_size <= 0) {
1361
- $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
1362
- return false;
1363
- }
1364
- $header = fread($this->getid3->fp, $sync_seek_buffer_size);
1365
- $sync_seek_buffer_size = strlen($header);
1366
- $SynchSeekOffset = 0;
1367
- while ($SynchSeekOffset < $sync_seek_buffer_size) {
1368
- if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) {
1369
-
1370
- if ($SynchSeekOffset > $sync_seek_buffer_size) {
1371
- // if a synch's not found within the first 128k bytes, then give up
1372
- $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
1373
- if (isset($info['audio']['bitrate'])) {
1374
- unset($info['audio']['bitrate']);
1375
- }
1376
- if (isset($info['mpeg']['audio'])) {
1377
- unset($info['mpeg']['audio']);
1378
- }
1379
- if (empty($info['mpeg'])) {
1380
- unset($info['mpeg']);
1381
- }
1382
- return false;
1383
-
1384
- } elseif (feof($this->getid3->fp)) {
1385
-
1386
- $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1387
- if (isset($info['audio']['bitrate'])) {
1388
- unset($info['audio']['bitrate']);
1389
- }
1390
- if (isset($info['mpeg']['audio'])) {
1391
- unset($info['mpeg']['audio']);
1392
- }
1393
- if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1394
- unset($info['mpeg']);
1395
- }
1396
- return false;
1397
- }
1398
- }
1399
-
1400
- if (($SynchSeekOffset + 1) >= strlen($header)) {
1401
- $info['error'][] = 'Could not find valid MPEG synch before end of file';
1402
- return false;
1403
- }
1404
-
1405
- if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1406
- if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1407
- $FirstFrameThisfileInfo = $info;
1408
- $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1409
- if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
1410
- // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1411
- // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1412
- unset($FirstFrameThisfileInfo);
1413
- }
1414
- }
1415
-
1416
- $dummy = $info; // only overwrite real data if valid header found
1417
- if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
1418
- $info = $dummy;
1419
- $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1420
- switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1421
- case '':
1422
- case 'id3':
1423
- case 'ape':
1424
- case 'mp3':
1425
- $info['fileformat'] = 'mp3';
1426
- $info['audio']['dataformat'] = 'mp3';
1427
- break;
1428
- }
1429
- if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1430
- if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1431
- // If there is garbage data between a valid VBR header frame and a sequence
1432
- // of valid MPEG-audio frames the VBR data is no longer discarded.
1433
- $info = $FirstFrameThisfileInfo;
1434
- $info['avdataoffset'] = $FirstFrameAVDataOffset;
1435
- $info['fileformat'] = 'mp3';
1436
- $info['audio']['dataformat'] = 'mp3';
1437
- $dummy = $info;
1438
- unset($dummy['mpeg']['audio']);
1439
- $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1440
- $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
1441
- if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
1442
- $info = $dummy;
1443
- $info['avdataoffset'] = $GarbageOffsetEnd;
1444
- $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
1445
- } else {
1446
- $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
1447
- }
1448
- }
1449
- }
1450
- if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1451
- // VBR file with no VBR header
1452
- $BitrateHistogram = true;
1453
- }
1454
-
1455
- if ($BitrateHistogram) {
1456
-
1457
- $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1458
- $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
1459
-
1460
- if ($info['mpeg']['audio']['version'] == '1') {
1461
- if ($info['mpeg']['audio']['layer'] == 3) {
1462
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1463
- } elseif ($info['mpeg']['audio']['layer'] == 2) {
1464
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1465
- } elseif ($info['mpeg']['audio']['layer'] == 1) {
1466
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1467
- }
1468
- } elseif ($info['mpeg']['audio']['layer'] == 1) {
1469
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1470
- } else {
1471
- $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1472
- }
1473
-
1474
- $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
1475
- $synchstartoffset = $info['avdataoffset'];
1476
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
1477
-
1478
- // you can play with these numbers:
1479
- $max_frames_scan = 50000;
1480
- $max_scan_segments = 10;
1481
-
1482
- // don't play with these numbers:
1483
- $FastMode = false;
1484
- $SynchErrorsFound = 0;
1485
- $frames_scanned = 0;
1486
- $this_scan_segment = 0;
1487
- $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1488
- $pct_data_scanned = 0;
1489
- for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
1490
- $frames_scanned_this_segment = 0;
1491
- if (ftell($this->getid3->fp) >= $info['avdataend']) {
1492
- break;
1493
- }
1494
- $scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1495
- if ($current_segment > 0) {
1496
- fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET);
1497
- $buffer_4k = fread($this->getid3->fp, 4096);
1498
- for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1499
- if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1500
- if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
1501
- $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1502
- if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
1503
- $scan_start_offset[$current_segment] += $j;
1504
- break;
1505
- }
1506
- }
1507
- }
1508
- }
1509
- }
1510
- $synchstartoffset = $scan_start_offset[$current_segment];
1511
- while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
1512
- $FastMode = true;
1513
- $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1514
-
1515
- if (empty($dummy['mpeg']['audio']['framelength'])) {
1516
- $SynchErrorsFound++;
1517
- $synchstartoffset++;
1518
- } else {
1519
- getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1520
- getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1521
- getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1522
- $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1523
- }
1524
- $frames_scanned++;
1525
- if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1526
- $this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1527
- if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1528
- // file likely contains < $max_frames_scan, just scan as one segment
1529
- $max_scan_segments = 1;
1530
- $frames_scan_per_segment = $max_frames_scan;
1531
- } else {
1532
- $pct_data_scanned += $this_pct_scanned;
1533
- break;
1534
- }
1535
- }
1536
- }
1537
- }
1538
- if ($pct_data_scanned > 0) {
1539
- $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1540
- foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1541
- if (!preg_match('#_distribution$#i', $key1)) {
1542
- continue;
1543
- }
1544
- foreach ($value1 as $key2 => $value2) {
1545
- $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1546
- }
1547
- }
1548
- }
1549
-
1550
- if ($SynchErrorsFound > 0) {
1551
- $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
1552
- //return false;
1553
- }
1554
-
1555
- $bittotal = 0;
1556
- $framecounter = 0;
1557
- foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1558
- $framecounter += $bitratecount;
1559
- if ($bitratevalue != 'free') {
1560
- $bittotal += ($bitratevalue * $bitratecount);
1561
- }
1562
- }
1563
- if ($framecounter == 0) {
1564
- $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1565
- return false;
1566
- }
1567
- $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
1568
- $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
1569
-
1570
- $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1571
-
1572
-
1573
- // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1574
- $distinct_bitrates = 0;
1575
- foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1576
- if ($bitrate_count > 0) {
1577
- $distinct_bitrates++;
1578
- }
1579
- }
1580
- if ($distinct_bitrates > 1) {
1581
- $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1582
- } else {
1583
- $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1584
- }
1585
- $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1586
-
1587
- }
1588
-
1589
- break; // exit while()
1590
- }
1591
- }
1592
-
1593
- $SynchSeekOffset++;
1594
- if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1595
- // end of file/data
1596
-
1597
- if (empty($info['mpeg']['audio'])) {
1598
-
1599
- $info['error'][] = 'could not find valid MPEG synch before end of file';
1600
- if (isset($info['audio']['bitrate'])) {
1601
- unset($info['audio']['bitrate']);
1602
- }
1603
- if (isset($info['mpeg']['audio'])) {
1604
- unset($info['mpeg']['audio']);
1605
- }
1606
- if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1607
- unset($info['mpeg']);
1608
- }
1609
- return false;
1610
-
1611
- }
1612
- break;
1613
- }
1614
-
1615
- }
1616
- $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1617
- $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
1618
- $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1619
- return true;
1620
- }
1621
-
1622
-
1623
- public static function MPEGaudioVersionArray() {
1624
- static $MPEGaudioVersion = array('2.5', false, '2', '1');
1625
- return $MPEGaudioVersion;
1626
- }
1627
-
1628
- public static function MPEGaudioLayerArray() {
1629
- static $MPEGaudioLayer = array(false, 3, 2, 1);
1630
- return $MPEGaudioLayer;
1631
- }
1632
-
1633
- public static function MPEGaudioBitrateArray() {
1634
- static $MPEGaudioBitrate;
1635
- if (empty($MPEGaudioBitrate)) {
1636
- $MPEGaudioBitrate = array (
1637
- '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1638
- 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1639
- 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1640
- ),
1641
-
1642
- '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1643
- 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000),
1644
- )
1645
- );
1646
- $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1647
- $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
1648
- }
1649
- return $MPEGaudioBitrate;
1650
- }
1651
-
1652
- public static function MPEGaudioFrequencyArray() {
1653
- static $MPEGaudioFrequency;
1654
- if (empty($MPEGaudioFrequency)) {
1655
- $MPEGaudioFrequency = array (
1656
- '1' => array(44100, 48000, 32000),
1657
- '2' => array(22050, 24000, 16000),
1658
- '2.5' => array(11025, 12000, 8000)
1659
- );
1660
- }
1661
- return $MPEGaudioFrequency;
1662
- }
1663
-
1664
- public static function MPEGaudioChannelModeArray() {
1665
- static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1666
- return $MPEGaudioChannelMode;
1667
- }
1668
-
1669
- public static function MPEGaudioModeExtensionArray() {
1670
- static $MPEGaudioModeExtension;
1671
- if (empty($MPEGaudioModeExtension)) {
1672
- $MPEGaudioModeExtension = array (
1673
- 1 => array('4-31', '8-31', '12-31', '16-31'),
1674
- 2 => array('4-31', '8-31', '12-31', '16-31'),
1675
- 3 => array('', 'IS', 'MS', 'IS+MS')
1676
- );
1677
- }
1678
- return $MPEGaudioModeExtension;
1679
- }
1680
-
1681
- public static function MPEGaudioEmphasisArray() {
1682
- static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1683
- return $MPEGaudioEmphasis;
1684
- }
1685
-
1686
- public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
1687
- return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
1688
- }
1689
-
1690
- public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
1691
- if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1692
- return false;
1693
- }
1694
-
1695
- static $MPEGaudioVersionLookup;
1696
- static $MPEGaudioLayerLookup;
1697
- static $MPEGaudioBitrateLookup;
1698
- static $MPEGaudioFrequencyLookup;
1699
- static $MPEGaudioChannelModeLookup;
1700
- static $MPEGaudioModeExtensionLookup;
1701
- static $MPEGaudioEmphasisLookup;
1702
- if (empty($MPEGaudioVersionLookup)) {
1703
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1704
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1705
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1706
- $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1707
- $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1708
- $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1709
- $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1710
- }
1711
-
1712
- if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1713
- $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1714
- } else {
1715
- echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
1716
- return false;
1717
- }
1718
- if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1719
- $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1720
- } else {
1721
- echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
1722
- return false;
1723
- }
1724
- if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1725
- echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
1726
- if ($rawarray['bitrate'] == 15) {
1727
- // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1728
- // let it go through here otherwise file will not be identified
1729
- if (!$allowBitrate15) {
1730
- return false;
1731
- }
1732
- } else {
1733
- return false;
1734
- }
1735
- }
1736
- if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
1737
- echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
1738
- return false;
1739
- }
1740
- if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
1741
- echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
1742
- return false;
1743
- }
1744
- if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
1745
- echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
1746
- return false;
1747
- }
1748
- if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
1749
- echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
1750
- return false;
1751
- }
1752
- // These are just either set or not set, you can't mess that up :)
1753
- // $rawarray['protection'];
1754
- // $rawarray['padding'];
1755
- // $rawarray['private'];
1756
- // $rawarray['copyright'];
1757
- // $rawarray['original'];
1758
-
1759
- return true;
1760
- }
1761
-
1762
- public static function MPEGaudioHeaderDecode($Header4Bytes) {
1763
- // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
1764
- // A - Frame sync (all bits set)
1765
- // B - MPEG Audio version ID
1766
- // C - Layer description
1767
- // D - Protection bit
1768
- // E - Bitrate index
1769
- // F - Sampling rate frequency index
1770
- // G - Padding bit
1771
- // H - Private bit
1772
- // I - Channel Mode
1773
- // J - Mode extension (Only if Joint stereo)
1774
- // K - Copyright
1775
- // L - Original
1776
- // M - Emphasis
1777
-
1778
- if (strlen($Header4Bytes) != 4) {
1779
- return false;
1780
- }
1781
-
1782
- $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
1783
- $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
1784
- $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
1785
- $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
1786
- $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
1787
- $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
1788
- $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
1789
- $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
1790
- $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
1791
- $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
1792
- $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
1793
- $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
1794
- $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
1795
-
1796
- return $MPEGrawHeader;
1797
- }
1798
-
1799
- public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
1800
- static $AudioFrameLengthCache = array();
1801
-
1802
- if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
1803
- $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
1804
- if ($bitrate != 'free') {
1805
-
1806
- if ($version == '1') {
1807
-
1808
- if ($layer == '1') {
1809
-
1810
- // For Layer I slot is 32 bits long
1811
- $FrameLengthCoefficient = 48;
1812
- $SlotLength = 4;
1813
-
1814
- } else { // Layer 2 / 3
1815
-
1816
- // for Layer 2 and Layer 3 slot is 8 bits long.
1817
- $FrameLengthCoefficient = 144;
1818
- $SlotLength = 1;
1819
-
1820
- }
1821
-
1822
- } else { // MPEG-2 / MPEG-2.5
1823
-
1824
- if ($layer == '1') {
1825
-
1826
- // For Layer I slot is 32 bits long
1827
- $FrameLengthCoefficient = 24;
1828
- $SlotLength = 4;
1829
-
1830
- } elseif ($layer == '2') {
1831
-
1832
- // for Layer 2 and Layer 3 slot is 8 bits long.
1833
- $FrameLengthCoefficient = 144;
1834
- $SlotLength = 1;
1835
-
1836
- } else { // layer 3
1837
-
1838
- // for Layer 2 and Layer 3 slot is 8 bits long.
1839
- $FrameLengthCoefficient = 72;
1840
- $SlotLength = 1;
1841
-
1842
- }
1843
-
1844
- }
1845
-
1846
- // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1847
- if ($samplerate > 0) {
1848
- $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
1849
- $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1850
- if ($padding) {
1851
- $NewFramelength += $SlotLength;
1852
- }
1853
- $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
1854
- }
1855
- }
1856
- }
1857
- return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
1858
- }
1859
-
1860
- public static function ClosestStandardMP3Bitrate($bit_rate) {
1861
- static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1862
- static $bit_rate_table = array (0=>'-');
1863
- $round_bit_rate = intval(round($bit_rate, -3));
1864
- if (!isset($bit_rate_table[$round_bit_rate])) {
1865
- if ($round_bit_rate > max($standard_bit_rates)) {
1866
- $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
1867
- } else {
1868
- $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
1869
- foreach ($standard_bit_rates as $standard_bit_rate) {
1870
- if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
1871
- break;
1872
- }
1873
- $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1874
- }
1875
- }
1876
- }
1877
- return $bit_rate_table[$round_bit_rate];
1878
- }
1879
-
1880
- public static function XingVBRidOffset($version, $channelmode) {
1881
- static $XingVBRidOffsetCache = array();
1882
- if (empty($XingVBRidOffset)) {
1883
- $XingVBRidOffset = array (
1884
- '1' => array ('mono' => 0x15, // 4 + 17 = 21
1885
- 'stereo' => 0x24, // 4 + 32 = 36
1886
- 'joint stereo' => 0x24,
1887
- 'dual channel' => 0x24
1888
- ),
1889
-
1890
- '2' => array ('mono' => 0x0D, // 4 + 9 = 13
1891
- 'stereo' => 0x15, // 4 + 17 = 21
1892
- 'joint stereo' => 0x15,
1893
- 'dual channel' => 0x15
1894
- ),
1895
-
1896
- '2.5' => array ('mono' => 0x15,
1897
- 'stereo' => 0x15,
1898
- 'joint stereo' => 0x15,
1899
- 'dual channel' => 0x15
1900
- )
1901
- );
1902
- }
1903
- return $XingVBRidOffset[$version][$channelmode];
1904
- }
1905
-
1906
- public static function LAMEvbrMethodLookup($VBRmethodID) {
1907
- static $LAMEvbrMethodLookup = array(
1908
- 0x00 => 'unknown',
1909
- 0x01 => 'cbr',
1910
- 0x02 => 'abr',
1911
- 0x03 => 'vbr-old / vbr-rh',
1912
- 0x04 => 'vbr-new / vbr-mtrh',
1913
- 0x05 => 'vbr-mt',
1914
- 0x06 => 'vbr (full vbr method 4)',
1915
- 0x08 => 'cbr (constant bitrate 2 pass)',
1916
- 0x09 => 'abr (2 pass)',
1917
- 0x0F => 'reserved'
1918
- );
1919
- return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
1920
- }
1921
-
1922
- public static function LAMEmiscStereoModeLookup($StereoModeID) {
1923
- static $LAMEmiscStereoModeLookup = array(
1924
- 0 => 'mono',
1925
- 1 => 'stereo',
1926
- 2 => 'dual mono',
1927
- 3 => 'joint stereo',
1928
- 4 => 'forced stereo',
1929
- 5 => 'auto',
1930
- 6 => 'intensity stereo',
1931
- 7 => 'other'
1932
- );
1933
- return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
1934
- }
1935
-
1936
- public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
1937
- static $LAMEmiscSourceSampleFrequencyLookup = array(
1938
- 0 => '<= 32 kHz',
1939
- 1 => '44.1 kHz',
1940
- 2 => '48 kHz',
1941
- 3 => '> 48kHz'
1942
- );
1943
- return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
1944
- }
1945
-
1946
- public static function LAMEsurroundInfoLookup($SurroundInfoID) {
1947
- static $LAMEsurroundInfoLookup = array(
1948
- 0 => 'no surround info',
1949
- 1 => 'DPL encoding',
1950
- 2 => 'DPL2 encoding',
1951
- 3 => 'Ambisonic encoding'
1952
- );
1953
- return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
1954
- }
1955
-
1956
- public static function LAMEpresetUsedLookup($LAMEtag) {
1957
-
1958
- if ($LAMEtag['preset_used_id'] == 0) {
1959
- // no preset used (LAME >=3.93)
1960
- // no preset recorded (LAME <3.93)
1961
- return '';
1962
- }
1963
- $LAMEpresetUsedLookup = array();
1964
-
1965
- ///// THIS PART CANNOT BE STATIC .
1966
- for ($i = 8; $i <= 320; $i++) {
1967
- switch ($LAMEtag['vbr_method']) {
1968
- case 'cbr':
1969
- $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
1970
- break;
1971
- case 'abr':
1972
- default: // other VBR modes shouldn't be here(?)
1973
- $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
1974
- break;
1975
- }
1976
- }
1977
-
1978
- // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1979
-
1980
- // named alt-presets
1981
- $LAMEpresetUsedLookup[1000] = '--r3mix';
1982
- $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
1983
- $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
1984
- $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
1985
- $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
1986
- $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
1987
- $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
1988
- $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
1989
-
1990
- // LAME 3.94 additions/changes
1991
- $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
1992
- $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
1993
-
1994
- $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
1995
- $LAMEpresetUsedLookup[410] = '-V9';
1996
- $LAMEpresetUsedLookup[420] = '-V8';
1997
- $LAMEpresetUsedLookup[440] = '-V6';
1998
- $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
1999
- $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003
2000
- $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003
2001
- $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
2002
- $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003
2003
- $LAMEpresetUsedLookup[490] = '-V1';
2004
- $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003
2005
-
2006
- return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
2007
- }
2008
-
2009
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio.mpc.php DELETED
@@ -1,506 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio.mpc.php //
11
- // module for analyzing Musepack/MPEG+ Audio files //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- class getid3_mpc extends getid3_handler
18
- {
19
-
20
- public function Analyze() {
21
- $info = &$this->getid3->info;
22
-
23
- $info['mpc']['header'] = array();
24
- $thisfile_mpc_header = &$info['mpc']['header'];
25
-
26
- $info['fileformat'] = 'mpc';
27
- $info['audio']['dataformat'] = 'mpc';
28
- $info['audio']['bitrate_mode'] = 'vbr';
29
- $info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
30
- $info['audio']['lossless'] = false;
31
-
32
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
33
- $MPCheaderData = fread($this->getid3->fp, 4);
34
- $info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
35
- if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) {
36
-
37
- // this is SV8
38
- return $this->ParseMPCsv8();
39
-
40
- } elseif (preg_match('#^MP\+#', $info['mpc']['header']['preamble'])) {
41
-
42
- // this is SV7
43
- return $this->ParseMPCsv7();
44
-
45
- } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) {
46
-
47
- // this is SV4 - SV6, handle seperately
48
- return $this->ParseMPCsv6();
49
-
50
- } else {
51
-
52
- $info['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($MPCheaderData, 0, 4)).'"';
53
- unset($info['fileformat']);
54
- unset($info['mpc']);
55
- return false;
56
-
57
- }
58
- return false;
59
- }
60
-
61
-
62
- public function ParseMPCsv8() {
63
- // this is SV8
64
- // http://trac.musepack.net/trac/wiki/SV8Specification
65
-
66
- $info = &$this->getid3->info;
67
- $thisfile_mpc_header = &$info['mpc']['header'];
68
-
69
- $keyNameSize = 2;
70
- $maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
71
-
72
- $offset = ftell($this->getid3->fp);
73
- while ($offset < $info['avdataend']) {
74
- $thisPacket = array();
75
- $thisPacket['offset'] = $offset;
76
- $packet_offset = 0;
77
-
78
- // Size is a variable-size field, could be 1-4 bytes (possibly more?)
79
- // read enough data in and figure out the exact size later
80
- $MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength);
81
- $packet_offset += $keyNameSize;
82
- $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
83
- $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
84
- if ($thisPacket['key'] == $thisPacket['key_name']) {
85
- $info['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
86
- return false;
87
- }
88
- $packetLength = 0;
89
- $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
90
- if ($thisPacket['packet_size'] === false) {
91
- $info['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize);
92
- return false;
93
- }
94
- $packet_offset += $packetLength;
95
- $offset += $thisPacket['packet_size'];
96
-
97
- switch ($thisPacket['key']) {
98
- case 'SH': // Stream Header
99
- $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
100
- if ($moreBytesToRead > 0) {
101
- $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
102
- }
103
- $thisPacket['crc'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
104
- $packet_offset += 4;
105
- $thisPacket['stream_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
106
- $packet_offset += 1;
107
-
108
- $packetLength = 0;
109
- $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
110
- $packet_offset += $packetLength;
111
-
112
- $packetLength = 0;
113
- $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
114
- $packet_offset += $packetLength;
115
-
116
- $otherUsefulData = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
117
- $packet_offset += 2;
118
- $thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
119
- $thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
120
- $thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
121
- $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
122
- $thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
123
- $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
124
-
125
- $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
126
- $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
127
- $thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
128
- $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
129
-
130
- $info['audio']['channels'] = $thisPacket['channels'];
131
- $info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
132
- $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
133
- $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
134
- break;
135
-
136
- case 'RG': // Replay Gain
137
- $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
138
- if ($moreBytesToRead > 0) {
139
- $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
140
- }
141
- $thisPacket['replaygain_version'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
142
- $packet_offset += 1;
143
- $thisPacket['replaygain_title_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
144
- $packet_offset += 2;
145
- $thisPacket['replaygain_title_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
146
- $packet_offset += 2;
147
- $thisPacket['replaygain_album_gain'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
148
- $packet_offset += 2;
149
- $thisPacket['replaygain_album_peak'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
150
- $packet_offset += 2;
151
-
152
- if ($thisPacket['replaygain_title_gain']) { $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; }
153
- if ($thisPacket['replaygain_title_peak']) { $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; }
154
- if ($thisPacket['replaygain_album_gain']) { $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; }
155
- if ($thisPacket['replaygain_album_peak']) { $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; }
156
- break;
157
-
158
- case 'EI': // Encoder Info
159
- $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
160
- if ($moreBytesToRead > 0) {
161
- $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
162
- }
163
- $profile_pns = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
164
- $packet_offset += 1;
165
- $quality_int = (($profile_pns & 0xF0) >> 4);
166
- $quality_dec = (($profile_pns & 0x0E) >> 3);
167
- $thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
168
- $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
169
- $thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
170
- $packet_offset += 1;
171
- $thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
172
- $packet_offset += 1;
173
- $thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
174
- $packet_offset += 1;
175
- $thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
176
-
177
- $info['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
178
- $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
179
- //$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
180
- $thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
181
- break;
182
-
183
- case 'SO': // Seek Table Offset
184
- $packetLength = 0;
185
- $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
186
- $packet_offset += $packetLength;
187
- break;
188
-
189
- case 'ST': // Seek Table
190
- case 'SE': // Stream End
191
- case 'AP': // Audio Data
192
- // nothing useful here, just skip this packet
193
- $thisPacket = array();
194
- break;
195
-
196
- default:
197
- $info['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
198
- return false;
199
- break;
200
- }
201
- if (!empty($thisPacket)) {
202
- $info['mpc']['packets'][] = $thisPacket;
203
- }
204
- fseek($this->getid3->fp, $offset);
205
- }
206
- $thisfile_mpc_header['size'] = $offset;
207
- return true;
208
- }
209
-
210
- public function ParseMPCsv7() {
211
- // this is SV7
212
- // http://www.uni-jena.de/~pfk/mpp/sv8/header.html
213
-
214
- $info = &$this->getid3->info;
215
- $thisfile_mpc_header = &$info['mpc']['header'];
216
- $offset = 0;
217
-
218
- $thisfile_mpc_header['size'] = 28;
219
- $MPCheaderData = $info['mpc']['header']['preamble'];
220
- $MPCheaderData .= fread($this->getid3->fp, $thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble']));
221
- $offset = strlen('MP+');
222
-
223
- $StreamVersionByte = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
224
- $offset += 1;
225
- $thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
226
- $thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
227
- $thisfile_mpc_header['frame_count'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
228
- $offset += 4;
229
-
230
- if ($thisfile_mpc_header['stream_version_major'] != 7) {
231
- $info['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')';
232
- return false;
233
- }
234
-
235
- $FlagsDWORD1 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
236
- $offset += 4;
237
- $thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
238
- $thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
239
- $thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
240
- $thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
241
- $thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
242
- $thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
243
- $thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
244
- $thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
245
-
246
- $thisfile_mpc_header['raw']['title_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
247
- $offset += 2;
248
- $thisfile_mpc_header['raw']['title_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
249
- $offset += 2;
250
-
251
- $thisfile_mpc_header['raw']['album_peak'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
252
- $offset += 2;
253
- $thisfile_mpc_header['raw']['album_gain'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
254
- $offset += 2;
255
-
256
- $FlagsDWORD2 = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
257
- $offset += 4;
258
- $thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
259
- $thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
260
-
261
-
262
- $thisfile_mpc_header['raw']['not_sure_what'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
263
- $offset += 3;
264
- $thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
265
- $offset += 1;
266
-
267
- $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
268
- $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
269
- if ($thisfile_mpc_header['sample_rate'] == 0) {
270
- $info['error'][] = 'Corrupt MPC file: frequency == zero';
271
- return false;
272
- }
273
- $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
274
- $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $info['audio']['channels'];
275
-
276
- $info['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $info['audio']['channels']) / $info['audio']['sample_rate'];
277
- if ($info['playtime_seconds'] == 0) {
278
- $info['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
279
- return false;
280
- }
281
-
282
- // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
283
- $info['avdataoffset'] += $thisfile_mpc_header['size'];
284
-
285
- $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
286
-
287
- $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak'];
288
- $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
289
- if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
290
- $thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
291
- } else {
292
- $thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
293
- }
294
-
295
- $thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak'];
296
- $thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
297
- if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
298
- $thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
299
- } else {
300
- $thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
301
- }
302
- $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
303
-
304
- $info['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
305
- $info['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
306
-
307
- if ($thisfile_mpc_header['title_peak'] > 0) {
308
- $info['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
309
- } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
310
- $info['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
311
- }
312
- if ($thisfile_mpc_header['album_peak'] > 0) {
313
- $info['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
314
- }
315
-
316
- //$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
317
- $info['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
318
- $info['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
319
- $thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
320
-
321
- return true;
322
- }
323
-
324
- public function ParseMPCsv6() {
325
- // this is SV4 - SV6
326
-
327
- $info = &$this->getid3->info;
328
- $thisfile_mpc_header = &$info['mpc']['header'];
329
- $offset = 0;
330
-
331
- $thisfile_mpc_header['size'] = 8;
332
- fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
333
- $MPCheaderData = fread($this->getid3->fp, $thisfile_mpc_header['size']);
334
-
335
- // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
336
- $info['avdataoffset'] += $thisfile_mpc_header['size'];
337
-
338
- // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
339
- $HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4));
340
- $HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
341
-
342
-
343
- // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
344
- // aaaa aaaa abcd dddd dddd deee eeff ffff
345
- //
346
- // a = bitrate = anything
347
- // b = IS = anything
348
- // c = MS = anything
349
- // d = streamversion = 0000000004 or 0000000005 or 0000000006
350
- // e = maxband = anything
351
- // f = blocksize = 000001 for SV5+, anything(?) for SV4
352
-
353
- $thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
354
- $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
355
- $thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
356
- $thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
357
- $thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
358
- $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
359
- $thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
360
-
361
- switch ($thisfile_mpc_header['stream_version_major']) {
362
- case 4:
363
- $thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
364
- break;
365
-
366
- case 5:
367
- case 6:
368
- $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
369
- break;
370
-
371
- default:
372
- $info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
373
- unset($info['mpc']);
374
- return false;
375
- break;
376
- }
377
-
378
- if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
379
- $info['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
380
- }
381
-
382
- $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
383
- $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
384
- $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels'];
385
-
386
- if ($thisfile_mpc_header['target_bitrate'] == 0) {
387
- $info['audio']['bitrate_mode'] = 'vbr';
388
- } else {
389
- $info['audio']['bitrate_mode'] = 'cbr';
390
- }
391
-
392
- $info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
393
- $info['audio']['bitrate'] = $info['mpc']['bitrate'];
394
- $info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
395
-
396
- return true;
397
- }
398
-
399
-
400
- public function MPCprofileNameLookup($profileid) {
401
- static $MPCprofileNameLookup = array(
402
- 0 => 'no profile',
403
- 1 => 'Experimental',
404
- 2 => 'unused',
405
- 3 => 'unused',
406
- 4 => 'unused',
407
- 5 => 'below Telephone (q = 0.0)',
408
- 6 => 'below Telephone (q = 1.0)',
409
- 7 => 'Telephone (q = 2.0)',
410
- 8 => 'Thumb (q = 3.0)',
411
- 9 => 'Radio (q = 4.0)',
412
- 10 => 'Standard (q = 5.0)',
413
- 11 => 'Extreme (q = 6.0)',
414
- 12 => 'Insane (q = 7.0)',
415
- 13 => 'BrainDead (q = 8.0)',
416
- 14 => 'above BrainDead (q = 9.0)',
417
- 15 => 'above BrainDead (q = 10.0)'
418
- );
419
- return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
420
- }
421
-
422
- public function MPCfrequencyLookup($frequencyid) {
423
- static $MPCfrequencyLookup = array(
424
- 0 => 44100,
425
- 1 => 48000,
426
- 2 => 37800,
427
- 3 => 32000
428
- );
429
- return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
430
- }
431
-
432
- public function MPCpeakDBLookup($intvalue) {
433
- if ($intvalue > 0) {
434
- return ((log10($intvalue) / log10(2)) - 15) * 6;
435
- }
436
- return false;
437
- }
438
-
439
- public function MPCencoderVersionLookup($encoderversion) {
440
- //Encoder version * 100 (106 = 1.06)
441
- //EncoderVersion % 10 == 0 Release (1.0)
442
- //EncoderVersion % 2 == 0 Beta (1.06)
443
- //EncoderVersion % 2 == 1 Alpha (1.05a...z)
444
-
445
- if ($encoderversion == 0) {
446
- // very old version, not known exactly which
447
- return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
448
- }
449
-
450
- if (($encoderversion % 10) == 0) {
451
-
452
- // release version
453
- return number_format($encoderversion / 100, 2);
454
-
455
- } elseif (($encoderversion % 2) == 0) {
456
-
457
- // beta version
458
- return number_format($encoderversion / 100, 2).' beta';
459
-
460
- }
461
-
462
- // alpha version
463
- return number_format($encoderversion / 100, 2).' alpha';
464
- }
465
-
466
- public function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
467
- $packet_size = 0;
468
- for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
469
- // variable-length size field:
470
- // bits, big-endian
471
- // 0xxx xxxx - value 0 to 2^7-1
472
- // 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
473
- // 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
474
- // 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
475
- // ...
476
- $thisbyte = ord(substr($data, ($packetLength - 1), 1));
477
- // look through bytes until find a byte with MSB==0
478
- $packet_size = ($packet_size << 7);
479
- $packet_size = ($packet_size | ($thisbyte & 0x7F));
480
- if (($thisbyte & 0x80) === 0) {
481
- break;
482
- }
483
- if ($packetLength >= $maxHandledPacketLength) {
484
- return false;
485
- }
486
- }
487
- return $packet_size;
488
- }
489
-
490
- public function MPCsv8PacketName($packetKey) {
491
- static $MPCsv8PacketName = array();
492
- if (empty($MPCsv8PacketName)) {
493
- $MPCsv8PacketName = array(
494
- 'AP' => 'Audio Packet',
495
- 'CT' => 'Chapter Tag',
496
- 'EI' => 'Encoder Info',
497
- 'RG' => 'Replay Gain',
498
- 'SE' => 'Stream End',
499
- 'SH' => 'Stream Header',
500
- 'SO' => 'Seek Table Offset',
501
- 'ST' => 'Seek Table',
502
- );
503
- }
504
- return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
505
- }
506
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.audio.ogg.php DELETED
@@ -1,671 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.audio.ogg.php //
11
- // module for analyzing Ogg Vorbis, OggFLAC and Speex files //
12
- // dependencies: module.audio.flac.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
17
-
18
- class getid3_ogg extends getid3_handler
19
- {
20
- // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
21
- public function Analyze() {
22
- $info = &$this->getid3->info;
23
-
24
- $info['fileformat'] = 'ogg';
25
-
26
- // Warn about illegal tags - only vorbiscomments are allowed
27
- if (isset($info['id3v2'])) {
28
- $info['warning'][] = 'Illegal ID3v2 tag present.';
29
- }
30
- if (isset($info['id3v1'])) {
31
- $info['warning'][] = 'Illegal ID3v1 tag present.';
32
- }
33
- if (isset($info['ape'])) {
34
- $info['warning'][] = 'Illegal APE tag present.';
35
- }
36
-
37
-
38
- // Page 1 - Stream Header
39
-
40
- $this->fseek($info['avdataoffset']);
41
-
42
- $oggpageinfo = $this->ParseOggPageHeader();
43
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
44
-
45
- if ($this->ftell() >= $this->getid3->fread_buffer_size()) {
46
- $info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)';
47
- unset($info['fileformat']);
48
- unset($info['ogg']);
49
- return false;
50
- }
51
-
52
- $filedata = $this->fread($oggpageinfo['page_length']);
53
- $filedataoffset = 0;
54
-
55
- if (substr($filedata, 0, 4) == 'fLaC') {
56
-
57
- $info['audio']['dataformat'] = 'flac';
58
- $info['audio']['bitrate_mode'] = 'vbr';
59
- $info['audio']['lossless'] = true;
60
-
61
- } elseif (substr($filedata, 1, 6) == 'vorbis') {
62
-
63
- $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
64
-
65
- } elseif (substr($filedata, 0, 8) == 'Speex ') {
66
-
67
- // http://www.speex.org/manual/node10.html
68
-
69
- $info['audio']['dataformat'] = 'speex';
70
- $info['mime_type'] = 'audio/speex';
71
- $info['audio']['bitrate_mode'] = 'abr';
72
- $info['audio']['lossless'] = false;
73
-
74
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex '
75
- $filedataoffset += 8;
76
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20);
77
- $filedataoffset += 20;
78
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
79
- $filedataoffset += 4;
80
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
81
- $filedataoffset += 4;
82
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
83
- $filedataoffset += 4;
84
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
85
- $filedataoffset += 4;
86
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
87
- $filedataoffset += 4;
88
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
89
- $filedataoffset += 4;
90
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
91
- $filedataoffset += 4;
92
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
93
- $filedataoffset += 4;
94
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
95
- $filedataoffset += 4;
96
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
97
- $filedataoffset += 4;
98
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
99
- $filedataoffset += 4;
100
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
101
- $filedataoffset += 4;
102
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
103
- $filedataoffset += 4;
104
-
105
- $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
106
- $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
107
- $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
108
- $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
109
- $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
110
-
111
- $info['audio']['sample_rate'] = $info['speex']['sample_rate'];
112
- $info['audio']['channels'] = $info['speex']['channels'];
113
- if ($info['speex']['vbr']) {
114
- $info['audio']['bitrate_mode'] = 'vbr';
115
- }
116
-
117
-
118
- } elseif (substr($filedata, 0, 8) == "fishead\x00") {
119
-
120
- // Ogg Skeleton version 3.0 Format Specification
121
- // http://xiph.org/ogg/doc/skeleton.html
122
- $filedataoffset += 8;
123
- $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
124
- $filedataoffset += 2;
125
- $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
126
- $filedataoffset += 2;
127
- $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
128
- $filedataoffset += 8;
129
- $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
130
- $filedataoffset += 8;
131
- $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
132
- $filedataoffset += 8;
133
- $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
134
- $filedataoffset += 8;
135
- $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20));
136
- $filedataoffset += 20;
137
-
138
- $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor'];
139
- $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'];
140
- $info['ogg']['skeleton']['fishead']['basetime'] = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'];
141
- $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc'];
142
-
143
-
144
- $counter = 0;
145
- do {
146
- $oggpageinfo = $this->ParseOggPageHeader();
147
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo;
148
- $filedata = $this->fread($oggpageinfo['page_length']);
149
- $this->fseek($oggpageinfo['page_end_offset']);
150
-
151
- if (substr($filedata, 0, 8) == "fisbone\x00") {
152
-
153
- $filedataoffset = 8;
154
- $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
155
- $filedataoffset += 4;
156
- $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
157
- $filedataoffset += 4;
158
- $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
159
- $filedataoffset += 4;
160
- $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
161
- $filedataoffset += 8;
162
- $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
163
- $filedataoffset += 8;
164
- $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
165
- $filedataoffset += 8;
166
- $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
167
- $filedataoffset += 4;
168
- $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
169
- $filedataoffset += 1;
170
- $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3);
171
- $filedataoffset += 3;
172
-
173
- } elseif (substr($filedata, 1, 6) == 'theora') {
174
-
175
- $info['video']['dataformat'] = 'theora';
176
- $info['error'][] = 'Ogg Theora not correctly handled in this version of getID3 ['.$this->getid3->version().']';
177
- //break;
178
-
179
- } elseif (substr($filedata, 1, 6) == 'vorbis') {
180
-
181
- $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
182
-
183
- } else {
184
- $info['error'][] = 'unexpected';
185
- //break;
186
- }
187
- //} while ($oggpageinfo['page_seqno'] == 0);
188
- } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00"));
189
-
190
- $this->fseek($oggpageinfo['page_start_offset']);
191
-
192
- $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']';
193
- //return false;
194
-
195
- } else {
196
-
197
- $info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
198
- unset($info['ogg']);
199
- unset($info['mime_type']);
200
- return false;
201
-
202
- }
203
-
204
- // Page 2 - Comment Header
205
- $oggpageinfo = $this->ParseOggPageHeader();
206
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
207
-
208
- switch ($info['audio']['dataformat']) {
209
- case 'vorbis':
210
- $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
211
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
212
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis'
213
-
214
- $this->ParseVorbisComments();
215
- break;
216
-
217
- case 'flac':
218
- $flac = new getid3_flac($this->getid3);
219
- if (!$flac->parseMETAdata()) {
220
- $info['error'][] = 'Failed to parse FLAC headers';
221
- return false;
222
- }
223
- unset($flac);
224
- break;
225
-
226
- case 'speex':
227
- $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
228
- $this->ParseVorbisComments();
229
- break;
230
- }
231
-
232
-
233
- // Last Page - Number of Samples
234
- if (!getid3_lib::intValueSupported($info['avdataend'])) {
235
-
236
- $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)';
237
-
238
- } else {
239
-
240
- $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0));
241
- $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size()));
242
- if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
243
- $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO')));
244
- $info['avdataend'] = $this->ftell();
245
- $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader();
246
- $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position'];
247
- if ($info['ogg']['samples'] == 0) {
248
- $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
249
- return false;
250
- }
251
- if (!empty($info['audio']['sample_rate'])) {
252
- $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']);
253
- }
254
- }
255
-
256
- }
257
-
258
- if (!empty($info['ogg']['bitrate_average'])) {
259
- $info['audio']['bitrate'] = $info['ogg']['bitrate_average'];
260
- } elseif (!empty($info['ogg']['bitrate_nominal'])) {
261
- $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal'];
262
- } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) {
263
- $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2;
264
- }
265
- if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) {
266
- if ($info['audio']['bitrate'] == 0) {
267
- $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
268
- return false;
269
- }
270
- $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']);
271
- }
272
-
273
- if (isset($info['ogg']['vendor'])) {
274
- $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']);
275
-
276
- // Vorbis only
277
- if ($info['audio']['dataformat'] == 'vorbis') {
278
-
279
- // Vorbis 1.0 starts with Xiph.Org
280
- if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) {
281
-
282
- if ($info['audio']['bitrate_mode'] == 'abr') {
283
-
284
- // Set -b 128 on abr files
285
- $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000);
286
-
287
- } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) {
288
- // Set -q N on vbr files
289
- $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']);
290
-
291
- }
292
- }
293
-
294
- if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) {
295
- $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps';
296
- }
297
- }
298
- }
299
-
300
- return true;
301
- }
302
-
303
- public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
304
- $info = &$this->getid3->info;
305
- $info['audio']['dataformat'] = 'vorbis';
306
- $info['audio']['lossless'] = false;
307
-
308
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
309
- $filedataoffset += 1;
310
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
311
- $filedataoffset += 6;
312
- $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
313
- $filedataoffset += 4;
314
- $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
315
- $filedataoffset += 1;
316
- $info['audio']['channels'] = $info['ogg']['numberofchannels'];
317
- $info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
318
- $filedataoffset += 4;
319
- if ($info['ogg']['samplerate'] == 0) {
320
- $info['error'][] = 'Corrupt Ogg file: sample rate == zero';
321
- return false;
322
- }
323
- $info['audio']['sample_rate'] = $info['ogg']['samplerate'];
324
- $info['ogg']['samples'] = 0; // filled in later
325
- $info['ogg']['bitrate_average'] = 0; // filled in later
326
- $info['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
327
- $filedataoffset += 4;
328
- $info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
329
- $filedataoffset += 4;
330
- $info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
331
- $filedataoffset += 4;
332
- $info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
333
- $info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
334
- $info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
335
-
336
- $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
337
- if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) {
338
- unset($info['ogg']['bitrate_max']);
339
- $info['audio']['bitrate_mode'] = 'abr';
340
- }
341
- if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
342
- unset($info['ogg']['bitrate_nominal']);
343
- }
344
- if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) {
345
- unset($info['ogg']['bitrate_min']);
346
- $info['audio']['bitrate_mode'] = 'abr';
347
- }
348
- return true;
349
- }
350
-
351
- public function ParseOggPageHeader() {
352
- // http://xiph.org/ogg/vorbis/doc/framing.html
353
- $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
354
-
355
- $filedata = $this->fread($this->getid3->fread_buffer_size());
356
- $filedataoffset = 0;
357
- while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
358
- if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) {
359
- // should be found before here
360
- return false;
361
- }
362
- if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
363
- if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) {
364
- // get some more data, unless eof, in which case fail
365
- return false;
366
- }
367
- }
368
- }
369
- $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
370
-
371
- $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
372
- $filedataoffset += 1;
373
- $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
374
- $filedataoffset += 1;
375
- $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
376
- $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
377
- $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
378
-
379
- $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
380
- $filedataoffset += 8;
381
- $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
382
- $filedataoffset += 4;
383
- $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
384
- $filedataoffset += 4;
385
- $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
386
- $filedataoffset += 4;
387
- $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
388
- $filedataoffset += 1;
389
- $oggheader['page_length'] = 0;
390
- for ($i = 0; $i < $oggheader['page_segments']; $i++) {
391
- $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
392
- $filedataoffset += 1;
393
- $oggheader['page_length'] += $oggheader['segment_table'][$i];
394
- }
395
- $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
396
- $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length'];
397
- $this->fseek($oggheader['header_end_offset']);
398
-
399
- return $oggheader;
400
- }
401
-
402
- // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005
403
- public function ParseVorbisComments() {
404
- $info = &$this->getid3->info;
405
-
406
- $OriginalOffset = $this->ftell();
407
- $commentdataoffset = 0;
408
- $VorbisCommentPage = 1;
409
-
410
- switch ($info['audio']['dataformat']) {
411
- case 'vorbis':
412
- case 'speex':
413
- $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
414
- $this->fseek($CommentStartOffset);
415
- $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
416
- $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
417
-
418
- if ($info['audio']['dataformat'] == 'vorbis') {
419
- $commentdataoffset += (strlen('vorbis') + 1);
420
- }
421
- break;
422
-
423
- case 'flac':
424
- $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4;
425
- $this->fseek($CommentStartOffset);
426
- $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']);
427
- break;
428
-
429
- default:
430
- return false;
431
- }
432
-
433
- $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
434
- $commentdataoffset += 4;
435
-
436
- $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
437
- $commentdataoffset += $VendorSize;
438
-
439
- $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
440
- $commentdataoffset += 4;
441
- $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
442
-
443
- $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
444
- $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
445
- for ($i = 0; $i < $CommentsCount; $i++) {
446
-
447
- $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
448
-
449
- if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
450
- if ($oggpageinfo = $this->ParseOggPageHeader()) {
451
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
452
-
453
- $VorbisCommentPage++;
454
-
455
- // First, save what we haven't read yet
456
- $AsYetUnusedData = substr($commentdata, $commentdataoffset);
457
-
458
- // Then take that data off the end
459
- $commentdata = substr($commentdata, 0, $commentdataoffset);
460
-
461
- // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
462
- $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
463
- $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
464
-
465
- // Finally, stick the unused data back on the end
466
- $commentdata .= $AsYetUnusedData;
467
-
468
- //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
469
- $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1));
470
- }
471
-
472
- }
473
- $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
474
-
475
- // replace avdataoffset with position just after the last vorbiscomment
476
- $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4;
477
-
478
- $commentdataoffset += 4;
479
- while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) {
480
- if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) {
481
- $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments';
482
- break 2;
483
- }
484
-
485
- $VorbisCommentPage++;
486
-
487
- $oggpageinfo = $this->ParseOggPageHeader();
488
- $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
489
-
490
- // First, save what we haven't read yet
491
- $AsYetUnusedData = substr($commentdata, $commentdataoffset);
492
-
493
- // Then take that data off the end
494
- $commentdata = substr($commentdata, 0, $commentdataoffset);
495
-
496
- // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
497
- $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
498
- $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
499
-
500
- // Finally, stick the unused data back on the end
501
- $commentdata .= $AsYetUnusedData;
502
-
503
- //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
504
- if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) {
505
- $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
506
- break;
507
- }
508
- $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1);
509
- if ($readlength <= 0) {
510
- $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell();
511
- break;
512
- }
513
- $commentdata .= $this->fread($readlength);
514
-
515
- //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
516
- }
517
- $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset;
518
- $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']);
519
- $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size'];
520
-
521
- if (!$commentstring) {
522
-
523
- // no comment?
524
- $info['warning'][] = 'Blank Ogg comment ['.$i.']';
525
-
526
- } elseif (strstr($commentstring, '=')) {
527
-
528
- $commentexploded = explode('=', $commentstring, 2);
529
- $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]);
530
- $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : '');
531
-
532
- if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') {
533
-
534
- // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE
535
- // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard.
536
- // http://flac.sourceforge.net/format.html#metadata_block_picture
537
- $flac = new getid3_flac($this->getid3);
538
- $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']));
539
- $flac->parsePICTURE();
540
- $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0];
541
- unset($flac);
542
-
543
- } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') {
544
-
545
- $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']);
546
- $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure');
547
- /** @todo use 'coverartmime' where available */
548
- $imageinfo = getid3_lib::GetDataImageSize($data);
549
- if ($imageinfo === false || !isset($imageinfo['mime'])) {
550
- $this->warning('COVERART vorbiscomment tag contains invalid image');
551
- continue;
552
- }
553
-
554
- $ogg = new self($this->getid3);
555
- $ogg->setStringMode($data);
556
- $info['ogg']['comments']['picture'][] = array(
557
- 'image_mime' => $imageinfo['mime'],
558
- 'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
559
- );
560
- unset($ogg);
561
-
562
- } else {
563
-
564
- $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value'];
565
-
566
- }
567
-
568
- } else {
569
-
570
- $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
571
-
572
- }
573
- unset($ThisFileInfo_ogg_comments_raw[$i]);
574
- }
575
- unset($ThisFileInfo_ogg_comments_raw);
576
-
577
-
578
- // Replay Gain Adjustment
579
- // http://privatewww.essex.ac.uk/~djmrob/replaygain/
580
- if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) {
581
- foreach ($info['ogg']['comments'] as $index => $commentvalue) {
582
- switch ($index) {
583
- case 'rg_audiophile':
584
- case 'replaygain_album_gain':
585
- $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
586
- unset($info['ogg']['comments'][$index]);
587
- break;
588
-
589
- case 'rg_radio':
590
- case 'replaygain_track_gain':
591
- $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
592
- unset($info['ogg']['comments'][$index]);
593
- break;
594
-
595
- case 'replaygain_album_peak':
596
- $info['replay_gain']['album']['peak'] = (double) $commentvalue[0];
597
- unset($info['ogg']['comments'][$index]);
598
- break;
599
-
600
- case 'rg_peak':
601
- case 'replaygain_track_peak':
602
- $info['replay_gain']['track']['peak'] = (double) $commentvalue[0];
603
- unset($info['ogg']['comments'][$index]);
604
- break;
605
-
606
- case 'replaygain_reference_loudness':
607
- $info['replay_gain']['reference_volume'] = (double) $commentvalue[0];
608
- unset($info['ogg']['comments'][$index]);
609
- break;
610
-
611
- default:
612
- // do nothing
613
- break;
614
- }
615
- }
616
- }
617
-
618
- $this->fseek($OriginalOffset);
619
-
620
- return true;
621
- }
622
-
623
- public static function SpeexBandModeLookup($mode) {
624
- static $SpeexBandModeLookup = array();
625
- if (empty($SpeexBandModeLookup)) {
626
- $SpeexBandModeLookup[0] = 'narrow';
627
- $SpeexBandModeLookup[1] = 'wide';
628
- $SpeexBandModeLookup[2] = 'ultra-wide';
629
- }
630
- return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
631
- }
632
-
633
-
634
- public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
635
- for ($i = 0; $i < $SegmentNumber; $i++) {
636
- $segmentlength = 0;
637
- foreach ($OggInfoArray['segment_table'] as $key => $value) {
638
- $segmentlength += $value;
639
- if ($value < 255) {
640
- break;
641
- }
642
- }
643
- }
644
- return $segmentlength;
645
- }
646
-
647
-
648
- public static function get_quality_from_nominal_bitrate($nominal_bitrate) {
649
-
650
- // decrease precision
651
- $nominal_bitrate = $nominal_bitrate / 1000;
652
-
653
- if ($nominal_bitrate < 128) {
654
- // q-1 to q4
655
- $qval = ($nominal_bitrate - 64) / 16;
656
- } elseif ($nominal_bitrate < 256) {
657
- // q4 to q8
658
- $qval = $nominal_bitrate / 32;
659
- } elseif ($nominal_bitrate < 320) {
660
- // q8 to q9
661
- $qval = ($nominal_bitrate + 256) / 64;
662
- } else {
663
- // q9 to q10
664
- $qval = ($nominal_bitrate + 1300) / 180;
665
- }
666
- //return $qval; // 5.031324
667
- //return intval($qval); // 5
668
- return round($qval, 1); // 5 or 4.9
669
- }
670
-
671
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.tag.apetag.php DELETED
@@ -1,370 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.tag.apetag.php //
11
- // module for analyzing APE tags //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- class getid3_apetag extends getid3_handler
17
- {
18
- public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory
19
- public $overrideendoffset = 0;
20
-
21
- public function Analyze() {
22
- $info = &$this->getid3->info;
23
-
24
- if (!getid3_lib::intValueSupported($info['filesize'])) {
25
- $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
26
- return false;
27
- }
28
-
29
- $id3v1tagsize = 128;
30
- $apetagheadersize = 32;
31
- $lyrics3tagsize = 10;
32
-
33
- if ($this->overrideendoffset == 0) {
34
-
35
- fseek($this->getid3->fp, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
36
- $APEfooterID3v1 = fread($this->getid3->fp, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
37
-
38
- //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
39
- if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
40
-
41
- // APE tag found before ID3v1
42
- $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
43
-
44
- //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
45
- } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
46
-
47
- // APE tag found, no ID3v1
48
- $info['ape']['tag_offset_end'] = $info['filesize'];
49
-
50
- }
51
-
52
- } else {
53
-
54
- fseek($this->getid3->fp, $this->overrideendoffset - $apetagheadersize, SEEK_SET);
55
- if (fread($this->getid3->fp, 8) == 'APETAGEX') {
56
- $info['ape']['tag_offset_end'] = $this->overrideendoffset;
57
- }
58
-
59
- }
60
- if (!isset($info['ape']['tag_offset_end'])) {
61
-
62
- // APE tag not found
63
- unset($info['ape']);
64
- return false;
65
-
66
- }
67
-
68
- // shortcut
69
- $thisfile_ape = &$info['ape'];
70
-
71
- fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
72
- $APEfooterData = fread($this->getid3->fp, 32);
73
- if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
74
- $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
75
- return false;
76
- }
77
-
78
- if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
79
- fseek($this->getid3->fp, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
80
- $thisfile_ape['tag_offset_start'] = ftell($this->getid3->fp);
81
- $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
82
- } else {
83
- $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
84
- fseek($this->getid3->fp, $thisfile_ape['tag_offset_start'], SEEK_SET);
85
- $APEtagData = fread($this->getid3->fp, $thisfile_ape['footer']['raw']['tagsize']);
86
- }
87
- $info['avdataend'] = $thisfile_ape['tag_offset_start'];
88
-
89
- if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
90
- $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
91
- unset($info['id3v1']);
92
- foreach ($info['warning'] as $key => $value) {
93
- if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
94
- unset($info['warning'][$key]);
95
- sort($info['warning']);
96
- break;
97
- }
98
- }
99
- }
100
-
101
- $offset = 0;
102
- if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
103
- if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
104
- $offset += $apetagheadersize;
105
- } else {
106
- $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
107
- return false;
108
- }
109
- }
110
-
111
- // shortcut
112
- $info['replay_gain'] = array();
113
- $thisfile_replaygain = &$info['replay_gain'];
114
-
115
- for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
116
- $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
117
- $offset += 4;
118
- $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
119
- $offset += 4;
120
- if (strstr(substr($APEtagData, $offset), "\x00") === false) {
121
- $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
122
- return false;
123
- }
124
- $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
125
- $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
126
-
127
- // shortcut
128
- $thisfile_ape['items'][$item_key] = array();
129
- $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
130
-
131
- $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
132
-
133
- $offset += ($ItemKeyLength + 1); // skip 0x00 terminator
134
- $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
135
- $offset += $value_size;
136
-
137
- $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
138
- switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
139
- case 0: // UTF-8
140
- case 3: // Locator (URL, filename, etc), UTF-8 encoded
141
- $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
142
- break;
143
-
144
- default: // binary data
145
- break;
146
- }
147
-
148
- switch (strtolower($item_key)) {
149
- case 'replaygain_track_gain':
150
- $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
151
- $thisfile_replaygain['track']['originator'] = 'unspecified';
152
- break;
153
-
154
- case 'replaygain_track_peak':
155
- $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
156
- $thisfile_replaygain['track']['originator'] = 'unspecified';
157
- if ($thisfile_replaygain['track']['peak'] <= 0) {
158
- $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
159
- }
160
- break;
161
-
162
- case 'replaygain_album_gain':
163
- $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
164
- $thisfile_replaygain['album']['originator'] = 'unspecified';
165
- break;
166
-
167
- case 'replaygain_album_peak':
168
- $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
169
- $thisfile_replaygain['album']['originator'] = 'unspecified';
170
- if ($thisfile_replaygain['album']['peak'] <= 0) {
171
- $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
172
- }
173
- break;
174
-
175
- case 'mp3gain_undo':
176
- list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
177
- $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
178
- $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
179
- $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
180
- break;
181
-
182
- case 'mp3gain_minmax':
183
- list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
184
- $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
185
- $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
186
- break;
187
-
188
- case 'mp3gain_album_minmax':
189
- list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
190
- $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
191
- $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
192
- break;
193
-
194
- case 'tracknumber':
195
- if (is_array($thisfile_ape_items_current['data'])) {
196
- foreach ($thisfile_ape_items_current['data'] as $comment) {
197
- $thisfile_ape['comments']['track'][] = $comment;
198
- }
199
- }
200
- break;
201
-
202
- case 'cover art (artist)':
203
- case 'cover art (back)':
204
- case 'cover art (band logo)':
205
- case 'cover art (band)':
206
- case 'cover art (colored fish)':
207
- case 'cover art (composer)':
208
- case 'cover art (conductor)':
209
- case 'cover art (front)':
210
- case 'cover art (icon)':
211
- case 'cover art (illustration)':
212
- case 'cover art (lead)':
213
- case 'cover art (leaflet)':
214
- case 'cover art (lyricist)':
215
- case 'cover art (media)':
216
- case 'cover art (movie scene)':
217
- case 'cover art (other icon)':
218
- case 'cover art (other)':
219
- case 'cover art (performance)':
220
- case 'cover art (publisher logo)':
221
- case 'cover art (recording)':
222
- case 'cover art (studio)':
223
- // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
224
- list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
225
- $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
226
- $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
227
-
228
- $thisfile_ape_items_current['image_mime'] = '';
229
- $imageinfo = array();
230
- $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
231
- $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
232
-
233
- do {
234
- if ($this->inline_attachments === false) {
235
- // skip entirely
236
- unset($thisfile_ape_items_current['data']);
237
- break;
238
- }
239
- if ($this->inline_attachments === true) {
240
- // great
241
- } elseif (is_int($this->inline_attachments)) {
242
- if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
243
- // too big, skip
244
- $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)';
245
- unset($thisfile_ape_items_current['data']);
246
- break;
247
- }
248
- } elseif (is_string($this->inline_attachments)) {
249
- $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
250
- if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
251
- // cannot write, skip
252
- $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)';
253
- unset($thisfile_ape_items_current['data']);
254
- break;
255
- }
256
- }
257
- // if we get this far, must be OK
258
- if (is_string($this->inline_attachments)) {
259
- $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset'];
260
- if (!file_exists($destination_filename) || is_writable($destination_filename)) {
261
- file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
262
- } else {
263
- $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)';
264
- }
265
- $thisfile_ape_items_current['data_filename'] = $destination_filename;
266
- unset($thisfile_ape_items_current['data']);
267
- } else {
268
- if (!isset($info['ape']['comments']['picture'])) {
269
- $info['ape']['comments']['picture'] = array();
270
- }
271
- $info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']);
272
- }
273
- } while (false);
274
- break;
275
-
276
- default:
277
- if (is_array($thisfile_ape_items_current['data'])) {
278
- foreach ($thisfile_ape_items_current['data'] as $comment) {
279
- $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
280
- }
281
- }
282
- break;
283
- }
284
-
285
- }
286
- if (empty($thisfile_replaygain)) {
287
- unset($info['replay_gain']);
288
- }
289
- return true;
290
- }
291
-
292
- public function parseAPEheaderFooter($APEheaderFooterData) {
293
- // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
294
-
295
- // shortcut
296
- $headerfooterinfo['raw'] = array();
297
- $headerfooterinfo_raw = &$headerfooterinfo['raw'];
298
-
299
- $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
300
- if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
301
- return false;
302
- }
303
- $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
304
- $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
305
- $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
306
- $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
307
- $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
308
-
309
- $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
310
- if ($headerfooterinfo['tag_version'] >= 2) {
311
- $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
312
- }
313
- return $headerfooterinfo;
314
- }
315
-
316
- public function parseAPEtagFlags($rawflagint) {
317
- // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
318
- // All are set to zero on creation and ignored on reading."
319
- // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
320
- $flags['header'] = (bool) ($rawflagint & 0x80000000);
321
- $flags['footer'] = (bool) ($rawflagint & 0x40000000);
322
- $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
323
- $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1;
324
- $flags['read_only'] = (bool) ($rawflagint & 0x00000001);
325
-
326
- $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
327
-
328
- return $flags;
329
- }
330
-
331
- public function APEcontentTypeFlagLookup($contenttypeid) {
332
- static $APEcontentTypeFlagLookup = array(
333
- 0 => 'utf-8',
334
- 1 => 'binary',
335
- 2 => 'external',
336
- 3 => 'reserved'
337
- );
338
- return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
339
- }
340
-
341
- public function APEtagItemIsUTF8Lookup($itemkey) {
342
- static $APEtagItemIsUTF8Lookup = array(
343
- 'title',
344
- 'subtitle',
345
- 'artist',
346
- 'album',
347
- 'debut album',
348
- 'publisher',
349
- 'conductor',
350
- 'track',
351
- 'composer',
352
- 'comment',
353
- 'copyright',
354
- 'publicationright',
355
- 'file',
356
- 'year',
357
- 'record date',
358
- 'record location',
359
- 'genre',
360
- 'media',
361
- 'related',
362
- 'isrc',
363
- 'abstract',
364
- 'language',
365
- 'bibliography'
366
- );
367
- return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
368
- }
369
-
370
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.tag.id3v1.php DELETED
@@ -1,359 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // module.tag.id3v1.php //
11
- // module for analyzing ID3v1 tags //
12
- // dependencies: NONE //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- class getid3_id3v1 extends getid3_handler
18
- {
19
-
20
- public function Analyze() {
21
- $info = &$this->getid3->info;
22
-
23
- if (!getid3_lib::intValueSupported($info['filesize'])) {
24
- $info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
25
- return false;
26
- }
27
-
28
- fseek($this->getid3->fp, -256, SEEK_END);
29
- $preid3v1 = fread($this->getid3->fp, 128);
30
- $id3v1tag = fread($this->getid3->fp, 128);
31
-
32
- if (substr($id3v1tag, 0, 3) == 'TAG') {
33
-
34
- $info['avdataend'] = $info['filesize'] - 128;
35
-
36
- $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
37
- $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
38
- $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
39
- $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
40
- $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them
41
- $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
42
-
43
- // If second-last byte of comment field is null and last byte of comment field is non-null
44
- // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
45
- if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
46
- $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
47
- $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
48
- }
49
- $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
50
-
51
- $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
52
- if (!empty($ParsedID3v1['genre'])) {
53
- unset($ParsedID3v1['genreid']);
54
- }
55
- if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) {
56
- unset($ParsedID3v1['genre']);
57
- }
58
-
59
- foreach ($ParsedID3v1 as $key => $value) {
60
- $ParsedID3v1['comments'][$key][0] = $value;
61
- }
62
-
63
- // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
64
- $GoodFormatID3v1tag = $this->GenerateID3v1Tag(
65
- $ParsedID3v1['title'],
66
- $ParsedID3v1['artist'],
67
- $ParsedID3v1['album'],
68
- $ParsedID3v1['year'],
69
- (isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
70
- $ParsedID3v1['comment'],
71
- (!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
72
- $ParsedID3v1['padding_valid'] = true;
73
- if ($id3v1tag !== $GoodFormatID3v1tag) {
74
- $ParsedID3v1['padding_valid'] = false;
75
- $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
76
- }
77
-
78
- $ParsedID3v1['tag_offset_end'] = $info['filesize'];
79
- $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
80
-
81
- $info['id3v1'] = $ParsedID3v1;
82
- }
83
-
84
- if (substr($preid3v1, 0, 3) == 'TAG') {
85
- // The way iTunes handles tags is, well, brain-damaged.
86
- // It completely ignores v1 if ID3v2 is present.
87
- // This goes as far as adding a new v1 tag *even if there already is one*
88
-
89
- // A suspected double-ID3v1 tag has been detected, but it could be that
90
- // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
91
- if (substr($preid3v1, 96, 8) == 'APETAGEX') {
92
- // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
93
- } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
94
- // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
95
- } else {
96
- // APE and Lyrics3 footers not found - assume double ID3v1
97
- $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
98
- $info['avdataend'] -= 128;
99
- }
100
- }
101
-
102
- return true;
103
- }
104
-
105
- public static function cutfield($str) {
106
- return trim(substr($str, 0, strcspn($str, "\x00")));
107
- }
108
-
109
- public static function ArrayOfGenres($allowSCMPXextended=false) {
110
- static $GenreLookup = array(
111
- 0 => 'Blues',
112
- 1 => 'Classic Rock',
113
- 2 => 'Country',
114
- 3 => 'Dance',
115
- 4 => 'Disco',
116
- 5 => 'Funk',
117
- 6 => 'Grunge',
118
- 7 => 'Hip-Hop',
119
- 8 => 'Jazz',
120
- 9 => 'Metal',
121
- 10 => 'New Age',
122
- 11 => 'Oldies',
123
- 12 => 'Other',
124
- 13 => 'Pop',
125
- 14 => 'R&B',
126
- 15 => 'Rap',
127
- 16 => 'Reggae',
128
- 17 => 'Rock',
129
- 18 => 'Techno',
130
- 19 => 'Industrial',
131
- 20 => 'Alternative',
132
- 21 => 'Ska',
133
- 22 => 'Death Metal',
134
- 23 => 'Pranks',
135
- 24 => 'Soundtrack',
136
- 25 => 'Euro-Techno',
137
- 26 => 'Ambient',
138
- 27 => 'Trip-Hop',
139
- 28 => 'Vocal',
140
- 29 => 'Jazz+Funk',
141
- 30 => 'Fusion',
142
- 31 => 'Trance',
143
- 32 => 'Classical',
144
- 33 => 'Instrumental',
145
- 34 => 'Acid',
146
- 35 => 'House',
147
- 36 => 'Game',
148
- 37 => 'Sound Clip',
149
- 38 => 'Gospel',
150
- 39 => 'Noise',
151
- 40 => 'Alt. Rock',
152
- 41 => 'Bass',
153
- 42 => 'Soul',
154
- 43 => 'Punk',
155
- 44 => 'Space',
156
- 45 => 'Meditative',
157
- 46 => 'Instrumental Pop',
158
- 47 => 'Instrumental Rock',
159
- 48 => 'Ethnic',
160
- 49 => 'Gothic',
161
- 50 => 'Darkwave',
162
- 51 => 'Techno-Industrial',
163
- 52 => 'Electronic',
164
- 53 => 'Pop-Folk',
165
- 54 => 'Eurodance',
166
- 55 => 'Dream',
167
- 56 => 'Southern Rock',
168
- 57 => 'Comedy',
169
- 58 => 'Cult',
170
- 59 => 'Gangsta Rap',
171
- 60 => 'Top 40',
172
- 61 => 'Christian Rap',
173
- 62 => 'Pop/Funk',
174
- 63 => 'Jungle',
175
- 64 => 'Native American',
176
- 65 => 'Cabaret',
177
- 66 => 'New Wave',
178
- 67 => 'Psychedelic',
179
- 68 => 'Rave',
180
- 69 => 'Showtunes',
181
- 70 => 'Trailer',
182
- 71 => 'Lo-Fi',
183
- 72 => 'Tribal',
184
- 73 => 'Acid Punk',
185
- 74 => 'Acid Jazz',
186
- 75 => 'Polka',
187
- 76 => 'Retro',
188
- 77 => 'Musical',
189
- 78 => 'Rock & Roll',
190
- 79 => 'Hard Rock',
191
- 80 => 'Folk',
192
- 81 => 'Folk/Rock',
193
- 82 => 'National Folk',
194
- 83 => 'Swing',
195
- 84 => 'Fast-Fusion',
196
- 85 => 'Bebob',
197
- 86 => 'Latin',
198
- 87 => 'Revival',
199
- 88 => 'Celtic',
200
- 89 => 'Bluegrass',
201
- 90 => 'Avantgarde',
202
- 91 => 'Gothic Rock',
203
- 92 => 'Progressive Rock',
204
- 93 => 'Psychedelic Rock',
205
- 94 => 'Symphonic Rock',
206
- 95 => 'Slow Rock',
207
- 96 => 'Big Band',
208
- 97 => 'Chorus',
209
- 98 => 'Easy Listening',
210
- 99 => 'Acoustic',
211
- 100 => 'Humour',
212
- 101 => 'Speech',
213
- 102 => 'Chanson',
214
- 103 => 'Opera',
215
- 104 => 'Chamber Music',
216
- 105 => 'Sonata',
217
- 106 => 'Symphony',
218
- 107 => 'Booty Bass',
219
- 108 => 'Primus',
220
- 109 => 'Porn Groove',
221
- 110 => 'Satire',
222
- 111 => 'Slow Jam',
223
- 112 => 'Club',
224
- 113 => 'Tango',
225
- 114 => 'Samba',
226
- 115 => 'Folklore',
227
- 116 => 'Ballad',
228
- 117 => 'Power Ballad',
229
- 118 => 'Rhythmic Soul',
230
- 119 => 'Freestyle',
231
- 120 => 'Duet',
232
- 121 => 'Punk Rock',
233
- 122 => 'Drum Solo',
234
- 123 => 'A Cappella',
235
- 124 => 'Euro-House',
236
- 125 => 'Dance Hall',
237
- 126 => 'Goa',
238
- 127 => 'Drum & Bass',
239
- 128 => 'Club-House',
240
- 129 => 'Hardcore',
241
- 130 => 'Terror',
242
- 131 => 'Indie',
243
- 132 => 'BritPop',
244
- 133 => 'Negerpunk',
245
- 134 => 'Polsk Punk',
246
- 135 => 'Beat',
247
- 136 => 'Christian Gangsta Rap',
248
- 137 => 'Heavy Metal',
249
- 138 => 'Black Metal',
250
- 139 => 'Crossover',
251
- 140 => 'Contemporary Christian',
252
- 141 => 'Christian Rock',
253
- 142 => 'Merengue',
254
- 143 => 'Salsa',
255
- 144 => 'Thrash Metal',
256
- 145 => 'Anime',
257
- 146 => 'JPop',
258
- 147 => 'Synthpop',
259
-
260
- 255 => 'Unknown',
261
-
262
- 'CR' => 'Cover',
263
- 'RX' => 'Remix'
264
- );
265
-
266
- static $GenreLookupSCMPX = array();
267
- if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
268
- $GenreLookupSCMPX = $GenreLookup;
269
- // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
270
- // Extended ID3v1 genres invented by SCMPX
271
- // Note that 255 "Japanese Anime" conflicts with standard "Unknown"
272
- $GenreLookupSCMPX[240] = 'Sacred';
273
- $GenreLookupSCMPX[241] = 'Northern Europe';
274
- $GenreLookupSCMPX[242] = 'Irish & Scottish';
275
- $GenreLookupSCMPX[243] = 'Scotland';
276
- $GenreLookupSCMPX[244] = 'Ethnic Europe';
277
- $GenreLookupSCMPX[245] = 'Enka';
278
- $GenreLookupSCMPX[246] = 'Children\'s Song';
279
- $GenreLookupSCMPX[247] = 'Japanese Sky';
280
- $GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
281
- $GenreLookupSCMPX[249] = 'Japanese Doom Rock';
282
- $GenreLookupSCMPX[250] = 'Japanese J-POP';
283
- $GenreLookupSCMPX[251] = 'Japanese Seiyu';
284
- $GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
285
- $GenreLookupSCMPX[253] = 'Japanese Moemoe';
286
- $GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
287
- //$GenreLookupSCMPX[255] = 'Japanese Anime';
288
- }
289
-
290
- return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
291
- }
292
-
293
- public static function LookupGenreName($genreid, $allowSCMPXextended=true) {
294
- switch ($genreid) {
295
- case 'RX':
296
- case 'CR':
297
- break;
298
- default:
299
- if (!is_numeric($genreid)) {
300
- return false;
301
- }
302
- $genreid = intval($genreid); // to handle 3 or '3' or '03'
303
- break;
304
- }
305
- $GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
306
- return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
307
- }
308
-
309
- public static function LookupGenreID($genre, $allowSCMPXextended=false) {
310
- $GenreLookup = self::ArrayOfGenres($allowSCMPXextended);
311
- $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
312
- foreach ($GenreLookup as $key => $value) {
313
- if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
314
- return $key;
315
- }
316
- }
317
- return false;
318
- }
319
-
320
- public static function StandardiseID3v1GenreName($OriginalGenre) {
321
- if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) {
322
- return self::LookupGenreName($GenreID);
323
- }
324
- return $OriginalGenre;
325
- }
326
-
327
- public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
328
- $ID3v1Tag = 'TAG';
329
- $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
330
- $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
331
- $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
332
- $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT);
333
- if (!empty($track) && ($track > 0) && ($track <= 255)) {
334
- $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
335
- $ID3v1Tag .= "\x00";
336
- if (gettype($track) == 'string') {
337
- $track = (int) $track;
338
- }
339
- $ID3v1Tag .= chr($track);
340
- } else {
341
- $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
342
- }
343
- if (($genreid < 0) || ($genreid > 147)) {
344
- $genreid = 255; // 'unknown' genre
345
- }
346
- switch (gettype($genreid)) {
347
- case 'string':
348
- case 'integer':
349
- $ID3v1Tag .= chr(intval($genreid));
350
- break;
351
- default:
352
- $ID3v1Tag .= chr(255); // 'unknown' genre
353
- break;
354
- }
355
-
356
- return $ID3v1Tag;
357
- }
358
-
359
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.tag.id3v2.php DELETED
@@ -1,3414 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- /// //
10
- // module.tag.id3v2.php //
11
- // module for analyzing ID3v2 tags //
12
- // dependencies: module.tag.id3v1.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
17
-
18
- class getid3_id3v2 extends getid3_handler
19
- {
20
- public $StartingOffset = 0;
21
-
22
- public function Analyze() {
23
- $info = &$this->getid3->info;
24
-
25
- // Overall tag structure:
26
- // +-----------------------------+
27
- // | Header (10 bytes) |
28
- // +-----------------------------+
29
- // | Extended Header |
30
- // | (variable length, OPTIONAL) |
31
- // +-----------------------------+
32
- // | Frames (variable length) |
33
- // +-----------------------------+
34
- // | Padding |
35
- // | (variable length, OPTIONAL) |
36
- // +-----------------------------+
37
- // | Footer (10 bytes, OPTIONAL) |
38
- // +-----------------------------+
39
-
40
- // Header
41
- // ID3v2/file identifier "ID3"
42
- // ID3v2 version $04 00
43
- // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
44
- // ID3v2 size 4 * %0xxxxxxx
45
-
46
-
47
- // shortcuts
48
- $info['id3v2']['header'] = true;
49
- $thisfile_id3v2 = &$info['id3v2'];
50
- $thisfile_id3v2['flags'] = array();
51
- $thisfile_id3v2_flags = &$thisfile_id3v2['flags'];
52
-
53
-
54
- fseek($this->getid3->fp, $this->StartingOffset, SEEK_SET);
55
- $header = fread($this->getid3->fp, 10);
56
- if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
57
-
58
- $thisfile_id3v2['majorversion'] = ord($header{3});
59
- $thisfile_id3v2['minorversion'] = ord($header{4});
60
-
61
- // shortcut
62
- $id3v2_majorversion = &$thisfile_id3v2['majorversion'];
63
-
64
- } else {
65
-
66
- unset($info['id3v2']);
67
- return false;
68
-
69
- }
70
-
71
- if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
72
-
73
- $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion'];
74
- return false;
75
-
76
- }
77
-
78
- $id3_flags = ord($header{5});
79
- switch ($id3v2_majorversion) {
80
- case 2:
81
- // %ab000000 in v2.2
82
- $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
83
- $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
84
- break;
85
-
86
- case 3:
87
- // %abc00000 in v2.3
88
- $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
89
- $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
90
- $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
91
- break;
92
-
93
- case 4:
94
- // %abcd0000 in v2.4
95
- $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
96
- $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header
97
- $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator
98
- $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present
99
- break;
100
- }
101
-
102
- $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
103
-
104
- $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset;
105
- $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
106
-
107
-
108
-
109
- // create 'encoding' key - used by getid3::HandleAllTags()
110
- // in ID3v2 every field can have it's own encoding type
111
- // so force everything to UTF-8 so it can be handled consistantly
112
- $thisfile_id3v2['encoding'] = 'UTF-8';
113
-
114
-
115
- // Frames
116
-
117
- // All ID3v2 frames consists of one frame header followed by one or more
118
- // fields containing the actual information. The header is always 10
119
- // bytes and laid out as follows:
120
- //
121
- // Frame ID $xx xx xx xx (four characters)
122
- // Size 4 * %0xxxxxxx
123
- // Flags $xx xx
124
-
125
- $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
126
- if (!empty($thisfile_id3v2['exthead']['length'])) {
127
- $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
128
- }
129
- if (!empty($thisfile_id3v2_flags['isfooter'])) {
130
- $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
131
- }
132
- if ($sizeofframes > 0) {
133
-
134
- $framedata = fread($this->getid3->fp, $sizeofframes); // read all frames from file into $framedata variable
135
-
136
- // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
137
- if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) {
138
- $framedata = $this->DeUnsynchronise($framedata);
139
- }
140
- // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
141
- // of on tag level, making it easier to skip frames, increasing the streamability
142
- // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
143
- // there exists an unsynchronised frame, while the new unsynchronisation flag in
144
- // the frame header [S:4.1.2] indicates unsynchronisation.
145
-
146
-
147
- //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
148
- $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
149
-
150
-
151
- // Extended Header
152
- if (!empty($thisfile_id3v2_flags['exthead'])) {
153
- $extended_header_offset = 0;
154
-
155
- if ($id3v2_majorversion == 3) {
156
-
157
- // v2.3 definition:
158
- //Extended header size $xx xx xx xx // 32-bit integer
159
- //Extended Flags $xx xx
160
- // %x0000000 %00000000 // v2.3
161
- // x - CRC data present
162
- //Size of padding $xx xx xx xx
163
-
164
- $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
165
- $extended_header_offset += 4;
166
-
167
- $thisfile_id3v2['exthead']['flag_bytes'] = 2;
168
- $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
169
- $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
170
-
171
- $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
172
-
173
- $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
174
- $extended_header_offset += 4;
175
-
176
- if ($thisfile_id3v2['exthead']['flags']['crc']) {
177
- $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
178
- $extended_header_offset += 4;
179
- }
180
- $extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
181
-
182
- } elseif ($id3v2_majorversion == 4) {
183
-
184
- // v2.4 definition:
185
- //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer
186
- //Number of flag bytes $01
187
- //Extended Flags $xx
188
- // %0bcd0000 // v2.4
189
- // b - Tag is an update
190
- // Flag data length $00
191
- // c - CRC data present
192
- // Flag data length $05
193
- // Total frame CRC 5 * %0xxxxxxx
194
- // d - Tag restrictions
195
- // Flag data length $01
196
-
197
- $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true);
198
- $extended_header_offset += 4;
199
-
200
- $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1
201
- $extended_header_offset += 1;
202
-
203
- $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
204
- $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
205
-
206
- $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40);
207
- $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20);
208
- $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10);
209
-
210
- if ($thisfile_id3v2['exthead']['flags']['update']) {
211
- $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0
212
- $extended_header_offset += 1;
213
- }
214
-
215
- if ($thisfile_id3v2['exthead']['flags']['crc']) {
216
- $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5
217
- $extended_header_offset += 1;
218
- $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false);
219
- $extended_header_offset += $ext_header_chunk_length;
220
- }
221
-
222
- if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
223
- $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1
224
- $extended_header_offset += 1;
225
-
226
- // %ppqrrstt
227
- $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
228
- $extended_header_offset += 1;
229
- $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions
230
- $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions
231
- $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions
232
- $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions
233
- $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions
234
-
235
- $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']);
236
- $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']);
237
- $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']);
238
- $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']);
239
- $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']);
240
- }
241
-
242
- if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) {
243
- $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')';
244
- }
245
- }
246
-
247
- $framedataoffset += $extended_header_offset;
248
- $framedata = substr($framedata, $extended_header_offset);
249
- } // end extended header
250
-
251
-
252
- while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
253
- if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
254
- // insufficient room left in ID3v2 header for actual data - must be padding
255
- $thisfile_id3v2['padding']['start'] = $framedataoffset;
256
- $thisfile_id3v2['padding']['length'] = strlen($framedata);
257
- $thisfile_id3v2['padding']['valid'] = true;
258
- for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
259
- if ($framedata{$i} != "\x00") {
260
- $thisfile_id3v2['padding']['valid'] = false;
261
- $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
262
- $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
263
- break;
264
- }
265
- }
266
- break; // skip rest of ID3v2 header
267
- }
268
- if ($id3v2_majorversion == 2) {
269
- // Frame ID $xx xx xx (three characters)
270
- // Size $xx xx xx (24-bit integer)
271
- // Flags $xx xx
272
-
273
- $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
274
- $framedata = substr($framedata, 6); // and leave the rest in $framedata
275
- $frame_name = substr($frame_header, 0, 3);
276
- $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
277
- $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
278
-
279
- } elseif ($id3v2_majorversion > 2) {
280
-
281
- // Frame ID $xx xx xx xx (four characters)
282
- // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
283
- // Flags $xx xx
284
-
285
- $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
286
- $framedata = substr($framedata, 10); // and leave the rest in $framedata
287
-
288
- $frame_name = substr($frame_header, 0, 4);
289
- if ($id3v2_majorversion == 3) {
290
- $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
291
- } else { // ID3v2.4+
292
- $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
293
- }
294
-
295
- if ($frame_size < (strlen($framedata) + 4)) {
296
- $nextFrameID = substr($framedata, $frame_size, 4);
297
- if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
298
- // next frame is OK
299
- } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
300
- // MP3ext known broken frames - "ok" for the purposes of this test
301
- } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
302
- $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
303
- $id3v2_majorversion = 3;
304
- $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
305
- }
306
- }
307
-
308
-
309
- $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
310
- }
311
-
312
- if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
313
- // padding encountered
314
-
315
- $thisfile_id3v2['padding']['start'] = $framedataoffset;
316
- $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
317
- $thisfile_id3v2['padding']['valid'] = true;
318
-
319
- $len = strlen($framedata);
320
- for ($i = 0; $i < $len; $i++) {
321
- if ($framedata{$i} != "\x00") {
322
- $thisfile_id3v2['padding']['valid'] = false;
323
- $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
324
- $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
325
- break;
326
- }
327
- }
328
- break; // skip rest of ID3v2 header
329
- }
330
-
331
- if ($frame_name == 'COM ') {
332
- $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
333
- $frame_name = 'COMM';
334
- }
335
- if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
336
-
337
- unset($parsedFrame);
338
- $parsedFrame['frame_name'] = $frame_name;
339
- $parsedFrame['frame_flags_raw'] = $frame_flags;
340
- $parsedFrame['data'] = substr($framedata, 0, $frame_size);
341
- $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size);
342
- $parsedFrame['dataoffset'] = $framedataoffset;
343
-
344
- $this->ParseID3v2Frame($parsedFrame);
345
- $thisfile_id3v2[$frame_name][] = $parsedFrame;
346
-
347
- $framedata = substr($framedata, $frame_size);
348
-
349
- } else { // invalid frame length or FrameID
350
-
351
- if ($frame_size <= strlen($framedata)) {
352
-
353
- if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
354
-
355
- // next frame is valid, just skip the current frame
356
- $framedata = substr($framedata, $frame_size);
357
- $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.';
358
-
359
- } else {
360
-
361
- // next frame is invalid too, abort processing
362
- //unset($framedata);
363
- $framedata = null;
364
- $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
365
-
366
- }
367
-
368
- } elseif ($frame_size == strlen($framedata)) {
369
-
370
- // this is the last frame, just skip
371
- $info['warning'][] = 'This was the last ID3v2 frame.';
372
-
373
- } else {
374
-
375
- // next frame is invalid too, abort processing
376
- //unset($framedata);
377
- $framedata = null;
378
- $info['warning'][] = 'Invalid ID3v2 frame size, aborting.';
379
-
380
- }
381
- if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
382
-
383
- switch ($frame_name) {
384
- case "\x00\x00".'MP':
385
- case "\x00".'MP3':
386
- case ' MP3':
387
- case 'MP3e':
388
- case "\x00".'MP':
389
- case ' MP':
390
- case 'MP3':
391
- $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]';
392
- break;
393
-
394
- default:
395
- $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).';
396
- break;
397
- }
398
-
399
- } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) {
400
-
401
- $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).';
402
-
403
- } else {
404
-
405
- $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).';
406
-
407
- }
408
-
409
- }
410
- $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
411
-
412
- }
413
-
414
- }
415
-
416
-
417
- // Footer
418
-
419
- // The footer is a copy of the header, but with a different identifier.
420
- // ID3v2 identifier "3DI"
421
- // ID3v2 version $04 00
422
- // ID3v2 flags %abcd0000
423
- // ID3v2 size 4 * %0xxxxxxx
424
-
425
- if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
426
- $footer = fread($this->getid3->fp, 10);
427
- if (substr($footer, 0, 3) == '3DI') {
428
- $thisfile_id3v2['footer'] = true;
429
- $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
430
- $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
431
- }
432
- if ($thisfile_id3v2['majorversion_footer'] <= 4) {
433
- $id3_flags = ord(substr($footer{5}));
434
- $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
435
- $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
436
- $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
437
- $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
438
-
439
- $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
440
- }
441
- } // end footer
442
-
443
- if (isset($thisfile_id3v2['comments']['genre'])) {
444
- foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
445
- unset($thisfile_id3v2['comments']['genre'][$key]);
446
- $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value)));
447
- }
448
- }
449
-
450
- if (isset($thisfile_id3v2['comments']['track'])) {
451
- foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
452
- if (strstr($value, '/')) {
453
- list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
454
- }
455
- }
456
- }
457
-
458
- if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
459
- $thisfile_id3v2['comments']['year'] = array($matches[1]);
460
- }
461
-
462
-
463
- if (!empty($thisfile_id3v2['TXXX'])) {
464
- // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames
465
- foreach ($thisfile_id3v2['TXXX'] as $txxx_array) {
466
- switch ($txxx_array['description']) {
467
- case 'replaygain_track_gain':
468
- if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) {
469
- $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
470
- }
471
- break;
472
- case 'replaygain_track_peak':
473
- if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) {
474
- $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']);
475
- }
476
- break;
477
- case 'replaygain_album_gain':
478
- if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) {
479
- $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data'])));
480
- }
481
- break;
482
- }
483
- }
484
- }
485
-
486
-
487
- // Set avdataoffset
488
- $info['avdataoffset'] = $thisfile_id3v2['headerlength'];
489
- if (isset($thisfile_id3v2['footer'])) {
490
- $info['avdataoffset'] += 10;
491
- }
492
-
493
- return true;
494
- }
495
-
496
-
497
- public function ParseID3v2GenreString($genrestring) {
498
- // Parse genres into arrays of genreName and genreID
499
- // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
500
- // ID3v2.4.x: '21' $00 'Eurodisco' $00
501
- $clean_genres = array();
502
- if (strpos($genrestring, "\x00") === false) {
503
- $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring);
504
- }
505
- $genre_elements = explode("\x00", $genrestring);
506
- foreach ($genre_elements as $element) {
507
- $element = trim($element);
508
- if ($element) {
509
- if (preg_match('#^[0-9]{1,3}#', $element)) {
510
- $clean_genres[] = getid3_id3v1::LookupGenreName($element);
511
- } else {
512
- $clean_genres[] = str_replace('((', '(', $element);
513
- }
514
- }
515
- }
516
- return $clean_genres;
517
- }
518
-
519
-
520
- public function ParseID3v2Frame(&$parsedFrame) {
521
-
522
- // shortcuts
523
- $info = &$this->getid3->info;
524
- $id3v2_majorversion = $info['id3v2']['majorversion'];
525
-
526
- $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
527
- if (empty($parsedFrame['framenamelong'])) {
528
- unset($parsedFrame['framenamelong']);
529
- }
530
- $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
531
- if (empty($parsedFrame['framenameshort'])) {
532
- unset($parsedFrame['framenameshort']);
533
- }
534
-
535
- if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
536
- if ($id3v2_majorversion == 3) {
537
- // Frame Header Flags
538
- // %abc00000 %ijk00000
539
- $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
540
- $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
541
- $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
542
- $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
543
- $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
544
- $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
545
-
546
- } elseif ($id3v2_majorversion == 4) {
547
- // Frame Header Flags
548
- // %0abc0000 %0h00kmnp
549
- $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
550
- $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
551
- $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
552
- $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
553
- $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
554
- $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
555
- $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
556
- $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
557
-
558
- // Frame-level de-unsynchronisation - ID3v2.4
559
- if ($parsedFrame['flags']['Unsynchronisation']) {
560
- $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
561
- }
562
-
563
- if ($parsedFrame['flags']['DataLengthIndicator']) {
564
- $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
565
- $parsedFrame['data'] = substr($parsedFrame['data'], 4);
566
- }
567
- }
568
-
569
- // Frame-level de-compression
570
- if ($parsedFrame['flags']['compression']) {
571
- $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
572
- if (!function_exists('gzuncompress')) {
573
- $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"';
574
- } else {
575
- if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
576
- //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
577
- $parsedFrame['data'] = $decompresseddata;
578
- unset($decompresseddata);
579
- } else {
580
- $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"';
581
- }
582
- }
583
- }
584
- }
585
-
586
- if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
587
- if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
588
- $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data';
589
- }
590
- }
591
-
592
- if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
593
-
594
- $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
595
- switch ($parsedFrame['frame_name']) {
596
- case 'WCOM':
597
- $warning .= ' (this is known to happen with files tagged by RioPort)';
598
- break;
599
-
600
- default:
601
- break;
602
- }
603
- $info['warning'][] = $warning;
604
-
605
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier
606
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier
607
- // There may be more than one 'UFID' frame in a tag,
608
- // but only one with the same 'Owner identifier'.
609
- // <Header for 'Unique file identifier', ID: 'UFID'>
610
- // Owner identifier <text string> $00
611
- // Identifier <up to 64 bytes binary data>
612
- $exploded = explode("\x00", $parsedFrame['data'], 2);
613
- $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : '');
614
- $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : '');
615
-
616
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
617
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame
618
- // There may be more than one 'TXXX' frame in each tag,
619
- // but only one with the same description.
620
- // <Header for 'User defined text information frame', ID: 'TXXX'>
621
- // Text encoding $xx
622
- // Description <text string according to encoding> $00 (00)
623
- // Value <text string according to encoding>
624
-
625
- $frame_offset = 0;
626
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
627
-
628
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
629
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
630
- }
631
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
632
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
633
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
634
- }
635
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
636
- if (ord($frame_description) === 0) {
637
- $frame_description = '';
638
- }
639
- $parsedFrame['encodingid'] = $frame_textencoding;
640
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
641
-
642
- $parsedFrame['description'] = $frame_description;
643
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
644
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
645
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
646
- }
647
- //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
648
-
649
-
650
- } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
651
- // There may only be one text information frame of its kind in an tag.
652
- // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
653
- // excluding 'TXXX' described in 4.2.6.>
654
- // Text encoding $xx
655
- // Information <text string(s) according to encoding>
656
-
657
- $frame_offset = 0;
658
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
659
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
660
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
661
- }
662
-
663
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
664
-
665
- $parsedFrame['encodingid'] = $frame_textencoding;
666
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
667
-
668
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
669
- // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
670
- // This of course breaks when an aritst name contains slash character, e.g. "AC/DC"
671
- // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense
672
- // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user
673
- switch ($parsedFrame['encoding']) {
674
- case 'UTF-16':
675
- case 'UTF-16BE':
676
- case 'UTF-16LE':
677
- $wordsize = 2;
678
- break;
679
- case 'ISO-8859-1':
680
- case 'UTF-8':
681
- default:
682
- $wordsize = 1;
683
- break;
684
- }
685
- $Txxx_elements = array();
686
- $Txxx_elements_start_offset = 0;
687
- for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) {
688
- if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) {
689
- $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
690
- $Txxx_elements_start_offset = $i + $wordsize;
691
- }
692
- }
693
- $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset);
694
- foreach ($Txxx_elements as $Txxx_element) {
695
- $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element);
696
- if (!empty($string)) {
697
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
698
- }
699
- }
700
- unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset);
701
- }
702
-
703
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
704
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame
705
- // There may be more than one 'WXXX' frame in each tag,
706
- // but only one with the same description
707
- // <Header for 'User defined URL link frame', ID: 'WXXX'>
708
- // Text encoding $xx
709
- // Description <text string according to encoding> $00 (00)
710
- // URL <text string>
711
-
712
- $frame_offset = 0;
713
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
714
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
715
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
716
- }
717
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
718
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
719
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
720
- }
721
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
722
-
723
- if (ord($frame_description) === 0) {
724
- $frame_description = '';
725
- }
726
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
727
-
728
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
729
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
730
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
731
- }
732
- if ($frame_terminatorpos) {
733
- // there are null bytes after the data - this is not according to spec
734
- // only use data up to first null byte
735
- $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
736
- } else {
737
- // no null bytes following data, just use all data
738
- $frame_urldata = (string) $parsedFrame['data'];
739
- }
740
-
741
- $parsedFrame['encodingid'] = $frame_textencoding;
742
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
743
-
744
- $parsedFrame['url'] = $frame_urldata;
745
- $parsedFrame['description'] = $frame_description;
746
- if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
747
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
748
- }
749
- unset($parsedFrame['data']);
750
-
751
-
752
- } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
753
- // There may only be one URL link frame of its kind in a tag,
754
- // except when stated otherwise in the frame description
755
- // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
756
- // described in 4.3.2.>
757
- // URL <text string>
758
-
759
- $parsedFrame['url'] = trim($parsedFrame['data']);
760
- if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
761
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
762
- }
763
- unset($parsedFrame['data']);
764
-
765
-
766
- } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only)
767
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only)
768
- // http://id3.org/id3v2.3.0#sec4.4
769
- // There may only be one 'IPL' frame in each tag
770
- // <Header for 'User defined URL link frame', ID: 'IPL'>
771
- // Text encoding $xx
772
- // People list strings <textstrings>
773
-
774
- $frame_offset = 0;
775
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
776
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
777
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
778
- }
779
- $parsedFrame['encodingid'] = $frame_textencoding;
780
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
781
- $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset);
782
-
783
- // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
784
- // "this tag typically contains null terminated strings, which are associated in pairs"
785
- // "there are users that use the tag incorrectly"
786
- $IPLS_parts = array();
787
- if (strpos($parsedFrame['data_raw'], "\x00") !== false) {
788
- $IPLS_parts_unsorted = array();
789
- if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) {
790
- // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding
791
- $thisILPS = '';
792
- for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) {
793
- $twobytes = substr($parsedFrame['data_raw'], $i, 2);
794
- if ($twobytes === "\x00\x00") {
795
- $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
796
- $thisILPS = '';
797
- } else {
798
- $thisILPS .= $twobytes;
799
- }
800
- }
801
- if (strlen($thisILPS) > 2) { // 2-byte BOM
802
- $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS);
803
- }
804
- } else {
805
- // ISO-8859-1 or UTF-8 or other single-byte-null character set
806
- $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']);
807
- }
808
- if (count($IPLS_parts_unsorted) == 1) {
809
- // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson"
810
- foreach ($IPLS_parts_unsorted as $key => $value) {
811
- $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value);
812
- $position = '';
813
- foreach ($IPLS_parts_sorted as $person) {
814
- $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
815
- }
816
- }
817
- } elseif ((count($IPLS_parts_unsorted) % 2) == 0) {
818
- $position = '';
819
- $person = '';
820
- foreach ($IPLS_parts_unsorted as $key => $value) {
821
- if (($key % 2) == 0) {
822
- $position = $value;
823
- } else {
824
- $person = $value;
825
- $IPLS_parts[] = array('position'=>$position, 'person'=>$person);
826
- $position = '';
827
- $person = '';
828
- }
829
- }
830
- } else {
831
- foreach ($IPLS_parts_unsorted as $key => $value) {
832
- $IPLS_parts[] = array($value);
833
- }
834
- }
835
-
836
- } else {
837
- $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']);
838
- }
839
- $parsedFrame['data'] = $IPLS_parts;
840
-
841
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
842
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
843
- }
844
-
845
-
846
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier
847
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier
848
- // There may only be one 'MCDI' frame in each tag
849
- // <Header for 'Music CD identifier', ID: 'MCDI'>
850
- // CD TOC <binary data>
851
-
852
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
853
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
854
- }
855
-
856
-
857
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes
858
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes
859
- // There may only be one 'ETCO' frame in each tag
860
- // <Header for 'Event timing codes', ID: 'ETCO'>
861
- // Time stamp format $xx
862
- // Where time stamp format is:
863
- // $01 (32-bit value) MPEG frames from beginning of file
864
- // $02 (32-bit value) milliseconds from beginning of file
865
- // Followed by a list of key events in the following format:
866
- // Type of event $xx
867
- // Time stamp $xx (xx ...)
868
- // The 'Time stamp' is set to zero if directly at the beginning of the sound
869
- // or after the previous event. All events MUST be sorted in chronological order.
870
-
871
- $frame_offset = 0;
872
- $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
873
-
874
- while ($frame_offset < strlen($parsedFrame['data'])) {
875
- $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
876
- $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
877
- $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
878
- $frame_offset += 4;
879
- }
880
- unset($parsedFrame['data']);
881
-
882
-
883
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table
884
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table
885
- // There may only be one 'MLLT' frame in each tag
886
- // <Header for 'Location lookup table', ID: 'MLLT'>
887
- // MPEG frames between reference $xx xx
888
- // Bytes between reference $xx xx xx
889
- // Milliseconds between reference $xx xx xx
890
- // Bits for bytes deviation $xx
891
- // Bits for milliseconds dev. $xx
892
- // Then for every reference the following data is included;
893
- // Deviation in bytes %xxx....
894
- // Deviation in milliseconds %xxx....
895
-
896
- $frame_offset = 0;
897
- $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
898
- $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
899
- $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
900
- $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
901
- $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
902
- $parsedFrame['data'] = substr($parsedFrame['data'], 10);
903
- while ($frame_offset < strlen($parsedFrame['data'])) {
904
- $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
905
- }
906
- $reference_counter = 0;
907
- while (strlen($deviationbitstream) > 0) {
908
- $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
909
- $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
910
- $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
911
- $reference_counter++;
912
- }
913
- unset($parsedFrame['data']);
914
-
915
-
916
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes
917
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes
918
- // There may only be one 'SYTC' frame in each tag
919
- // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
920
- // Time stamp format $xx
921
- // Tempo data <binary data>
922
- // Where time stamp format is:
923
- // $01 (32-bit value) MPEG frames from beginning of file
924
- // $02 (32-bit value) milliseconds from beginning of file
925
-
926
- $frame_offset = 0;
927
- $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
928
- $timestamp_counter = 0;
929
- while ($frame_offset < strlen($parsedFrame['data'])) {
930
- $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
931
- if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
932
- $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
933
- }
934
- $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
935
- $frame_offset += 4;
936
- $timestamp_counter++;
937
- }
938
- unset($parsedFrame['data']);
939
-
940
-
941
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription
942
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription
943
- // There may be more than one 'Unsynchronised lyrics/text transcription' frame
944
- // in each tag, but only one with the same language and content descriptor.
945
- // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
946
- // Text encoding $xx
947
- // Language $xx xx xx
948
- // Content descriptor <text string according to encoding> $00 (00)
949
- // Lyrics/text <full text string according to encoding>
950
-
951
- $frame_offset = 0;
952
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
953
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
954
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
955
- }
956
- $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
957
- $frame_offset += 3;
958
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
959
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
960
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
961
- }
962
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
963
- if (ord($frame_description) === 0) {
964
- $frame_description = '';
965
- }
966
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
967
-
968
- $parsedFrame['encodingid'] = $frame_textencoding;
969
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
970
-
971
- $parsedFrame['data'] = $parsedFrame['data'];
972
- $parsedFrame['language'] = $frame_language;
973
- $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
974
- $parsedFrame['description'] = $frame_description;
975
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
976
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
977
- }
978
- unset($parsedFrame['data']);
979
-
980
-
981
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text
982
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text
983
- // There may be more than one 'SYLT' frame in each tag,
984
- // but only one with the same language and content descriptor.
985
- // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
986
- // Text encoding $xx
987
- // Language $xx xx xx
988
- // Time stamp format $xx
989
- // $01 (32-bit value) MPEG frames from beginning of file
990
- // $02 (32-bit value) milliseconds from beginning of file
991
- // Content type $xx
992
- // Content descriptor <text string according to encoding> $00 (00)
993
- // Terminated text to be synced (typically a syllable)
994
- // Sync identifier (terminator to above string) $00 (00)
995
- // Time stamp $xx (xx ...)
996
-
997
- $frame_offset = 0;
998
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
999
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1000
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1001
- }
1002
- $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1003
- $frame_offset += 3;
1004
- $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1005
- $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1006
- $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
1007
- $parsedFrame['encodingid'] = $frame_textencoding;
1008
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1009
-
1010
- $parsedFrame['language'] = $frame_language;
1011
- $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1012
-
1013
- $timestampindex = 0;
1014
- $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
1015
- while (strlen($frame_remainingdata)) {
1016
- $frame_offset = 0;
1017
- $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
1018
- if ($frame_terminatorpos === false) {
1019
- $frame_remainingdata = '';
1020
- } else {
1021
- if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1022
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1023
- }
1024
- $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
1025
-
1026
- $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
1027
- if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
1028
- // timestamp probably omitted for first data item
1029
- } else {
1030
- $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
1031
- $frame_remainingdata = substr($frame_remainingdata, 4);
1032
- }
1033
- $timestampindex++;
1034
- }
1035
- }
1036
- unset($parsedFrame['data']);
1037
-
1038
-
1039
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments
1040
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments
1041
- // There may be more than one comment frame in each tag,
1042
- // but only one with the same language and content descriptor.
1043
- // <Header for 'Comment', ID: 'COMM'>
1044
- // Text encoding $xx
1045
- // Language $xx xx xx
1046
- // Short content descrip. <text string according to encoding> $00 (00)
1047
- // The actual text <full text string according to encoding>
1048
-
1049
- if (strlen($parsedFrame['data']) < 5) {
1050
-
1051
- $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset'];
1052
-
1053
- } else {
1054
-
1055
- $frame_offset = 0;
1056
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1057
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1058
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1059
- }
1060
- $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1061
- $frame_offset += 3;
1062
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1063
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1064
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1065
- }
1066
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1067
- if (ord($frame_description) === 0) {
1068
- $frame_description = '';
1069
- }
1070
- $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
1071
-
1072
- $parsedFrame['encodingid'] = $frame_textencoding;
1073
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1074
-
1075
- $parsedFrame['language'] = $frame_language;
1076
- $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1077
- $parsedFrame['description'] = $frame_description;
1078
- $parsedFrame['data'] = $frame_text;
1079
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1080
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1081
- }
1082
-
1083
- }
1084
-
1085
- } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
1086
- // There may be more than one 'RVA2' frame in each tag,
1087
- // but only one with the same identification string
1088
- // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
1089
- // Identification <text string> $00
1090
- // The 'identification' string is used to identify the situation and/or
1091
- // device where this adjustment should apply. The following is then
1092
- // repeated for every channel:
1093
- // Type of channel $xx
1094
- // Volume adjustment $xx xx
1095
- // Bits representing peak $xx
1096
- // Peak volume $xx (xx ...)
1097
-
1098
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
1099
- $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
1100
- if (ord($frame_idstring) === 0) {
1101
- $frame_idstring = '';
1102
- }
1103
- $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1104
- $parsedFrame['description'] = $frame_idstring;
1105
- $RVA2channelcounter = 0;
1106
- while (strlen($frame_remainingdata) >= 5) {
1107
- $frame_offset = 0;
1108
- $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
1109
- $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
1110
- $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
1111
- $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
1112
- $frame_offset += 2;
1113
- $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
1114
- if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) {
1115
- $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value';
1116
- break;
1117
- }
1118
- $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
1119
- $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
1120
- $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
1121
- $RVA2channelcounter++;
1122
- }
1123
- unset($parsedFrame['data']);
1124
-
1125
-
1126
- } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
1127
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only)
1128
- // There may only be one 'RVA' frame in each tag
1129
- // <Header for 'Relative volume adjustment', ID: 'RVA'>
1130
- // ID3v2.2 => Increment/decrement %000000ba
1131
- // ID3v2.3 => Increment/decrement %00fedcba
1132
- // Bits used for volume descr. $xx
1133
- // Relative volume change, right $xx xx (xx ...) // a
1134
- // Relative volume change, left $xx xx (xx ...) // b
1135
- // Peak volume right $xx xx (xx ...)
1136
- // Peak volume left $xx xx (xx ...)
1137
- // ID3v2.3 only, optional (not present in ID3v2.2):
1138
- // Relative volume change, right back $xx xx (xx ...) // c
1139
- // Relative volume change, left back $xx xx (xx ...) // d
1140
- // Peak volume right back $xx xx (xx ...)
1141
- // Peak volume left back $xx xx (xx ...)
1142
- // ID3v2.3 only, optional (not present in ID3v2.2):
1143
- // Relative volume change, center $xx xx (xx ...) // e
1144
- // Peak volume center $xx xx (xx ...)
1145
- // ID3v2.3 only, optional (not present in ID3v2.2):
1146
- // Relative volume change, bass $xx xx (xx ...) // f
1147
- // Peak volume bass $xx xx (xx ...)
1148
-
1149
- $frame_offset = 0;
1150
- $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1151
- $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
1152
- $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
1153
- $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1154
- $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
1155
- $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1156
- if ($parsedFrame['incdec']['right'] === false) {
1157
- $parsedFrame['volumechange']['right'] *= -1;
1158
- }
1159
- $frame_offset += $frame_bytesvolume;
1160
- $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1161
- if ($parsedFrame['incdec']['left'] === false) {
1162
- $parsedFrame['volumechange']['left'] *= -1;
1163
- }
1164
- $frame_offset += $frame_bytesvolume;
1165
- $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1166
- $frame_offset += $frame_bytesvolume;
1167
- $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1168
- $frame_offset += $frame_bytesvolume;
1169
- if ($id3v2_majorversion == 3) {
1170
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1171
- if (strlen($parsedFrame['data']) > 0) {
1172
- $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
1173
- $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
1174
- $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1175
- if ($parsedFrame['incdec']['rightrear'] === false) {
1176
- $parsedFrame['volumechange']['rightrear'] *= -1;
1177
- }
1178
- $frame_offset += $frame_bytesvolume;
1179
- $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1180
- if ($parsedFrame['incdec']['leftrear'] === false) {
1181
- $parsedFrame['volumechange']['leftrear'] *= -1;
1182
- }
1183
- $frame_offset += $frame_bytesvolume;
1184
- $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1185
- $frame_offset += $frame_bytesvolume;
1186
- $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1187
- $frame_offset += $frame_bytesvolume;
1188
- }
1189
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1190
- if (strlen($parsedFrame['data']) > 0) {
1191
- $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
1192
- $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1193
- if ($parsedFrame['incdec']['center'] === false) {
1194
- $parsedFrame['volumechange']['center'] *= -1;
1195
- }
1196
- $frame_offset += $frame_bytesvolume;
1197
- $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1198
- $frame_offset += $frame_bytesvolume;
1199
- }
1200
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
1201
- if (strlen($parsedFrame['data']) > 0) {
1202
- $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
1203
- $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1204
- if ($parsedFrame['incdec']['bass'] === false) {
1205
- $parsedFrame['volumechange']['bass'] *= -1;
1206
- }
1207
- $frame_offset += $frame_bytesvolume;
1208
- $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
1209
- $frame_offset += $frame_bytesvolume;
1210
- }
1211
- }
1212
- unset($parsedFrame['data']);
1213
-
1214
-
1215
- } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
1216
- // There may be more than one 'EQU2' frame in each tag,
1217
- // but only one with the same identification string
1218
- // <Header of 'Equalisation (2)', ID: 'EQU2'>
1219
- // Interpolation method $xx
1220
- // $00 Band
1221
- // $01 Linear
1222
- // Identification <text string> $00
1223
- // The following is then repeated for every adjustment point
1224
- // Frequency $xx xx
1225
- // Volume adjustment $xx xx
1226
-
1227
- $frame_offset = 0;
1228
- $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1229
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1230
- $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1231
- if (ord($frame_idstring) === 0) {
1232
- $frame_idstring = '';
1233
- }
1234
- $parsedFrame['description'] = $frame_idstring;
1235
- $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
1236
- while (strlen($frame_remainingdata)) {
1237
- $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
1238
- $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
1239
- $frame_remainingdata = substr($frame_remainingdata, 4);
1240
- }
1241
- $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
1242
- unset($parsedFrame['data']);
1243
-
1244
-
1245
- } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only)
1246
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only)
1247
- // There may only be one 'EQUA' frame in each tag
1248
- // <Header for 'Relative volume adjustment', ID: 'EQU'>
1249
- // Adjustment bits $xx
1250
- // This is followed by 2 bytes + ('adjustment bits' rounded up to the
1251
- // nearest byte) for every equalisation band in the following format,
1252
- // giving a frequency range of 0 - 32767Hz:
1253
- // Increment/decrement %x (MSB of the Frequency)
1254
- // Frequency (lower 15 bits)
1255
- // Adjustment $xx (xx ...)
1256
-
1257
- $frame_offset = 0;
1258
- $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
1259
- $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
1260
-
1261
- $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
1262
- while (strlen($frame_remainingdata) > 0) {
1263
- $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
1264
- $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
1265
- $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
1266
- $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
1267
- $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
1268
- if ($parsedFrame[$frame_frequency]['incdec'] === false) {
1269
- $parsedFrame[$frame_frequency]['adjustment'] *= -1;
1270
- }
1271
- $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
1272
- }
1273
- unset($parsedFrame['data']);
1274
-
1275
-
1276
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb
1277
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb
1278
- // There may only be one 'RVRB' frame in each tag.
1279
- // <Header for 'Reverb', ID: 'RVRB'>
1280
- // Reverb left (ms) $xx xx
1281
- // Reverb right (ms) $xx xx
1282
- // Reverb bounces, left $xx
1283
- // Reverb bounces, right $xx
1284
- // Reverb feedback, left to left $xx
1285
- // Reverb feedback, left to right $xx
1286
- // Reverb feedback, right to right $xx
1287
- // Reverb feedback, right to left $xx
1288
- // Premix left to right $xx
1289
- // Premix right to left $xx
1290
-
1291
- $frame_offset = 0;
1292
- $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1293
- $frame_offset += 2;
1294
- $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1295
- $frame_offset += 2;
1296
- $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1297
- $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1298
- $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1299
- $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1300
- $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1301
- $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1302
- $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1303
- $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1304
- unset($parsedFrame['data']);
1305
-
1306
-
1307
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture
1308
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture
1309
- // There may be several pictures attached to one file,
1310
- // each in their individual 'APIC' frame, but only one
1311
- // with the same content descriptor
1312
- // <Header for 'Attached picture', ID: 'APIC'>
1313
- // Text encoding $xx
1314
- // ID3v2.3+ => MIME type <text string> $00
1315
- // ID3v2.2 => Image format $xx xx xx
1316
- // Picture type $xx
1317
- // Description <text string according to encoding> $00 (00)
1318
- // Picture data <binary data>
1319
-
1320
- $frame_offset = 0;
1321
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1322
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1323
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1324
- }
1325
-
1326
- if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
1327
- $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
1328
- if (strtolower($frame_imagetype) == 'ima') {
1329
- // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
1330
- // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoff�pacbell*net)
1331
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1332
- $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1333
- if (ord($frame_mimetype) === 0) {
1334
- $frame_mimetype = '';
1335
- }
1336
- $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
1337
- if ($frame_imagetype == 'JPEG') {
1338
- $frame_imagetype = 'JPG';
1339
- }
1340
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1341
- } else {
1342
- $frame_offset += 3;
1343
- }
1344
- }
1345
- if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
1346
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1347
- $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1348
- if (ord($frame_mimetype) === 0) {
1349
- $frame_mimetype = '';
1350
- }
1351
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1352
- }
1353
-
1354
- $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1355
-
1356
- if ($frame_offset >= $parsedFrame['datalength']) {
1357
- $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset);
1358
- } else {
1359
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1360
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1361
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1362
- }
1363
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1364
- if (ord($frame_description) === 0) {
1365
- $frame_description = '';
1366
- }
1367
- $parsedFrame['encodingid'] = $frame_textencoding;
1368
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1369
-
1370
- if ($id3v2_majorversion == 2) {
1371
- $parsedFrame['imagetype'] = $frame_imagetype;
1372
- } else {
1373
- $parsedFrame['mime'] = $frame_mimetype;
1374
- }
1375
- $parsedFrame['picturetypeid'] = $frame_picturetype;
1376
- $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
1377
- $parsedFrame['description'] = $frame_description;
1378
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
1379
- $parsedFrame['datalength'] = strlen($parsedFrame['data']);
1380
-
1381
- $parsedFrame['image_mime'] = '';
1382
- $imageinfo = array();
1383
- $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo);
1384
- if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
1385
- $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
1386
- if ($imagechunkcheck[0]) {
1387
- $parsedFrame['image_width'] = $imagechunkcheck[0];
1388
- }
1389
- if ($imagechunkcheck[1]) {
1390
- $parsedFrame['image_height'] = $imagechunkcheck[1];
1391
- }
1392
- }
1393
-
1394
- do {
1395
- if ($this->getid3->option_save_attachments === false) {
1396
- // skip entirely
1397
- unset($parsedFrame['data']);
1398
- break;
1399
- }
1400
- if ($this->getid3->option_save_attachments === true) {
1401
- // great
1402
- /*
1403
- } elseif (is_int($this->getid3->option_save_attachments)) {
1404
- if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) {
1405
- // too big, skip
1406
- $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)';
1407
- unset($parsedFrame['data']);
1408
- break;
1409
- }
1410
- */
1411
- } elseif (is_string($this->getid3->option_save_attachments)) {
1412
- $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
1413
- if (!is_dir($dir) || !is_writable($dir)) {
1414
- // cannot write, skip
1415
- $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)';
1416
- unset($parsedFrame['data']);
1417
- break;
1418
- }
1419
- }
1420
- // if we get this far, must be OK
1421
- if (is_string($this->getid3->option_save_attachments)) {
1422
- $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset;
1423
- if (!file_exists($destination_filename) || is_writable($destination_filename)) {
1424
- file_put_contents($destination_filename, $parsedFrame['data']);
1425
- } else {
1426
- $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)';
1427
- }
1428
- $parsedFrame['data_filename'] = $destination_filename;
1429
- unset($parsedFrame['data']);
1430
- } else {
1431
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1432
- if (!isset($info['id3v2']['comments']['picture'])) {
1433
- $info['id3v2']['comments']['picture'] = array();
1434
- }
1435
- $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']);
1436
- }
1437
- }
1438
- } while (false);
1439
- }
1440
-
1441
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object
1442
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object
1443
- // There may be more than one 'GEOB' frame in each tag,
1444
- // but only one with the same content descriptor
1445
- // <Header for 'General encapsulated object', ID: 'GEOB'>
1446
- // Text encoding $xx
1447
- // MIME type <text string> $00
1448
- // Filename <text string according to encoding> $00 (00)
1449
- // Content description <text string according to encoding> $00 (00)
1450
- // Encapsulated object <binary data>
1451
-
1452
- $frame_offset = 0;
1453
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1454
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1455
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1456
- }
1457
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1458
- $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1459
- if (ord($frame_mimetype) === 0) {
1460
- $frame_mimetype = '';
1461
- }
1462
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1463
-
1464
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1465
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1466
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1467
- }
1468
- $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1469
- if (ord($frame_filename) === 0) {
1470
- $frame_filename = '';
1471
- }
1472
- $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
1473
-
1474
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1475
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1476
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1477
- }
1478
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1479
- if (ord($frame_description) === 0) {
1480
- $frame_description = '';
1481
- }
1482
- $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
1483
-
1484
- $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
1485
- $parsedFrame['encodingid'] = $frame_textencoding;
1486
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1487
-
1488
- $parsedFrame['mime'] = $frame_mimetype;
1489
- $parsedFrame['filename'] = $frame_filename;
1490
- $parsedFrame['description'] = $frame_description;
1491
- unset($parsedFrame['data']);
1492
-
1493
-
1494
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter
1495
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter
1496
- // There may only be one 'PCNT' frame in each tag.
1497
- // When the counter reaches all one's, one byte is inserted in
1498
- // front of the counter thus making the counter eight bits bigger
1499
- // <Header for 'Play counter', ID: 'PCNT'>
1500
- // Counter $xx xx xx xx (xx ...)
1501
-
1502
- $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']);
1503
-
1504
-
1505
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter
1506
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter
1507
- // There may be more than one 'POPM' frame in each tag,
1508
- // but only one with the same email address
1509
- // <Header for 'Popularimeter', ID: 'POPM'>
1510
- // Email to user <text string> $00
1511
- // Rating $xx
1512
- // Counter $xx xx xx xx (xx ...)
1513
-
1514
- $frame_offset = 0;
1515
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1516
- $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1517
- if (ord($frame_emailaddress) === 0) {
1518
- $frame_emailaddress = '';
1519
- }
1520
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1521
- $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1522
- $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1523
- $parsedFrame['email'] = $frame_emailaddress;
1524
- $parsedFrame['rating'] = $frame_rating;
1525
- unset($parsedFrame['data']);
1526
-
1527
-
1528
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size
1529
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size
1530
- // There may only be one 'RBUF' frame in each tag
1531
- // <Header for 'Recommended buffer size', ID: 'RBUF'>
1532
- // Buffer size $xx xx xx
1533
- // Embedded info flag %0000000x
1534
- // Offset to next tag $xx xx xx xx
1535
-
1536
- $frame_offset = 0;
1537
- $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
1538
- $frame_offset += 3;
1539
-
1540
- $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
1541
- $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
1542
- $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1543
- unset($parsedFrame['data']);
1544
-
1545
-
1546
- } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only)
1547
- // There may be more than one 'CRM' frame in a tag,
1548
- // but only one with the same 'owner identifier'
1549
- // <Header for 'Encrypted meta frame', ID: 'CRM'>
1550
- // Owner identifier <textstring> $00 (00)
1551
- // Content/explanation <textstring> $00 (00)
1552
- // Encrypted datablock <binary data>
1553
-
1554
- $frame_offset = 0;
1555
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1556
- $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1557
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1558
-
1559
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1560
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1561
- if (ord($frame_description) === 0) {
1562
- $frame_description = '';
1563
- }
1564
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1565
-
1566
- $parsedFrame['ownerid'] = $frame_ownerid;
1567
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1568
- $parsedFrame['description'] = $frame_description;
1569
- unset($parsedFrame['data']);
1570
-
1571
-
1572
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption
1573
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption
1574
- // There may be more than one 'AENC' frames in a tag,
1575
- // but only one with the same 'Owner identifier'
1576
- // <Header for 'Audio encryption', ID: 'AENC'>
1577
- // Owner identifier <text string> $00
1578
- // Preview start $xx xx
1579
- // Preview length $xx xx
1580
- // Encryption info <binary data>
1581
-
1582
- $frame_offset = 0;
1583
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1584
- $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1585
- if (ord($frame_ownerid) === 0) {
1586
- $frame_ownerid == '';
1587
- }
1588
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1589
- $parsedFrame['ownerid'] = $frame_ownerid;
1590
- $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1591
- $frame_offset += 2;
1592
- $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1593
- $frame_offset += 2;
1594
- $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
1595
- unset($parsedFrame['data']);
1596
-
1597
-
1598
- } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
1599
- (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
1600
- // There may be more than one 'LINK' frame in a tag,
1601
- // but only one with the same contents
1602
- // <Header for 'Linked information', ID: 'LINK'>
1603
- // ID3v2.3+ => Frame identifier $xx xx xx xx
1604
- // ID3v2.2 => Frame identifier $xx xx xx
1605
- // URL <text string> $00
1606
- // ID and additional data <text string(s)>
1607
-
1608
- $frame_offset = 0;
1609
- if ($id3v2_majorversion == 2) {
1610
- $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
1611
- $frame_offset += 3;
1612
- } else {
1613
- $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
1614
- $frame_offset += 4;
1615
- }
1616
-
1617
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1618
- $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1619
- if (ord($frame_url) === 0) {
1620
- $frame_url = '';
1621
- }
1622
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1623
- $parsedFrame['url'] = $frame_url;
1624
-
1625
- $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
1626
- if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
1627
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
1628
- }
1629
- unset($parsedFrame['data']);
1630
-
1631
-
1632
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
1633
- // There may only be one 'POSS' frame in each tag
1634
- // <Head for 'Position synchronisation', ID: 'POSS'>
1635
- // Time stamp format $xx
1636
- // Position $xx (xx ...)
1637
-
1638
- $frame_offset = 0;
1639
- $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1640
- $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
1641
- unset($parsedFrame['data']);
1642
-
1643
-
1644
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only)
1645
- // There may be more than one 'Terms of use' frame in a tag,
1646
- // but only one with the same 'Language'
1647
- // <Header for 'Terms of use frame', ID: 'USER'>
1648
- // Text encoding $xx
1649
- // Language $xx xx xx
1650
- // The actual text <text string according to encoding>
1651
-
1652
- $frame_offset = 0;
1653
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1654
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1655
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1656
- }
1657
- $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
1658
- $frame_offset += 3;
1659
- $parsedFrame['language'] = $frame_language;
1660
- $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
1661
- $parsedFrame['encodingid'] = $frame_textencoding;
1662
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1663
-
1664
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1665
- if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
1666
- $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
1667
- }
1668
- unset($parsedFrame['data']);
1669
-
1670
-
1671
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only)
1672
- // There may only be one 'OWNE' frame in a tag
1673
- // <Header for 'Ownership frame', ID: 'OWNE'>
1674
- // Text encoding $xx
1675
- // Price paid <text string> $00
1676
- // Date of purch. <text string>
1677
- // Seller <text string according to encoding>
1678
-
1679
- $frame_offset = 0;
1680
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1681
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1682
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1683
- }
1684
- $parsedFrame['encodingid'] = $frame_textencoding;
1685
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1686
-
1687
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1688
- $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1689
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1690
-
1691
- $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
1692
- $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
1693
- $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
1694
-
1695
- $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
1696
- if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
1697
- $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
1698
- }
1699
- $frame_offset += 8;
1700
-
1701
- $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
1702
- unset($parsedFrame['data']);
1703
-
1704
-
1705
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only)
1706
- // There may be more than one 'commercial frame' in a tag,
1707
- // but no two may be identical
1708
- // <Header for 'Commercial frame', ID: 'COMR'>
1709
- // Text encoding $xx
1710
- // Price string <text string> $00
1711
- // Valid until <text string>
1712
- // Contact URL <text string> $00
1713
- // Received as $xx
1714
- // Name of seller <text string according to encoding> $00 (00)
1715
- // Description <text string according to encoding> $00 (00)
1716
- // Picture MIME type <string> $00
1717
- // Seller logo <binary data>
1718
-
1719
- $frame_offset = 0;
1720
- $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1721
- if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
1722
- $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
1723
- }
1724
-
1725
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1726
- $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1727
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1728
- $frame_rawpricearray = explode('/', $frame_pricestring);
1729
- foreach ($frame_rawpricearray as $key => $val) {
1730
- $frame_currencyid = substr($val, 0, 3);
1731
- $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
1732
- $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
1733
- }
1734
-
1735
- $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
1736
- $frame_offset += 8;
1737
-
1738
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1739
- $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1740
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1741
-
1742
- $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1743
-
1744
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1745
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1746
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1747
- }
1748
- $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1749
- if (ord($frame_sellername) === 0) {
1750
- $frame_sellername = '';
1751
- }
1752
- $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
1753
-
1754
- $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
1755
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
1756
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
1757
- }
1758
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1759
- if (ord($frame_description) === 0) {
1760
- $frame_description = '';
1761
- }
1762
- $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
1763
-
1764
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1765
- $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1766
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1767
-
1768
- $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
1769
-
1770
- $parsedFrame['encodingid'] = $frame_textencoding;
1771
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
1772
-
1773
- $parsedFrame['pricevaliduntil'] = $frame_datestring;
1774
- $parsedFrame['contacturl'] = $frame_contacturl;
1775
- $parsedFrame['receivedasid'] = $frame_receivedasid;
1776
- $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
1777
- $parsedFrame['sellername'] = $frame_sellername;
1778
- $parsedFrame['description'] = $frame_description;
1779
- $parsedFrame['mime'] = $frame_mimetype;
1780
- $parsedFrame['logo'] = $frame_sellerlogo;
1781
- unset($parsedFrame['data']);
1782
-
1783
-
1784
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
1785
- // There may be several 'ENCR' frames in a tag,
1786
- // but only one containing the same symbol
1787
- // and only one containing the same owner identifier
1788
- // <Header for 'Encryption method registration', ID: 'ENCR'>
1789
- // Owner identifier <text string> $00
1790
- // Method symbol $xx
1791
- // Encryption data <binary data>
1792
-
1793
- $frame_offset = 0;
1794
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1795
- $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1796
- if (ord($frame_ownerid) === 0) {
1797
- $frame_ownerid = '';
1798
- }
1799
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1800
-
1801
- $parsedFrame['ownerid'] = $frame_ownerid;
1802
- $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1803
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1804
-
1805
-
1806
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only)
1807
-
1808
- // There may be several 'GRID' frames in a tag,
1809
- // but only one containing the same symbol
1810
- // and only one containing the same owner identifier
1811
- // <Header for 'Group ID registration', ID: 'GRID'>
1812
- // Owner identifier <text string> $00
1813
- // Group symbol $xx
1814
- // Group dependent data <binary data>
1815
-
1816
- $frame_offset = 0;
1817
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1818
- $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1819
- if (ord($frame_ownerid) === 0) {
1820
- $frame_ownerid = '';
1821
- }
1822
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1823
-
1824
- $parsedFrame['ownerid'] = $frame_ownerid;
1825
- $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1826
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1827
-
1828
-
1829
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only)
1830
- // The tag may contain more than one 'PRIV' frame
1831
- // but only with different contents
1832
- // <Header for 'Private frame', ID: 'PRIV'>
1833
- // Owner identifier <text string> $00
1834
- // The private data <binary data>
1835
-
1836
- $frame_offset = 0;
1837
- $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
1838
- $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
1839
- if (ord($frame_ownerid) === 0) {
1840
- $frame_ownerid = '';
1841
- }
1842
- $frame_offset = $frame_terminatorpos + strlen("\x00");
1843
-
1844
- $parsedFrame['ownerid'] = $frame_ownerid;
1845
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1846
-
1847
-
1848
- } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only)
1849
- // There may be more than one 'signature frame' in a tag,
1850
- // but no two may be identical
1851
- // <Header for 'Signature frame', ID: 'SIGN'>
1852
- // Group symbol $xx
1853
- // Signature <binary data>
1854
-
1855
- $frame_offset = 0;
1856
- $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1857
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
1858
-
1859
-
1860
- } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only)
1861
- // There may only be one 'seek frame' in a tag
1862
- // <Header for 'Seek frame', ID: 'SEEK'>
1863
- // Minimum offset to next tag $xx xx xx xx
1864
-
1865
- $frame_offset = 0;
1866
- $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1867
-
1868
-
1869
- } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only)
1870
- // There may only be one 'audio seek point index' frame in a tag
1871
- // <Header for 'Seek Point Index', ID: 'ASPI'>
1872
- // Indexed data start (S) $xx xx xx xx
1873
- // Indexed data length (L) $xx xx xx xx
1874
- // Number of index points (N) $xx xx
1875
- // Bits per index point (b) $xx
1876
- // Then for every index point the following data is included:
1877
- // Fraction at index (Fi) $xx (xx)
1878
-
1879
- $frame_offset = 0;
1880
- $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1881
- $frame_offset += 4;
1882
- $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
1883
- $frame_offset += 4;
1884
- $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
1885
- $frame_offset += 2;
1886
- $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
1887
- $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
1888
- for ($i = 0; $i < $frame_indexpoints; $i++) {
1889
- $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
1890
- $frame_offset += $frame_bytesperpoint;
1891
- }
1892
- unset($parsedFrame['data']);
1893
-
1894
- } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
1895
- // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
1896
- // There may only be one 'RGAD' frame in a tag
1897
- // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
1898
- // Peak Amplitude $xx $xx $xx $xx
1899
- // Radio Replay Gain Adjustment %aaabbbcd %dddddddd
1900
- // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd
1901
- // a - name code
1902
- // b - originator code
1903
- // c - sign bit
1904
- // d - replay gain adjustment
1905
-
1906
- $frame_offset = 0;
1907
- $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
1908
- $frame_offset += 4;
1909
- $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1910
- $frame_offset += 2;
1911
- $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
1912
- $frame_offset += 2;
1913
- $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
1914
- $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
1915
- $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
1916
- $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
1917
- $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
1918
- $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
1919
- $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
1920
- $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
1921
- $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
1922
- $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
1923
- $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
1924
- $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
1925
- $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
1926
- $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
1927
-
1928
- $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
1929
- $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
1930
- $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
1931
- $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
1932
- $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
1933
-
1934
- unset($parsedFrame['data']);
1935
-
1936
- }
1937
-
1938
- return true;
1939
- }
1940
-
1941
-
1942
- public function DeUnsynchronise($data) {
1943
- return str_replace("\xFF\x00", "\xFF", $data);
1944
- }
1945
-
1946
- public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
1947
- static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
1948
- 0x00 => 'No more than 128 frames and 1 MB total tag size',
1949
- 0x01 => 'No more than 64 frames and 128 KB total tag size',
1950
- 0x02 => 'No more than 32 frames and 40 KB total tag size',
1951
- 0x03 => 'No more than 32 frames and 4 KB total tag size',
1952
- );
1953
- return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : '');
1954
- }
1955
-
1956
- public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
1957
- static $LookupExtendedHeaderRestrictionsTextEncodings = array(
1958
- 0x00 => 'No restrictions',
1959
- 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8',
1960
- );
1961
- return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : '');
1962
- }
1963
-
1964
- public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
1965
- static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
1966
- 0x00 => 'No restrictions',
1967
- 0x01 => 'No string is longer than 1024 characters',
1968
- 0x02 => 'No string is longer than 128 characters',
1969
- 0x03 => 'No string is longer than 30 characters',
1970
- );
1971
- return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : '');
1972
- }
1973
-
1974
- public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
1975
- static $LookupExtendedHeaderRestrictionsImageEncoding = array(
1976
- 0x00 => 'No restrictions',
1977
- 0x01 => 'Images are encoded only with PNG or JPEG',
1978
- );
1979
- return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : '');
1980
- }
1981
-
1982
- public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
1983
- static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
1984
- 0x00 => 'No restrictions',
1985
- 0x01 => 'All images are 256x256 pixels or smaller',
1986
- 0x02 => 'All images are 64x64 pixels or smaller',
1987
- 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise',
1988
- );
1989
- return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : '');
1990
- }
1991
-
1992
- public function LookupCurrencyUnits($currencyid) {
1993
-
1994
- $begin = __LINE__;
1995
-
1996
- /** This is not a comment!
1997
-
1998
-
1999
- AED Dirhams
2000
- AFA Afghanis
2001
- ALL Leke
2002
- AMD Drams
2003
- ANG Guilders
2004
- AOA Kwanza
2005
- ARS Pesos
2006
- ATS Schillings
2007
- AUD Dollars
2008
- AWG Guilders
2009
- AZM Manats
2010
- BAM Convertible Marka
2011
- BBD Dollars
2012
- BDT Taka
2013
- BEF Francs
2014
- BGL Leva
2015
- BHD Dinars
2016
- BIF Francs
2017
- BMD Dollars
2018
- BND Dollars
2019
- BOB Bolivianos
2020
- BRL Brazil Real
2021
- BSD Dollars
2022
- BTN Ngultrum
2023
- BWP Pulas
2024
- BYR Rubles
2025
- BZD Dollars
2026
- CAD Dollars
2027
- CDF Congolese Francs
2028
- CHF Francs
2029
- CLP Pesos
2030
- CNY Yuan Renminbi
2031
- COP Pesos
2032
- CRC Colones
2033
- CUP Pesos
2034
- CVE Escudos
2035
- CYP Pounds
2036
- CZK Koruny
2037
- DEM Deutsche Marks
2038
- DJF Francs
2039
- DKK Kroner
2040
- DOP Pesos
2041
- DZD Algeria Dinars
2042
- EEK Krooni
2043
- EGP Pounds
2044
- ERN Nakfa
2045
- ESP Pesetas
2046
- ETB Birr
2047
- EUR Euro
2048
- FIM Markkaa
2049
- FJD Dollars
2050
- FKP Pounds
2051
- FRF Francs
2052
- GBP Pounds
2053
- GEL Lari
2054
- GGP Pounds
2055
- GHC Cedis
2056
- GIP Pounds
2057
- GMD Dalasi
2058
- GNF Francs
2059
- GRD Drachmae
2060
- GTQ Quetzales
2061
- GYD Dollars
2062
- HKD Dollars
2063
- HNL Lempiras
2064
- HRK Kuna
2065
- HTG Gourdes
2066
- HUF Forints
2067
- IDR Rupiahs
2068
- IEP Pounds
2069
- ILS New Shekels
2070
- IMP Pounds
2071
- INR Rupees
2072
- IQD Dinars
2073
- IRR Rials
2074
- ISK Kronur
2075
- ITL Lire
2076
- JEP Pounds
2077
- JMD Dollars
2078
- JOD Dinars
2079
- JPY Yen
2080
- KES Shillings
2081
- KGS Soms
2082
- KHR Riels
2083
- KMF Francs
2084
- KPW Won
2085
- KWD Dinars
2086
- KYD Dollars
2087
- KZT Tenge
2088
- LAK Kips
2089
- LBP Pounds
2090
- LKR Rupees
2091
- LRD Dollars
2092
- LSL Maloti
2093
- LTL Litai
2094
- LUF Francs
2095
- LVL Lati
2096
- LYD Dinars
2097
- MAD Dirhams
2098
- MDL Lei
2099
- MGF Malagasy Francs
2100
- MKD Denars
2101
- MMK Kyats
2102
- MNT Tugriks
2103
- MOP Patacas
2104
- MRO Ouguiyas
2105
- MTL Liri
2106
- MUR Rupees
2107
- MVR Rufiyaa
2108
- MWK Kwachas
2109
- MXN Pesos
2110
- MYR Ringgits
2111
- MZM Meticais
2112
- NAD Dollars
2113
- NGN Nairas
2114
- NIO Gold Cordobas
2115
- NLG Guilders
2116
- NOK Krone
2117
- NPR Nepal Rupees
2118
- NZD Dollars
2119
- OMR Rials
2120
- PAB Balboa
2121
- PEN Nuevos Soles
2122
- PGK Kina
2123
- PHP Pesos
2124
- PKR Rupees
2125
- PLN Zlotych
2126
- PTE Escudos
2127
- PYG Guarani
2128
- QAR Rials
2129
- ROL Lei
2130
- RUR Rubles
2131
- RWF Rwanda Francs
2132
- SAR Riyals
2133
- SBD Dollars
2134
- SCR Rupees
2135
- SDD Dinars
2136
- SEK Kronor
2137
- SGD Dollars
2138
- SHP Pounds
2139
- SIT Tolars
2140
- SKK Koruny
2141
- SLL Leones
2142
- SOS Shillings
2143
- SPL Luigini
2144
- SRG Guilders
2145
- STD Dobras
2146
- SVC Colones
2147
- SYP Pounds
2148
- SZL Emalangeni
2149
- THB Baht
2150
- TJR Rubles
2151
- TMM Manats
2152
- TND Dinars
2153
- TOP Pa'anga
2154
- TRL Liras
2155
- TTD Dollars
2156
- TVD Tuvalu Dollars
2157
- TWD New Dollars
2158
- TZS Shillings
2159
- UAH Hryvnia
2160
- UGX Shillings
2161
- USD Dollars
2162
- UYU Pesos
2163
- UZS Sums
2164
- VAL Lire
2165
- VEB Bolivares
2166
- VND Dong
2167
- VUV Vatu
2168
- WST Tala
2169
- XAF Francs
2170
- XAG Ounces
2171
- XAU Ounces
2172
- XCD Dollars
2173
- XDR Special Drawing Rights
2174
- XPD Ounces
2175
- XPF Francs
2176
- XPT Ounces
2177
- YER Rials
2178
- YUM New Dinars
2179
- ZAR Rand
2180
- ZMK Kwacha
2181
- ZWD Zimbabwe Dollars
2182
-
2183
- */
2184
-
2185
- return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
2186
- }
2187
-
2188
-
2189
- public function LookupCurrencyCountry($currencyid) {
2190
-
2191
- $begin = __LINE__;
2192
-
2193
- /** This is not a comment!
2194
-
2195
- AED United Arab Emirates
2196
- AFA Afghanistan
2197
- ALL Albania
2198
- AMD Armenia
2199
- ANG Netherlands Antilles
2200
- AOA Angola
2201
- ARS Argentina
2202
- ATS Austria
2203
- AUD Australia
2204
- AWG Aruba
2205
- AZM Azerbaijan
2206
- BAM Bosnia and Herzegovina
2207
- BBD Barbados
2208
- BDT Bangladesh
2209
- BEF Belgium
2210
- BGL Bulgaria
2211
- BHD Bahrain
2212
- BIF Burundi
2213
- BMD Bermuda
2214
- BND Brunei Darussalam
2215
- BOB Bolivia
2216
- BRL Brazil
2217
- BSD Bahamas
2218
- BTN Bhutan
2219
- BWP Botswana
2220
- BYR Belarus
2221
- BZD Belize
2222
- CAD Canada
2223
- CDF Congo/Kinshasa
2224
- CHF Switzerland
2225
- CLP Chile
2226
- CNY China
2227
- COP Colombia
2228
- CRC Costa Rica
2229
- CUP Cuba
2230
- CVE Cape Verde
2231
- CYP Cyprus
2232
- CZK Czech Republic
2233
- DEM Germany
2234
- DJF Djibouti
2235
- DKK Denmark
2236
- DOP Dominican Republic
2237
- DZD Algeria
2238
- EEK Estonia
2239
- EGP Egypt
2240
- ERN Eritrea
2241
- ESP Spain
2242
- ETB Ethiopia
2243
- EUR Euro Member Countries
2244
- FIM Finland
2245
- FJD Fiji
2246
- FKP Falkland Islands (Malvinas)
2247
- FRF France
2248
- GBP United Kingdom
2249
- GEL Georgia
2250
- GGP Guernsey
2251
- GHC Ghana
2252
- GIP Gibraltar
2253
- GMD Gambia
2254
- GNF Guinea
2255
- GRD Greece
2256
- GTQ Guatemala
2257
- GYD Guyana
2258
- HKD Hong Kong
2259
- HNL Honduras
2260
- HRK Croatia
2261
- HTG Haiti
2262
- HUF Hungary
2263
- IDR Indonesia
2264
- IEP Ireland (Eire)
2265
- ILS Israel
2266
- IMP Isle of Man
2267
- INR India
2268
- IQD Iraq
2269
- IRR Iran
2270
- ISK Iceland
2271
- ITL Italy
2272
- JEP Jersey
2273
- JMD Jamaica
2274
- JOD Jordan
2275
- JPY Japan
2276
- KES Kenya
2277
- KGS Kyrgyzstan
2278
- KHR Cambodia
2279
- KMF Comoros
2280
- KPW Korea
2281
- KWD Kuwait
2282
- KYD Cayman Islands
2283
- KZT Kazakstan
2284
- LAK Laos
2285
- LBP Lebanon
2286
- LKR Sri Lanka
2287
- LRD Liberia
2288
- LSL Lesotho
2289
- LTL Lithuania
2290
- LUF Luxembourg
2291
- LVL Latvia
2292
- LYD Libya
2293
- MAD Morocco
2294
- MDL Moldova
2295
- MGF Madagascar
2296
- MKD Macedonia
2297
- MMK Myanmar (Burma)
2298
- MNT Mongolia
2299
- MOP Macau
2300
- MRO Mauritania
2301
- MTL Malta
2302
- MUR Mauritius
2303
- MVR Maldives (Maldive Islands)
2304
- MWK Malawi
2305
- MXN Mexico
2306
- MYR Malaysia
2307
- MZM Mozambique
2308
- NAD Namibia
2309
- NGN Nigeria
2310
- NIO Nicaragua
2311
- NLG Netherlands (Holland)
2312
- NOK Norway
2313
- NPR Nepal
2314
- NZD New Zealand
2315
- OMR Oman
2316
- PAB Panama
2317
- PEN Peru
2318
- PGK Papua New Guinea
2319
- PHP Philippines
2320
- PKR Pakistan
2321
- PLN Poland
2322
- PTE Portugal
2323
- PYG Paraguay
2324
- QAR Qatar
2325
- ROL Romania
2326
- RUR Russia
2327
- RWF Rwanda
2328
- SAR Saudi Arabia
2329
- SBD Solomon Islands
2330
- SCR Seychelles
2331
- SDD Sudan
2332
- SEK Sweden
2333
- SGD Singapore
2334
- SHP Saint Helena
2335
- SIT Slovenia
2336
- SKK Slovakia
2337
- SLL Sierra Leone
2338
- SOS Somalia
2339
- SPL Seborga
2340
- SRG Suriname
2341
- STD S�o Tome and Principe
2342
- SVC El Salvador
2343
- SYP Syria
2344
- SZL Swaziland
2345
- THB Thailand
2346
- TJR Tajikistan
2347
- TMM Turkmenistan
2348
- TND Tunisia
2349
- TOP Tonga
2350
- TRL Turkey
2351
- TTD Trinidad and Tobago
2352
- TVD Tuvalu
2353
- TWD Taiwan
2354
- TZS Tanzania
2355
- UAH Ukraine
2356
- UGX Uganda
2357
- USD United States of America
2358
- UYU Uruguay
2359
- UZS Uzbekistan
2360
- VAL Vatican City
2361
- VEB Venezuela
2362
- VND Viet Nam
2363
- VUV Vanuatu
2364
- WST Samoa
2365
- XAF Communaut� Financi�re Africaine
2366
- XAG Silver
2367
- XAU Gold
2368
- XCD East Caribbean
2369
- XDR International Monetary Fund
2370
- XPD Palladium
2371
- XPF Comptoirs Fran�ais du Pacifique
2372
- XPT Platinum
2373
- YER Yemen
2374
- YUM Yugoslavia
2375
- ZAR South Africa
2376
- ZMK Zambia
2377
- ZWD Zimbabwe
2378
-
2379
- */
2380
-
2381
- return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
2382
- }
2383
-
2384
-
2385
-
2386
- public static function LanguageLookup($languagecode, $casesensitive=false) {
2387
-
2388
- if (!$casesensitive) {
2389
- $languagecode = strtolower($languagecode);
2390
- }
2391
-
2392
- // http://www.id3.org/id3v2.4.0-structure.txt
2393
- // [4. ID3v2 frame overview]
2394
- // The three byte language field, present in several frames, is used to
2395
- // describe the language of the frame's content, according to ISO-639-2
2396
- // [ISO-639-2]. The language should be represented in lower case. If the
2397
- // language is not known the string "XXX" should be used.
2398
-
2399
-
2400
- // ISO 639-2 - http://www.id3.org/iso639-2.html
2401
-
2402
- $begin = __LINE__;
2403
-
2404
- /** This is not a comment!
2405
-
2406
- XXX unknown
2407
- xxx unknown
2408
- aar Afar
2409
- abk Abkhazian
2410
- ace Achinese
2411
- ach Acoli
2412
- ada Adangme
2413
- afa Afro-Asiatic (Other)
2414
- afh Afrihili
2415
- afr Afrikaans
2416
- aka Akan
2417
- akk Akkadian
2418
- alb Albanian
2419
- ale Aleut
2420
- alg Algonquian Languages
2421
- amh Amharic
2422
- ang English, Old (ca. 450-1100)
2423
- apa Apache Languages
2424
- ara Arabic
2425
- arc Aramaic
2426
- arm Armenian
2427
- arn Araucanian
2428
- arp Arapaho
2429
- art Artificial (Other)
2430
- arw Arawak
2431
- asm Assamese
2432
- ath Athapascan Languages
2433
- ava Avaric
2434
- ave Avestan
2435
- awa Awadhi
2436
- aym Aymara
2437
- aze Azerbaijani
2438
- bad Banda
2439
- bai Bamileke Languages
2440
- bak Bashkir
2441
- bal Baluchi
2442
- bam Bambara
2443
- ban Balinese
2444
- baq Basque
2445
- bas Basa
2446
- bat Baltic (Other)
2447
- bej Beja
2448
- bel Byelorussian
2449
- bem Bemba
2450
- ben Bengali
2451
- ber Berber (Other)
2452
- bho Bhojpuri
2453
- bih Bihari
2454
- bik Bikol
2455
- bin Bini
2456
- bis Bislama
2457
- bla Siksika
2458
- bnt Bantu (Other)
2459
- bod Tibetan
2460
- bra Braj
2461
- bre Breton
2462
- bua Buriat
2463
- bug Buginese
2464
- bul Bulgarian
2465
- bur Burmese
2466
- cad Caddo
2467
- cai Central American Indian (Other)
2468
- car Carib
2469
- cat Catalan
2470
- cau Caucasian (Other)
2471
- ceb Cebuano
2472
- cel Celtic (Other)
2473
- ces Czech
2474
- cha Chamorro
2475
- chb Chibcha
2476
- che Chechen
2477
- chg Chagatai
2478
- chi Chinese
2479
- chm Mari
2480
- chn Chinook jargon
2481
- cho Choctaw
2482
- chr Cherokee
2483
- chu Church Slavic
2484
- chv Chuvash
2485
- chy Cheyenne
2486
- cop Coptic
2487
- cor Cornish
2488
- cos Corsican
2489
- cpe Creoles and Pidgins, English-based (Other)
2490
- cpf Creoles and Pidgins, French-based (Other)
2491
- cpp Creoles and Pidgins, Portuguese-based (Other)
2492
- cre Cree
2493
- crp Creoles and Pidgins (Other)
2494
- cus Cushitic (Other)
2495
- cym Welsh
2496
- cze Czech
2497
- dak Dakota
2498
- dan Danish
2499
- del Delaware
2500
- deu German
2501
- din Dinka
2502
- div Divehi
2503
- doi Dogri
2504
- dra Dravidian (Other)
2505
- dua Duala
2506
- dum Dutch, Middle (ca. 1050-1350)
2507
- dut Dutch
2508
- dyu Dyula
2509
- dzo Dzongkha
2510
- efi Efik
2511
- egy Egyptian (Ancient)
2512
- eka Ekajuk
2513
- ell Greek, Modern (1453-)
2514
- elx Elamite
2515
- eng English
2516
- enm English, Middle (ca. 1100-1500)
2517
- epo Esperanto
2518
- esk Eskimo (Other)
2519
- esl Spanish
2520
- est Estonian
2521
- eus Basque
2522
- ewe Ewe
2523
- ewo Ewondo
2524
- fan Fang
2525
- fao Faroese
2526
- fas Persian
2527
- fat Fanti
2528
- fij Fijian
2529
- fin Finnish
2530
- fiu Finno-Ugrian (Other)
2531
- fon Fon
2532
- fra French
2533
- fre French
2534
- frm French, Middle (ca. 1400-1600)
2535
- fro French, Old (842- ca. 1400)
2536
- fry Frisian
2537
- ful Fulah
2538
- gaa Ga
2539
- gae Gaelic (Scots)
2540
- gai Irish
2541
- gay Gayo
2542
- gdh Gaelic (Scots)
2543
- gem Germanic (Other)
2544
- geo Georgian
2545
- ger German
2546
- gez Geez
2547
- gil Gilbertese
2548
- glg Gallegan
2549
- gmh German, Middle High (ca. 1050-1500)
2550
- goh German, Old High (ca. 750-1050)
2551
- gon Gondi
2552
- got Gothic
2553
- grb Grebo
2554
- grc Greek, Ancient (to 1453)
2555
- gre Greek, Modern (1453-)
2556
- grn Guarani
2557
- guj Gujarati
2558
- hai Haida
2559
- hau Hausa
2560
- haw Hawaiian
2561
- heb Hebrew
2562
- her Herero
2563
- hil Hiligaynon
2564
- him Himachali
2565
- hin Hindi
2566
- hmo Hiri Motu
2567
- hun Hungarian
2568
- hup Hupa
2569
- hye Armenian
2570
- iba Iban
2571
- ibo Igbo
2572
- ice Icelandic
2573
- ijo Ijo
2574
- iku Inuktitut
2575
- ilo Iloko
2576
- ina Interlingua (International Auxiliary language Association)
2577
- inc Indic (Other)
2578
- ind Indonesian
2579
- ine Indo-European (Other)
2580
- ine Interlingue
2581
- ipk Inupiak
2582
- ira Iranian (Other)
2583
- iri Irish
2584
- iro Iroquoian uages
2585
- isl Icelandic
2586
- ita Italian
2587
- jav Javanese
2588
- jaw Javanese
2589
- jpn Japanese
2590
- jpr Judeo-Persian
2591
- jrb Judeo-Arabic
2592
- kaa Kara-Kalpak
2593
- kab Kabyle
2594
- kac Kachin
2595
- kal Greenlandic
2596
- kam Kamba
2597
- kan Kannada
2598
- kar Karen
2599
- kas Kashmiri
2600
- kat Georgian
2601
- kau Kanuri
2602
- kaw Kawi
2603
- kaz Kazakh
2604
- kha Khasi
2605
- khi Khoisan (Other)
2606
- khm Khmer
2607
- kho Khotanese
2608
- kik Kikuyu
2609
- kin Kinyarwanda
2610
- kir Kirghiz
2611
- kok Konkani
2612
- kom Komi
2613
- kon Kongo
2614
- kor Korean
2615
- kpe Kpelle
2616
- kro Kru
2617
- kru Kurukh
2618
- kua Kuanyama
2619
- kum Kumyk
2620
- kur Kurdish
2621
- kus Kusaie
2622
- kut Kutenai
2623
- lad Ladino
2624
- lah Lahnda
2625
- lam Lamba
2626
- lao Lao
2627
- lat Latin
2628
- lav Latvian
2629
- lez Lezghian
2630
- lin Lingala
2631
- lit Lithuanian
2632
- lol Mongo
2633
- loz Lozi
2634
- ltz Letzeburgesch
2635
- lub Luba-Katanga
2636
- lug Ganda
2637
- lui Luiseno
2638
- lun Lunda
2639
- luo Luo (Kenya and Tanzania)
2640
- mac Macedonian
2641
- mad Madurese
2642
- mag Magahi
2643
- mah Marshall
2644
- mai Maithili
2645
- mak Macedonian
2646
- mak Makasar
2647
- mal Malayalam
2648
- man Mandingo
2649
- mao Maori
2650
- map Austronesian (Other)
2651
- mar Marathi
2652
- mas Masai
2653
- max Manx
2654
- may Malay
2655
- men Mende
2656
- mga Irish, Middle (900 - 1200)
2657
- mic Micmac
2658
- min Minangkabau
2659
- mis Miscellaneous (Other)
2660
- mkh Mon-Kmer (Other)
2661
- mlg Malagasy
2662
- mlt Maltese
2663
- mni Manipuri
2664
- mno Manobo Languages
2665
- moh Mohawk
2666
- mol Moldavian
2667
- mon Mongolian
2668
- mos Mossi
2669
- mri Maori
2670
- msa Malay
2671
- mul Multiple Languages
2672
- mun Munda Languages
2673
- mus Creek
2674
- mwr Marwari
2675
- mya Burmese
2676
- myn Mayan Languages
2677
- nah Aztec
2678
- nai North American Indian (Other)
2679
- nau Nauru
2680
- nav Navajo
2681
- nbl Ndebele, South
2682
- nde Ndebele, North
2683
- ndo Ndongo
2684
- nep Nepali
2685
- new Newari
2686
- nic Niger-Kordofanian (Other)
2687
- niu Niuean
2688
- nla Dutch
2689
- nno Norwegian (Nynorsk)
2690
- non Norse, Old
2691
- nor Norwegian
2692
- nso Sotho, Northern
2693
- nub Nubian Languages
2694
- nya Nyanja
2695
- nym Nyamwezi
2696
- nyn Nyankole
2697
- nyo Nyoro
2698
- nzi Nzima
2699
- oci Langue d'Oc (post 1500)
2700
- oji Ojibwa
2701
- ori Oriya
2702
- orm Oromo
2703
- osa Osage
2704
- oss Ossetic
2705
- ota Turkish, Ottoman (1500 - 1928)
2706
- oto Otomian Languages
2707
- paa Papuan-Australian (Other)
2708
- pag Pangasinan
2709
- pal Pahlavi
2710
- pam Pampanga
2711
- pan Panjabi
2712
- pap Papiamento
2713
- pau Palauan
2714
- peo Persian, Old (ca 600 - 400 B.C.)
2715
- per Persian
2716
- phn Phoenician
2717
- pli Pali
2718
- pol Polish
2719
- pon Ponape
2720
- por Portuguese
2721
- pra Prakrit uages
2722
- pro Provencal, Old (to 1500)
2723
- pus Pushto
2724
- que Quechua
2725
- raj Rajasthani
2726
- rar Rarotongan
2727
- roa Romance (Other)
2728
- roh Rhaeto-Romance
2729
- rom Romany
2730
- ron Romanian
2731
- rum Romanian
2732
- run Rundi
2733
- rus Russian
2734
- sad Sandawe
2735
- sag Sango
2736
- sah Yakut
2737
- sai South American Indian (Other)
2738
- sal Salishan Languages
2739
- sam Samaritan Aramaic
2740
- san Sanskrit
2741
- sco Scots
2742
- scr Serbo-Croatian
2743
- sel Selkup
2744
- sem Semitic (Other)
2745
- sga Irish, Old (to 900)
2746
- shn Shan
2747
- sid Sidamo
2748
- sin Singhalese
2749
- sio Siouan Languages
2750
- sit Sino-Tibetan (Other)
2751
- sla Slavic (Other)
2752
- slk Slovak
2753
- slo Slovak
2754
- slv Slovenian
2755
- smi Sami Languages
2756
- smo Samoan
2757
- sna Shona
2758
- snd Sindhi
2759
- sog Sogdian
2760
- som Somali
2761
- son Songhai
2762
- sot Sotho, Southern
2763
- spa Spanish
2764
- sqi Albanian
2765
- srd Sardinian
2766
- srr Serer
2767
- ssa Nilo-Saharan (Other)
2768
- ssw Siswant
2769
- ssw Swazi
2770
- suk Sukuma
2771
- sun Sudanese
2772
- sus Susu
2773
- sux Sumerian
2774
- sve Swedish
2775
- swa Swahili
2776
- swe Swedish
2777
- syr Syriac
2778
- tah Tahitian
2779
- tam Tamil
2780
- tat Tatar
2781
- tel Telugu
2782
- tem Timne
2783
- ter Tereno
2784
- tgk Tajik
2785
- tgl Tagalog
2786
- tha Thai
2787
- tib Tibetan
2788
- tig Tigre
2789
- tir Tigrinya
2790
- tiv Tivi
2791
- tli Tlingit
2792
- tmh Tamashek
2793
- tog Tonga (Nyasa)
2794
- ton Tonga (Tonga Islands)
2795
- tru Truk
2796
- tsi Tsimshian
2797
- tsn Tswana
2798
- tso Tsonga
2799
- tuk Turkmen
2800
- tum Tumbuka
2801
- tur Turkish
2802
- tut Altaic (Other)
2803
- twi Twi
2804
- tyv Tuvinian
2805
- uga Ugaritic
2806
- uig Uighur
2807
- ukr Ukrainian
2808
- umb Umbundu
2809
- und Undetermined
2810
- urd Urdu
2811
- uzb Uzbek
2812
- vai Vai
2813
- ven Venda
2814
- vie Vietnamese
2815
- vol Volap�k
2816
- vot Votic
2817
- wak Wakashan Languages
2818
- wal Walamo
2819
- war Waray
2820
- was Washo
2821
- wel Welsh
2822
- wen Sorbian Languages
2823
- wol Wolof
2824
- xho Xhosa
2825
- yao Yao
2826
- yap Yap
2827
- yid Yiddish
2828
- yor Yoruba
2829
- zap Zapotec
2830
- zen Zenaga
2831
- zha Zhuang
2832
- zho Chinese
2833
- zul Zulu
2834
- zun Zuni
2835
-
2836
- */
2837
-
2838
- return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
2839
- }
2840
-
2841
-
2842
- public static function ETCOEventLookup($index) {
2843
- if (($index >= 0x17) && ($index <= 0xDF)) {
2844
- return 'reserved for future use';
2845
- }
2846
- if (($index >= 0xE0) && ($index <= 0xEF)) {
2847
- return 'not predefined synch 0-F';
2848
- }
2849
- if (($index >= 0xF0) && ($index <= 0xFC)) {
2850
- return 'reserved for future use';
2851
- }
2852
-
2853
- static $EventLookup = array(
2854
- 0x00 => 'padding (has no meaning)',
2855
- 0x01 => 'end of initial silence',
2856
- 0x02 => 'intro start',
2857
- 0x03 => 'main part start',
2858
- 0x04 => 'outro start',
2859
- 0x05 => 'outro end',
2860
- 0x06 => 'verse start',
2861
- 0x07 => 'refrain start',
2862
- 0x08 => 'interlude start',
2863
- 0x09 => 'theme start',
2864
- 0x0A => 'variation start',
2865
- 0x0B => 'key change',
2866
- 0x0C => 'time change',
2867
- 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
2868
- 0x0E => 'sustained noise',
2869
- 0x0F => 'sustained noise end',
2870
- 0x10 => 'intro end',
2871
- 0x11 => 'main part end',
2872
- 0x12 => 'verse end',
2873
- 0x13 => 'refrain end',
2874
- 0x14 => 'theme end',
2875
- 0x15 => 'profanity',
2876
- 0x16 => 'profanity end',
2877
- 0xFD => 'audio end (start of silence)',
2878
- 0xFE => 'audio file ends',
2879
- 0xFF => 'one more byte of events follows'
2880
- );
2881
-
2882
- return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
2883
- }
2884
-
2885
- public static function SYTLContentTypeLookup($index) {
2886
- static $SYTLContentTypeLookup = array(
2887
- 0x00 => 'other',
2888
- 0x01 => 'lyrics',
2889
- 0x02 => 'text transcription',
2890
- 0x03 => 'movement/part name', // (e.g. 'Adagio')
2891
- 0x04 => 'events', // (e.g. 'Don Quijote enters the stage')
2892
- 0x05 => 'chord', // (e.g. 'Bb F Fsus')
2893
- 0x06 => 'trivia/\'pop up\' information',
2894
- 0x07 => 'URLs to webpages',
2895
- 0x08 => 'URLs to images'
2896
- );
2897
-
2898
- return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
2899
- }
2900
-
2901
- public static function APICPictureTypeLookup($index, $returnarray=false) {
2902
- static $APICPictureTypeLookup = array(
2903
- 0x00 => 'Other',
2904
- 0x01 => '32x32 pixels \'file icon\' (PNG only)',
2905
- 0x02 => 'Other file icon',
2906
- 0x03 => 'Cover (front)',
2907
- 0x04 => 'Cover (back)',
2908
- 0x05 => 'Leaflet page',
2909
- 0x06 => 'Media (e.g. label side of CD)',
2910
- 0x07 => 'Lead artist/lead performer/soloist',
2911
- 0x08 => 'Artist/performer',
2912
- 0x09 => 'Conductor',
2913
- 0x0A => 'Band/Orchestra',
2914
- 0x0B => 'Composer',
2915
- 0x0C => 'Lyricist/text writer',
2916
- 0x0D => 'Recording Location',
2917
- 0x0E => 'During recording',
2918
- 0x0F => 'During performance',
2919
- 0x10 => 'Movie/video screen capture',
2920
- 0x11 => 'A bright coloured fish',
2921
- 0x12 => 'Illustration',
2922
- 0x13 => 'Band/artist logotype',
2923
- 0x14 => 'Publisher/Studio logotype'
2924
- );
2925
- if ($returnarray) {
2926
- return $APICPictureTypeLookup;
2927
- }
2928
- return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
2929
- }
2930
-
2931
- public static function COMRReceivedAsLookup($index) {
2932
- static $COMRReceivedAsLookup = array(
2933
- 0x00 => 'Other',
2934
- 0x01 => 'Standard CD album with other songs',
2935
- 0x02 => 'Compressed audio on CD',
2936
- 0x03 => 'File over the Internet',
2937
- 0x04 => 'Stream over the Internet',
2938
- 0x05 => 'As note sheets',
2939
- 0x06 => 'As note sheets in a book with other sheets',
2940
- 0x07 => 'Music on other media',
2941
- 0x08 => 'Non-musical merchandise'
2942
- );
2943
-
2944
- return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
2945
- }
2946
-
2947
- public static function RVA2ChannelTypeLookup($index) {
2948
- static $RVA2ChannelTypeLookup = array(
2949
- 0x00 => 'Other',
2950
- 0x01 => 'Master volume',
2951
- 0x02 => 'Front right',
2952
- 0x03 => 'Front left',
2953
- 0x04 => 'Back right',
2954
- 0x05 => 'Back left',
2955
- 0x06 => 'Front centre',
2956
- 0x07 => 'Back centre',
2957
- 0x08 => 'Subwoofer'
2958
- );
2959
-
2960
- return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
2961
- }
2962
-
2963
- public static function FrameNameLongLookup($framename) {
2964
-
2965
- $begin = __LINE__;
2966
-
2967
- /** This is not a comment!
2968
-
2969
- AENC Audio encryption
2970
- APIC Attached picture
2971
- ASPI Audio seek point index
2972
- BUF Recommended buffer size
2973
- CNT Play counter
2974
- COM Comments
2975
- COMM Comments
2976
- COMR Commercial frame
2977
- CRA Audio encryption
2978
- CRM Encrypted meta frame
2979
- ENCR Encryption method registration
2980
- EQU Equalisation
2981
- EQU2 Equalisation (2)
2982
- EQUA Equalisation
2983
- ETC Event timing codes
2984
- ETCO Event timing codes
2985
- GEO General encapsulated object
2986
- GEOB General encapsulated object
2987
- GRID Group identification registration
2988
- IPL Involved people list
2989
- IPLS Involved people list
2990
- LINK Linked information
2991
- LNK Linked information
2992
- MCDI Music CD identifier
2993
- MCI Music CD Identifier
2994
- MLL MPEG location lookup table
2995
- MLLT MPEG location lookup table
2996
- OWNE Ownership frame
2997
- PCNT Play counter
2998
- PIC Attached picture
2999
- POP Popularimeter
3000
- POPM Popularimeter
3001
- POSS Position synchronisation frame
3002
- PRIV Private frame
3003
- RBUF Recommended buffer size
3004
- REV Reverb
3005
- RVA Relative volume adjustment
3006
- RVA2 Relative volume adjustment (2)
3007
- RVAD Relative volume adjustment
3008
- RVRB Reverb
3009
- SEEK Seek frame
3010
- SIGN Signature frame
3011
- SLT Synchronised lyric/text
3012
- STC Synced tempo codes
3013
- SYLT Synchronised lyric/text
3014
- SYTC Synchronised tempo codes
3015
- TAL Album/Movie/Show title
3016
- TALB Album/Movie/Show title
3017
- TBP BPM (Beats Per Minute)
3018
- TBPM BPM (beats per minute)
3019
- TCM Composer
3020
- TCMP Part of a compilation
3021
- TCO Content type
3022
- TCOM Composer
3023
- TCON Content type
3024
- TCOP Copyright message
3025
- TCP Part of a compilation
3026
- TCR Copyright message
3027
- TDA Date
3028
- TDAT Date
3029
- TDEN Encoding time
3030
- TDLY Playlist delay
3031
- TDOR Original release time
3032
- TDRC Recording time
3033
- TDRL Release time
3034
- TDTG Tagging time
3035
- TDY Playlist delay
3036
- TEN Encoded by
3037
- TENC Encoded by
3038
- TEXT Lyricist/Text writer
3039
- TFLT File type
3040
- TFT File type
3041
- TIM Time
3042
- TIME Time
3043
- TIPL Involved people list
3044
- TIT1 Content group description
3045
- TIT2 Title/songname/content description
3046
- TIT3 Subtitle/Description refinement
3047
- TKE Initial key
3048
- TKEY Initial key
3049
- TLA Language(s)
3050
- TLAN Language(s)
3051
- TLE Length
3052
- TLEN Length
3053
- TMCL Musician credits list
3054
- TMED Media type
3055
- TMOO Mood
3056
- TMT Media type
3057
- TOA Original artist(s)/performer(s)
3058
- TOAL Original album/movie/show title
3059
- TOF Original filename
3060
- TOFN Original filename
3061
- TOL Original Lyricist(s)/text writer(s)
3062
- TOLY Original lyricist(s)/text writer(s)
3063
- TOPE Original artist(s)/performer(s)
3064
- TOR Original release year
3065
- TORY Original release year
3066
- TOT Original album/Movie/Show title
3067
- TOWN File owner/licensee
3068
- TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
3069
- TP2 Band/Orchestra/Accompaniment
3070
- TP3 Conductor/Performer refinement
3071
- TP4 Interpreted, remixed, or otherwise modified by
3072
- TPA Part of a set
3073
- TPB Publisher
3074
- TPE1 Lead performer(s)/Soloist(s)
3075
- TPE2 Band/orchestra/accompaniment
3076
- TPE3 Conductor/performer refinement
3077
- TPE4 Interpreted, remixed, or otherwise modified by
3078
- TPOS Part of a set
3079
- TPRO Produced notice
3080
- TPUB Publisher
3081
- TRC ISRC (International Standard Recording Code)
3082
- TRCK Track number/Position in set
3083
- TRD Recording dates
3084
- TRDA Recording dates
3085
- TRK Track number/Position in set
3086
- TRSN Internet radio station name
3087
- TRSO Internet radio station owner
3088
- TS2 Album-Artist sort order
3089
- TSA Album sort order
3090
- TSC Composer sort order
3091
- TSI Size
3092
- TSIZ Size
3093
- TSO2 Album-Artist sort order
3094
- TSOA Album sort order
3095
- TSOC Composer sort order
3096
- TSOP Performer sort order
3097
- TSOT Title sort order
3098
- TSP Performer sort order
3099
- TSRC ISRC (international standard recording code)
3100
- TSS Software/hardware and settings used for encoding
3101
- TSSE Software/Hardware and settings used for encoding
3102
- TSST Set subtitle
3103
- TST Title sort order
3104
- TT1 Content group description
3105
- TT2 Title/Songname/Content description
3106
- TT3 Subtitle/Description refinement
3107
- TXT Lyricist/text writer
3108
- TXX User defined text information frame
3109
- TXXX User defined text information frame
3110
- TYE Year
3111
- TYER Year
3112
- UFI Unique file identifier
3113
- UFID Unique file identifier
3114
- ULT Unsychronised lyric/text transcription
3115
- USER Terms of use
3116
- USLT Unsynchronised lyric/text transcription
3117
- WAF Official audio file webpage
3118
- WAR Official artist/performer webpage
3119
- WAS Official audio source webpage
3120
- WCM Commercial information
3121
- WCOM Commercial information
3122
- WCOP Copyright/Legal information
3123
- WCP Copyright/Legal information
3124
- WOAF Official audio file webpage
3125
- WOAR Official artist/performer webpage
3126
- WOAS Official audio source webpage
3127
- WORS Official Internet radio station homepage
3128
- WPAY Payment
3129
- WPB Publishers official webpage
3130
- WPUB Publishers official webpage
3131
- WXX User defined URL link frame
3132
- WXXX User defined URL link frame
3133
- TFEA Featured Artist
3134
- TSTU Recording Studio
3135
- rgad Replay Gain Adjustment
3136
-
3137
- */
3138
-
3139
- return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
3140
-
3141
- // Last three:
3142
- // from Helium2 [www.helium2.com]
3143
- // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
3144
- }
3145
-
3146
-
3147
- public static function FrameNameShortLookup($framename) {
3148
-
3149
- $begin = __LINE__;
3150
-
3151
- /** This is not a comment!
3152
-
3153
- AENC audio_encryption
3154
- APIC attached_picture
3155
- ASPI audio_seek_point_index
3156
- BUF recommended_buffer_size
3157
- CNT play_counter
3158
- COM comment
3159
- COMM comment
3160
- COMR commercial_frame
3161
- CRA audio_encryption
3162
- CRM encrypted_meta_frame
3163
- ENCR encryption_method_registration
3164
- EQU equalisation
3165
- EQU2 equalisation
3166
- EQUA equalisation
3167
- ETC event_timing_codes
3168
- ETCO event_timing_codes
3169
- GEO general_encapsulated_object
3170
- GEOB general_encapsulated_object
3171
- GRID group_identification_registration
3172
- IPL involved_people_list
3173
- IPLS involved_people_list
3174
- LINK linked_information
3175
- LNK linked_information
3176
- MCDI music_cd_identifier
3177
- MCI music_cd_identifier
3178
- MLL mpeg_location_lookup_table
3179
- MLLT mpeg_location_lookup_table
3180
- OWNE ownership_frame
3181
- PCNT play_counter
3182
- PIC attached_picture
3183
- POP popularimeter
3184
- POPM popularimeter
3185
- POSS position_synchronisation_frame
3186
- PRIV private_frame
3187
- RBUF recommended_buffer_size
3188
- REV reverb
3189
- RVA relative_volume_adjustment
3190
- RVA2 relative_volume_adjustment
3191
- RVAD relative_volume_adjustment
3192
- RVRB reverb
3193
- SEEK seek_frame
3194
- SIGN signature_frame
3195
- SLT synchronised_lyric
3196
- STC synced_tempo_codes
3197
- SYLT synchronised_lyric
3198
- SYTC synchronised_tempo_codes
3199
- TAL album
3200
- TALB album
3201
- TBP bpm
3202
- TBPM bpm
3203
- TCM composer
3204
- TCMP part_of_a_compilation
3205
- TCO genre
3206
- TCOM composer
3207
- TCON genre
3208
- TCOP copyright_message
3209
- TCP part_of_a_compilation
3210
- TCR copyright_message
3211
- TDA date
3212
- TDAT date
3213
- TDEN encoding_time
3214
- TDLY playlist_delay
3215
- TDOR original_release_time
3216
- TDRC recording_time
3217
- TDRL release_time
3218
- TDTG tagging_time
3219
- TDY playlist_delay
3220
- TEN encoded_by
3221
- TENC encoded_by
3222
- TEXT lyricist
3223
- TFLT file_type
3224
- TFT file_type
3225
- TIM time
3226
- TIME time
3227
- TIPL involved_people_list
3228
- TIT1 content_group_description
3229
- TIT2 title
3230
- TIT3 subtitle
3231
- TKE initial_key
3232
- TKEY initial_key
3233
- TLA language
3234
- TLAN language
3235
- TLE length
3236
- TLEN length
3237
- TMCL musician_credits_list
3238
- TMED media_type
3239
- TMOO mood
3240
- TMT media_type
3241
- TOA original_artist
3242
- TOAL original_album
3243
- TOF original_filename
3244
- TOFN original_filename
3245
- TOL original_lyricist
3246
- TOLY original_lyricist
3247
- TOPE original_artist
3248
- TOR original_year
3249
- TORY original_year
3250
- TOT original_album
3251
- TOWN file_owner
3252
- TP1 artist
3253
- TP2 band
3254
- TP3 conductor
3255
- TP4 remixer
3256
- TPA part_of_a_set
3257
- TPB publisher
3258
- TPE1 artist
3259
- TPE2 band
3260
- TPE3 conductor
3261
- TPE4 remixer
3262
- TPOS part_of_a_set
3263
- TPRO produced_notice
3264
- TPUB publisher
3265
- TRC isrc
3266
- TRCK track_number
3267
- TRD recording_dates
3268
- TRDA recording_dates
3269
- TRK track_number
3270
- TRSN internet_radio_station_name
3271
- TRSO internet_radio_station_owner
3272
- TS2 album_artist_sort_order
3273
- TSA album_sort_order
3274
- TSC composer_sort_order
3275
- TSI size
3276
- TSIZ size
3277
- TSO2 album_artist_sort_order
3278
- TSOA album_sort_order
3279
- TSOC composer_sort_order
3280
- TSOP performer_sort_order
3281
- TSOT title_sort_order
3282
- TSP performer_sort_order
3283
- TSRC isrc
3284
- TSS encoder_settings
3285
- TSSE encoder_settings
3286
- TSST set_subtitle
3287
- TST title_sort_order
3288
- TT1 content_group_description
3289
- TT2 title
3290
- TT3 subtitle
3291
- TXT lyricist
3292
- TXX text
3293
- TXXX text
3294
- TYE year
3295
- TYER year
3296
- UFI unique_file_identifier
3297
- UFID unique_file_identifier
3298
- ULT unsychronised_lyric
3299
- USER terms_of_use
3300
- USLT unsynchronised_lyric
3301
- WAF url_file
3302
- WAR url_artist
3303
- WAS url_source
3304
- WCM commercial_information
3305
- WCOM commercial_information
3306
- WCOP copyright
3307
- WCP copyright
3308
- WOAF url_file
3309
- WOAR url_artist
3310
- WOAS url_source
3311
- WORS url_station
3312
- WPAY url_payment
3313
- WPB url_publisher
3314
- WPUB url_publisher
3315
- WXX url_user
3316
- WXXX url_user
3317
- TFEA featured_artist
3318
- TSTU recording_studio
3319
- rgad replay_gain_adjustment
3320
-
3321
- */
3322
-
3323
- return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
3324
- }
3325
-
3326
- public static function TextEncodingTerminatorLookup($encoding) {
3327
- // http://www.id3.org/id3v2.4.0-structure.txt
3328
- // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3329
- static $TextEncodingTerminatorLookup = array(
3330
- 0 => "\x00", // $00 ISO-8859-1. Terminated with $00.
3331
- 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
3332
- 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3333
- 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
3334
- 255 => "\x00\x00"
3335
- );
3336
- return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : '');
3337
- }
3338
-
3339
- public static function TextEncodingNameLookup($encoding) {
3340
- // http://www.id3.org/id3v2.4.0-structure.txt
3341
- // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
3342
- static $TextEncodingNameLookup = array(
3343
- 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00.
3344
- 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
3345
- 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
3346
- 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00.
3347
- 255 => 'UTF-16BE'
3348
- );
3349
- return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
3350
- }
3351
-
3352
- public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
3353
- switch ($id3v2majorversion) {
3354
- case 2:
3355
- return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
3356
- break;
3357
-
3358
- case 3:
3359
- case 4:
3360
- return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
3361
- break;
3362
- }
3363
- return false;
3364
- }
3365
-
3366
- public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
3367
- for ($i = 0; $i < strlen($numberstring); $i++) {
3368
- if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
3369
- if (($numberstring{$i} == '.') && $allowdecimal) {
3370
- // allowed
3371
- } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
3372
- // allowed
3373
- } else {
3374
- return false;
3375
- }
3376
- }
3377
- }
3378
- return true;
3379
- }
3380
-
3381
- public static function IsValidDateStampString($datestamp) {
3382
- if (strlen($datestamp) != 8) {
3383
- return false;
3384
- }
3385
- if (!self::IsANumber($datestamp, false)) {
3386
- return false;
3387
- }
3388
- $year = substr($datestamp, 0, 4);
3389
- $month = substr($datestamp, 4, 2);
3390
- $day = substr($datestamp, 6, 2);
3391
- if (($year == 0) || ($month == 0) || ($day == 0)) {
3392
- return false;
3393
- }
3394
- if ($month > 12) {
3395
- return false;
3396
- }
3397
- if ($day > 31) {
3398
- return false;
3399
- }
3400
- if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
3401
- return false;
3402
- }
3403
- if (($day > 29) && ($month == 2)) {
3404
- return false;
3405
- }
3406
- return true;
3407
- }
3408
-
3409
- public static function ID3v2HeaderLength($majorversion) {
3410
- return (($majorversion == 2) ? 6 : 10);
3411
- }
3412
-
3413
- }
3414
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/module.tag.lyrics3.php DELETED
@@ -1,294 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- /// //
10
- // module.tag.lyrics3.php //
11
- // module for analyzing Lyrics3 tags //
12
- // dependencies: module.tag.apetag.php (optional) //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
-
17
- class getid3_lyrics3 extends getid3_handler
18
- {
19
-
20
- public function Analyze() {
21
- $info = &$this->getid3->info;
22
-
23
- // http://www.volweb.cz/str/tags.htm
24
-
25
- if (!getid3_lib::intValueSupported($info['filesize'])) {
26
- $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
27
- return false;
28
- }
29
-
30
- fseek($this->getid3->fp, (0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
31
- $lyrics3_id3v1 = fread($this->getid3->fp, 128 + 9 + 6);
32
- $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
33
- $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
34
- $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
35
-
36
- if ($lyrics3end == 'LYRICSEND') {
37
- // Lyrics3v1, ID3v1, no APE
38
-
39
- $lyrics3size = 5100;
40
- $lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
41
- $lyrics3version = 1;
42
-
43
- } elseif ($lyrics3end == 'LYRICS200') {
44
- // Lyrics3v2, ID3v1, no APE
45
-
46
- // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
47
- $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200');
48
- $lyrics3offset = $info['filesize'] - 128 - $lyrics3size;
49
- $lyrics3version = 2;
50
-
51
- } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
52
- // Lyrics3v1, no ID3v1, no APE
53
-
54
- $lyrics3size = 5100;
55
- $lyrics3offset = $info['filesize'] - $lyrics3size;
56
- $lyrics3version = 1;
57
- $lyrics3offset = $info['filesize'] - $lyrics3size;
58
-
59
- } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
60
-
61
- // Lyrics3v2, no ID3v1, no APE
62
-
63
- $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
64
- $lyrics3offset = $info['filesize'] - $lyrics3size;
65
- $lyrics3version = 2;
66
-
67
- } else {
68
-
69
- if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) {
70
-
71
- fseek($this->getid3->fp, $info['ape']['tag_offset_start'] - 15, SEEK_SET);
72
- $lyrics3lsz = fread($this->getid3->fp, 6);
73
- $lyrics3end = fread($this->getid3->fp, 9);
74
-
75
- if ($lyrics3end == 'LYRICSEND') {
76
- // Lyrics3v1, APE, maybe ID3v1
77
-
78
- $lyrics3size = 5100;
79
- $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
80
- $info['avdataend'] = $lyrics3offset;
81
- $lyrics3version = 1;
82
- $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
83
-
84
- } elseif ($lyrics3end == 'LYRICS200') {
85
- // Lyrics3v2, APE, maybe ID3v1
86
-
87
- $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
88
- $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size;
89
- $lyrics3version = 2;
90
- $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
91
-
92
- }
93
-
94
- }
95
-
96
- }
97
-
98
- if (isset($lyrics3offset)) {
99
- $info['avdataend'] = $lyrics3offset;
100
- $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
101
-
102
- if (!isset($info['ape'])) {
103
- $GETID3_ERRORARRAY = &$info['warning'];
104
- if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) {
105
- $getid3_temp = new getID3();
106
- $getid3_temp->openfile($this->getid3->filename);
107
- $getid3_apetag = new getid3_apetag($getid3_temp);
108
- $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
109
- $getid3_apetag->Analyze();
110
- if (!empty($getid3_temp->info['ape'])) {
111
- $info['ape'] = $getid3_temp->info['ape'];
112
- }
113
- if (!empty($getid3_temp->info['replay_gain'])) {
114
- $info['replay_gain'] = $getid3_temp->info['replay_gain'];
115
- }
116
- unset($getid3_temp, $getid3_apetag);
117
- }
118
- }
119
-
120
- }
121
-
122
- return true;
123
- }
124
-
125
- public function getLyrics3Data($endoffset, $version, $length) {
126
- // http://www.volweb.cz/str/tags.htm
127
-
128
- $info = &$this->getid3->info;
129
-
130
- if (!getid3_lib::intValueSupported($endoffset)) {
131
- $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
132
- return false;
133
- }
134
-
135
- fseek($this->getid3->fp, $endoffset, SEEK_SET);
136
- if ($length <= 0) {
137
- return false;
138
- }
139
- $rawdata = fread($this->getid3->fp, $length);
140
-
141
- $ParsedLyrics3['raw']['lyrics3version'] = $version;
142
- $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
143
- $ParsedLyrics3['tag_offset_start'] = $endoffset;
144
- $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
145
-
146
- if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
147
- if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
148
-
149
- $info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
150
- $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
151
- $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
152
- $length = strlen($rawdata);
153
- $ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
154
- $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
155
-
156
- } else {
157
-
158
- $info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
159
- return false;
160
-
161
- }
162
-
163
- }
164
-
165
- switch ($version) {
166
-
167
- case 1:
168
- if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
169
- $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
170
- $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
171
- } else {
172
- $info['error'][] = '"LYRICSEND" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
173
- return false;
174
- }
175
- break;
176
-
177
- case 2:
178
- if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
179
- $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
180
- $rawdata = $ParsedLyrics3['raw']['unparsed'];
181
- while (strlen($rawdata) > 0) {
182
- $fieldname = substr($rawdata, 0, 3);
183
- $fieldsize = (int) substr($rawdata, 3, 5);
184
- $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
185
- $rawdata = substr($rawdata, 3 + 5 + $fieldsize);
186
- }
187
-
188
- if (isset($ParsedLyrics3['raw']['IND'])) {
189
- $i = 0;
190
- $flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
191
- foreach ($flagnames as $flagname) {
192
- if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
193
- $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
194
- }
195
- }
196
- }
197
-
198
- $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
199
- foreach ($fieldnametranslation as $key => $value) {
200
- if (isset($ParsedLyrics3['raw'][$key])) {
201
- $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
202
- }
203
- }
204
-
205
- if (isset($ParsedLyrics3['raw']['IMG'])) {
206
- $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
207
- foreach ($imagestrings as $key => $imagestring) {
208
- if (strpos($imagestring, '||') !== false) {
209
- $imagearray = explode('||', $imagestring);
210
- $ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : '');
211
- $ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : '');
212
- $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
213
- }
214
- }
215
- }
216
- if (isset($ParsedLyrics3['raw']['LYR'])) {
217
- $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
218
- }
219
- } else {
220
- $info['error'][] = '"LYRICS200" expected at '.(ftell($this->getid3->fp) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
221
- return false;
222
- }
223
- break;
224
-
225
- default:
226
- $info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
227
- return false;
228
- break;
229
- }
230
-
231
-
232
- if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) {
233
- $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
234
- unset($info['id3v1']);
235
- foreach ($info['warning'] as $key => $value) {
236
- if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
237
- unset($info['warning'][$key]);
238
- sort($info['warning']);
239
- break;
240
- }
241
- }
242
- }
243
-
244
- $info['lyrics3'] = $ParsedLyrics3;
245
-
246
- return true;
247
- }
248
-
249
- public function Lyrics3Timestamp2Seconds($rawtimestamp) {
250
- if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) {
251
- return (int) (($regs[1] * 60) + $regs[2]);
252
- }
253
- return false;
254
- }
255
-
256
- public function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
257
- $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
258
- foreach ($lyricsarray as $key => $lyricline) {
259
- $regs = array();
260
- unset($thislinetimestamps);
261
- while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) {
262
- $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
263
- $lyricline = str_replace($regs[0], '', $lyricline);
264
- }
265
- $notimestamplyricsarray[$key] = $lyricline;
266
- if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
267
- sort($thislinetimestamps);
268
- foreach ($thislinetimestamps as $timestampkey => $timestamp) {
269
- if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
270
- // timestamps only have a 1-second resolution, it's possible that multiple lines
271
- // could have the same timestamp, if so, append
272
- $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
273
- } else {
274
- $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
275
- }
276
- }
277
- }
278
- }
279
- $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
280
- if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
281
- ksort($Lyrics3data['synchedlyrics']);
282
- }
283
- return true;
284
- }
285
-
286
- public function IntString2Bool($char) {
287
- if ($char == '1') {
288
- return true;
289
- } elseif ($char == '0') {
290
- return false;
291
- }
292
- return null;
293
- }
294
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/write.id3v1.php DELETED
@@ -1,136 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- // //
10
- // write.id3v1.php //
11
- // module for writing ID3v1 tags //
12
- // dependencies: module.tag.id3v1.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
17
-
18
- class getid3_write_id3v1
19
- {
20
- public $filename;
21
- public $filesize;
22
- public $tag_data;
23
- public $warnings = array(); // any non-critical errors will be stored here
24
- public $errors = array(); // any critical errors will be stored here
25
-
26
- public function getid3_write_id3v1() {
27
- return true;
28
- }
29
-
30
- public function WriteID3v1() {
31
- // File MUST be writeable - CHMOD(646) at least
32
- if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
33
- $this->setRealFileSize();
34
- if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
35
- $this->errors[] = 'Unable to WriteID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
36
- return false;
37
- }
38
- if ($fp_source = fopen($this->filename, 'r+b')) {
39
- fseek($fp_source, -128, SEEK_END);
40
- if (fread($fp_source, 3) == 'TAG') {
41
- fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
42
- } else {
43
- fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
44
- }
45
- $this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
46
-
47
- $new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
48
- (isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''),
49
- (isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''),
50
- (isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''),
51
- (isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''),
52
- (isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
53
- (isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
54
- (isset($this->tag_data['track'] ) ? $this->tag_data['track'] : ''));
55
- fwrite($fp_source, $new_id3v1_tag_data, 128);
56
- fclose($fp_source);
57
- return true;
58
-
59
- } else {
60
- $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
61
- return false;
62
- }
63
- }
64
- $this->errors[] = 'File is not writeable: '.$this->filename;
65
- return false;
66
- }
67
-
68
- public function FixID3v1Padding() {
69
- // ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
70
- // This function rewrites the ID3v1 tag with correct padding
71
-
72
- // Initialize getID3 engine
73
- $getID3 = new getID3;
74
- $getID3->option_tag_id3v2 = false;
75
- $getID3->option_tag_apetag = false;
76
- $getID3->option_tags_html = false;
77
- $getID3->option_extra_info = false;
78
- $getID3->option_tag_id3v1 = true;
79
- $ThisFileInfo = $getID3->analyze($this->filename);
80
- if (isset($ThisFileInfo['tags']['id3v1'])) {
81
- foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
82
- $id3v1data[$key] = implode(',', $value);
83
- }
84
- $this->tag_data = $id3v1data;
85
- return $this->WriteID3v1();
86
- }
87
- return false;
88
- }
89
-
90
- public function RemoveID3v1() {
91
- // File MUST be writeable - CHMOD(646) at least
92
- if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
93
- $this->setRealFileSize();
94
- if (($this->filesize <= 0) || !getid3_lib::intValueSupported($this->filesize)) {
95
- $this->errors[] = 'Unable to RemoveID3v1('.$this->filename.') because filesize ('.$this->filesize.') is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
96
- return false;
97
- }
98
- if ($fp_source = fopen($this->filename, 'r+b')) {
99
-
100
- fseek($fp_source, -128, SEEK_END);
101
- if (fread($fp_source, 3) == 'TAG') {
102
- ftruncate($fp_source, $this->filesize - 128);
103
- } else {
104
- // no ID3v1 tag to begin with - do nothing
105
- }
106
- fclose($fp_source);
107
- return true;
108
-
109
- } else {
110
- $this->errors[] = 'Could not fopen('.$this->filename.', "r+b")';
111
- }
112
- } else {
113
- $this->errors[] = $this->filename.' is not writeable';
114
- }
115
- return false;
116
- }
117
-
118
- public function setRealFileSize() {
119
- if (PHP_INT_MAX > 2147483647) {
120
- $this->filesize = filesize($this->filename);
121
- return true;
122
- }
123
- // 32-bit PHP will not return correct values for filesize() if file is >=2GB
124
- // but getID3->analyze() has workarounds to get actual filesize
125
- $getID3 = new getID3;
126
- $getID3->option_tag_id3v1 = false;
127
- $getID3->option_tag_id3v2 = false;
128
- $getID3->option_tag_apetag = false;
129
- $getID3->option_tags_html = false;
130
- $getID3->option_extra_info = false;
131
- $ThisFileInfo = $getID3->analyze($this->filename);
132
- $this->filesize = $ThisFileInfo['filesize'];
133
- return true;
134
- }
135
-
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/getid3/write.id3v2.php DELETED
@@ -1,2049 +0,0 @@
1
- <?php
2
- /////////////////////////////////////////////////////////////////
3
- /// getID3() by James Heinrich <info@getid3.org> //
4
- // available at http://getid3.sourceforge.net //
5
- // or http://www.getid3.org //
6
- /////////////////////////////////////////////////////////////////
7
- // See readme.txt for more details //
8
- /////////////////////////////////////////////////////////////////
9
- /// //
10
- // write.id3v2.php //
11
- // module for writing ID3v2 tags //
12
- // dependencies: module.tag.id3v2.php //
13
- // ///
14
- /////////////////////////////////////////////////////////////////
15
-
16
- getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
17
-
18
- class getid3_write_id3v2
19
- {
20
- public $filename;
21
- public $tag_data;
22
- public $fread_buffer_size = 32768; // read buffer size in bytes
23
- public $paddedlength = 4096; // minimum length of ID3v2 tag in bytes
24
- public $majorversion = 3; // ID3v2 major version (2, 3 (recommended), 4)
25
- public $minorversion = 0; // ID3v2 minor version - always 0
26
- public $merge_existing_data = false; // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
27
- public $id3v2_default_encodingid = 0; // default text encoding (ISO-8859-1) if not explicitly passed
28
- public $id3v2_use_unsynchronisation = false; // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it.
29
- public $warnings = array(); // any non-critical errors will be stored here
30
- public $errors = array(); // any critical errors will be stored here
31
-
32
- public function getid3_write_id3v2() {
33
- return true;
34
- }
35
-
36
- public function WriteID3v2() {
37
- // File MUST be writeable - CHMOD(646) at least. It's best if the
38
- // directory is also writeable, because that method is both faster and less susceptible to errors.
39
-
40
- if (!empty($this->filename) && (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename))))) {
41
- // Initialize getID3 engine
42
- $getID3 = new getID3;
43
- $OldThisFileInfo = $getID3->analyze($this->filename);
44
- if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
45
- $this->errors[] = 'Unable to write ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
46
- fclose($fp_source);
47
- return false;
48
- }
49
- if ($this->merge_existing_data) {
50
- // merge with existing data
51
- if (!empty($OldThisFileInfo['id3v2'])) {
52
- $this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
53
- }
54
- }
55
- $this->paddedlength = (isset($OldThisFileInfo['id3v2']['headerlength']) ? max($OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength) : $this->paddedlength);
56
-
57
- if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
58
-
59
- if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
60
-
61
- // best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary)
62
- if (file_exists($this->filename)) {
63
-
64
- if (is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'r+b'))) {
65
- rewind($fp);
66
- fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
67
- fclose($fp);
68
- } else {
69
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
70
- }
71
-
72
- } else {
73
-
74
- if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'wb'))) {
75
- rewind($fp);
76
- fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
77
- fclose($fp);
78
- } else {
79
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "wb")';
80
- }
81
-
82
- }
83
-
84
- } else {
85
-
86
- if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
87
- if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
88
- if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
89
-
90
- fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
91
-
92
- rewind($fp_source);
93
- if (!empty($OldThisFileInfo['avdataoffset'])) {
94
- fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
95
- }
96
-
97
- while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
98
- fwrite($fp_temp, $buffer, strlen($buffer));
99
- }
100
-
101
- fclose($fp_temp);
102
- fclose($fp_source);
103
- copy($tempfilename, $this->filename);
104
- unlink($tempfilename);
105
- return true;
106
-
107
- } else {
108
- $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
109
- }
110
- fclose($fp_source);
111
-
112
- } else {
113
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")';
114
- }
115
- }
116
- return false;
117
-
118
- }
119
-
120
- } else {
121
-
122
- $this->errors[] = '$this->GenerateID3v2Tag() failed';
123
-
124
- }
125
-
126
- if (!empty($this->errors)) {
127
- return false;
128
- }
129
- return true;
130
- } else {
131
- $this->errors[] = 'WriteID3v2() failed: !is_writeable('.$this->filename.')';
132
- }
133
- return false;
134
- }
135
-
136
- public function RemoveID3v2() {
137
- // File MUST be writeable - CHMOD(646) at least. It's best if the
138
- // directory is also writeable, because that method is both faster and less susceptible to errors.
139
- if (is_writeable(dirname($this->filename))) {
140
-
141
- // preferred method - only one copying operation, minimal chance of corrupting
142
- // original file if script is interrupted, but required directory to be writeable
143
- if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
144
-
145
- // Initialize getID3 engine
146
- $getID3 = new getID3;
147
- $OldThisFileInfo = $getID3->analyze($this->filename);
148
- if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
149
- $this->errors[] = 'Unable to remove ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
150
- fclose($fp_source);
151
- return false;
152
- }
153
- rewind($fp_source);
154
- if ($OldThisFileInfo['avdataoffset'] !== false) {
155
- fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
156
- }
157
- if (is_writable($this->filename) && is_file($this->filename) && ($fp_temp = fopen($this->filename.'getid3tmp', 'w+b'))) {
158
- while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
159
- fwrite($fp_temp, $buffer, strlen($buffer));
160
- }
161
- fclose($fp_temp);
162
- } else {
163
- $this->errors[] = 'Could not fopen("'.$this->filename.'getid3tmp", "w+b")';
164
- }
165
- fclose($fp_source);
166
- } else {
167
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")';
168
- }
169
- if (file_exists($this->filename)) {
170
- unlink($this->filename);
171
- }
172
- rename($this->filename.'getid3tmp', $this->filename);
173
-
174
- } elseif (is_writable($this->filename)) {
175
-
176
- // less desirable alternate method - double-copies the file, overwrites original file
177
- // and could corrupt source file if the script is interrupted or an error occurs.
178
- if (is_readable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'rb'))) {
179
-
180
- // Initialize getID3 engine
181
- $getID3 = new getID3;
182
- $OldThisFileInfo = $getID3->analyze($this->filename);
183
- if (!getid3_lib::intValueSupported($OldThisFileInfo['filesize'])) {
184
- $this->errors[] = 'Unable to remove ID3v2 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB';
185
- fclose($fp_source);
186
- return false;
187
- }
188
- rewind($fp_source);
189
- if ($OldThisFileInfo['avdataoffset'] !== false) {
190
- fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
191
- }
192
- if ($fp_temp = tmpfile()) {
193
- while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
194
- fwrite($fp_temp, $buffer, strlen($buffer));
195
- }
196
- fclose($fp_source);
197
- if (is_writable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'wb'))) {
198
- rewind($fp_temp);
199
- while ($buffer = fread($fp_temp, $this->fread_buffer_size)) {
200
- fwrite($fp_source, $buffer, strlen($buffer));
201
- }
202
- fseek($fp_temp, -128, SEEK_END);
203
- fclose($fp_source);
204
- } else {
205
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "wb")';
206
- }
207
- fclose($fp_temp);
208
- } else {
209
- $this->errors[] = 'Could not create tmpfile()';
210
- }
211
- } else {
212
- $this->errors[] = 'Could not fopen("'.$this->filename.'", "rb")';
213
- }
214
-
215
- } else {
216
-
217
- $this->errors[] = 'Directory and file both not writeable';
218
-
219
- }
220
-
221
- if (!empty($this->errors)) {
222
- return false;
223
- }
224
- return true;
225
- }
226
-
227
-
228
- public function GenerateID3v2TagFlags($flags) {
229
- switch ($this->majorversion) {
230
- case 4:
231
- // %abcd0000
232
- $flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
233
- $flag .= (!empty($flags['extendedheader'] ) ? '1' : '0'); // b - Extended header
234
- $flag .= (!empty($flags['experimental'] ) ? '1' : '0'); // c - Experimental indicator
235
- $flag .= (!empty($flags['footer'] ) ? '1' : '0'); // d - Footer present
236
- $flag .= '0000';
237
- break;
238
-
239
- case 3:
240
- // %abc00000
241
- $flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
242
- $flag .= (!empty($flags['extendedheader'] ) ? '1' : '0'); // b - Extended header
243
- $flag .= (!empty($flags['experimental'] ) ? '1' : '0'); // c - Experimental indicator
244
- $flag .= '00000';
245
- break;
246
-
247
- case 2:
248
- // %ab000000
249
- $flag = (!empty($flags['unsynchronisation']) ? '1' : '0'); // a - Unsynchronisation
250
- $flag .= (!empty($flags['compression'] ) ? '1' : '0'); // b - Compression
251
- $flag .= '000000';
252
- break;
253
-
254
- default:
255
- return false;
256
- break;
257
- }
258
- return chr(bindec($flag));
259
- }
260
-
261
-
262
- public function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
263
- switch ($this->majorversion) {
264
- case 4:
265
- // %0abc0000 %0h00kmnp
266
- $flag1 = '0';
267
- $flag1 .= $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard)
268
- $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
269
- $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only)
270
- $flag1 .= '0000';
271
-
272
- $flag2 = '0';
273
- $flag2 .= $GroupingIdentity ? '1' : '0'; // h - Grouping identity (true == contains group information)
274
- $flag2 .= '00';
275
- $flag2 .= $Compression ? '1' : '0'; // k - Compression (true == compressed)
276
- $flag2 .= $Encryption ? '1' : '0'; // m - Encryption (true == encrypted)
277
- $flag2 .= $Unsynchronisation ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised)
278
- $flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added)
279
- break;
280
-
281
- case 3:
282
- // %abc00000 %ijk00000
283
- $flag1 = $TagAlter ? '1' : '0'; // a - Tag alter preservation (true == discard)
284
- $flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
285
- $flag1 .= $ReadOnly ? '1' : '0'; // c - Read only (true == read only)
286
- $flag1 .= '00000';
287
-
288
- $flag2 = $Compression ? '1' : '0'; // i - Compression (true == compressed)
289
- $flag2 .= $Encryption ? '1' : '0'; // j - Encryption (true == encrypted)
290
- $flag2 .= $GroupingIdentity ? '1' : '0'; // k - Grouping identity (true == contains group information)
291
- $flag2 .= '00000';
292
- break;
293
-
294
- default:
295
- return false;
296
- break;
297
-
298
- }
299
- return chr(bindec($flag1)).chr(bindec($flag2));
300
- }
301
-
302
- public function GenerateID3v2FrameData($frame_name, $source_data_array) {
303
- if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
304
- return false;
305
- }
306
- $framedata = '';
307
-
308
- if (($this->majorversion < 3) || ($this->majorversion > 4)) {
309
-
310
- $this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
311
-
312
- } else { // $this->majorversion 3 or 4
313
-
314
- switch ($frame_name) {
315
- case 'UFID':
316
- // 4.1 UFID Unique file identifier
317
- // Owner identifier <text string> $00
318
- // Identifier <up to 64 bytes binary data>
319
- if (strlen($source_data_array['data']) > 64) {
320
- $this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
321
- } else {
322
- $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
323
- $framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
324
- }
325
- break;
326
-
327
- case 'TXXX':
328
- // 4.2.2 TXXX User defined text information frame
329
- // Text encoding $xx
330
- // Description <text string according to encoding> $00 (00)
331
- // Value <text string according to encoding>
332
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
333
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
334
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
335
- } else {
336
- $framedata .= chr($source_data_array['encodingid']);
337
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
338
- $framedata .= $source_data_array['data'];
339
- }
340
- break;
341
-
342
- case 'WXXX':
343
- // 4.3.2 WXXX User defined URL link frame
344
- // Text encoding $xx
345
- // Description <text string according to encoding> $00 (00)
346
- // URL <text string>
347
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
348
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
349
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
350
- } elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
351
- //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
352
- // probably should be an error, need to rewrite IsValidURL() to handle other encodings
353
- $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
354
- } else {
355
- $framedata .= chr($source_data_array['encodingid']);
356
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
357
- $framedata .= $source_data_array['data'];
358
- }
359
- break;
360
-
361
- case 'IPLS':
362
- // 4.4 IPLS Involved people list (ID3v2.3 only)
363
- // Text encoding $xx
364
- // People list strings <textstrings>
365
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
366
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
367
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
368
- } else {
369
- $framedata .= chr($source_data_array['encodingid']);
370
- $framedata .= $source_data_array['data'];
371
- }
372
- break;
373
-
374
- case 'MCDI':
375
- // 4.4 MCDI Music CD identifier
376
- // CD TOC <binary data>
377
- $framedata .= $source_data_array['data'];
378
- break;
379
-
380
- case 'ETCO':
381
- // 4.5 ETCO Event timing codes
382
- // Time stamp format $xx
383
- // Where time stamp format is:
384
- // $01 (32-bit value) MPEG frames from beginning of file
385
- // $02 (32-bit value) milliseconds from beginning of file
386
- // Followed by a list of key events in the following format:
387
- // Type of event $xx
388
- // Time stamp $xx (xx ...)
389
- // The 'Time stamp' is set to zero if directly at the beginning of the sound
390
- // or after the previous event. All events MUST be sorted in chronological order.
391
- if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
392
- $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
393
- } else {
394
- $framedata .= chr($source_data_array['timestampformat']);
395
- foreach ($source_data_array as $key => $val) {
396
- if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
397
- $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
398
- } elseif (($key != 'timestampformat') && ($key != 'flags')) {
399
- if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
400
- // The 'Time stamp' is set to zero if directly at the beginning of the sound
401
- // or after the previous event. All events MUST be sorted in chronological order.
402
- $this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
403
- } else {
404
- $framedata .= chr($val['typeid']);
405
- $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
406
- }
407
- }
408
- }
409
- }
410
- break;
411
-
412
- case 'MLLT':
413
- // 4.6 MLLT MPEG location lookup table
414
- // MPEG frames between reference $xx xx
415
- // Bytes between reference $xx xx xx
416
- // Milliseconds between reference $xx xx xx
417
- // Bits for bytes deviation $xx
418
- // Bits for milliseconds dev. $xx
419
- // Then for every reference the following data is included;
420
- // Deviation in bytes %xxx....
421
- // Deviation in milliseconds %xxx....
422
- if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
423
- $framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
424
- } else {
425
- $this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
426
- }
427
- if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
428
- $framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
429
- } else {
430
- $this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
431
- }
432
- if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
433
- $framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
434
- } else {
435
- $this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
436
- }
437
- if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
438
- if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
439
- $framedata .= chr($source_data_array['bitsforbytesdeviation']);
440
- } else {
441
- $this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
442
- }
443
- } else {
444
- $this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
445
- }
446
- if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
447
- if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
448
- $framedata .= chr($source_data_array['bitsformsdeviation']);
449
- } else {
450
- $this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
451
- }
452
- } else {
453
- $this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
454
- }
455
- foreach ($source_data_array as $key => $val) {
456
- if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
457
- $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
458
- $unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']), $source_data_array['bitsformsdeviation'], '0', STR_PAD_LEFT);
459
- }
460
- }
461
- for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
462
- $highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
463
- $lownibble = bindec(substr($unwrittenbitstream, $i + 4, 4));
464
- $framedata .= chr($highnibble & $lownibble);
465
- }
466
- break;
467
-
468
- case 'SYTC':
469
- // 4.7 SYTC Synchronised tempo codes
470
- // Time stamp format $xx
471
- // Tempo data <binary data>
472
- // Where time stamp format is:
473
- // $01 (32-bit value) MPEG frames from beginning of file
474
- // $02 (32-bit value) milliseconds from beginning of file
475
- if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
476
- $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
477
- } else {
478
- $framedata .= chr($source_data_array['timestampformat']);
479
- foreach ($source_data_array as $key => $val) {
480
- if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
481
- $this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
482
- } elseif (($key != 'timestampformat') && ($key != 'flags')) {
483
- if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
484
- $this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
485
- } else {
486
- if ($val['tempo'] > 255) {
487
- $framedata .= chr(255);
488
- $val['tempo'] -= 255;
489
- }
490
- $framedata .= chr($val['tempo']);
491
- $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
492
- }
493
- }
494
- }
495
- }
496
- break;
497
-
498
- case 'USLT':
499
- // 4.8 USLT Unsynchronised lyric/text transcription
500
- // Text encoding $xx
501
- // Language $xx xx xx
502
- // Content descriptor <text string according to encoding> $00 (00)
503
- // Lyrics/text <full text string according to encoding>
504
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
505
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
506
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
507
- } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
508
- $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
509
- } else {
510
- $framedata .= chr($source_data_array['encodingid']);
511
- $framedata .= strtolower($source_data_array['language']);
512
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
513
- $framedata .= $source_data_array['data'];
514
- }
515
- break;
516
-
517
- case 'SYLT':
518
- // 4.9 SYLT Synchronised lyric/text
519
- // Text encoding $xx
520
- // Language $xx xx xx
521
- // Time stamp format $xx
522
- // $01 (32-bit value) MPEG frames from beginning of file
523
- // $02 (32-bit value) milliseconds from beginning of file
524
- // Content type $xx
525
- // Content descriptor <text string according to encoding> $00 (00)
526
- // Terminated text to be synced (typically a syllable)
527
- // Sync identifier (terminator to above string) $00 (00)
528
- // Time stamp $xx (xx ...)
529
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
530
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
531
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
532
- } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
533
- $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
534
- } elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
535
- $this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
536
- } elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
537
- $this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
538
- } elseif (!is_array($source_data_array['data'])) {
539
- $this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
540
- } else {
541
- $framedata .= chr($source_data_array['encodingid']);
542
- $framedata .= strtolower($source_data_array['language']);
543
- $framedata .= chr($source_data_array['timestampformat']);
544
- $framedata .= chr($source_data_array['contenttypeid']);
545
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
546
- ksort($source_data_array['data']);
547
- foreach ($source_data_array['data'] as $key => $val) {
548
- $framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
549
- $framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
550
- }
551
- }
552
- break;
553
-
554
- case 'COMM':
555
- // 4.10 COMM Comments
556
- // Text encoding $xx
557
- // Language $xx xx xx
558
- // Short content descrip. <text string according to encoding> $00 (00)
559
- // The actual text <full text string according to encoding>
560
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
561
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
562
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
563
- } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
564
- $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
565
- } else {
566
- $framedata .= chr($source_data_array['encodingid']);
567
- $framedata .= strtolower($source_data_array['language']);
568
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
569
- $framedata .= $source_data_array['data'];
570
- }
571
- break;
572
-
573
- case 'RVA2':
574
- // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
575
- // Identification <text string> $00
576
- // The 'identification' string is used to identify the situation and/or
577
- // device where this adjustment should apply. The following is then
578
- // repeated for every channel:
579
- // Type of channel $xx
580
- // Volume adjustment $xx xx
581
- // Bits representing peak $xx
582
- // Peak volume $xx (xx ...)
583
- $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
584
- foreach ($source_data_array as $key => $val) {
585
- if ($key != 'description') {
586
- $framedata .= chr($val['channeltypeid']);
587
- $framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
588
- if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
589
- $framedata .= chr($val['bitspeakvolume']);
590
- if ($val['bitspeakvolume'] > 0) {
591
- $framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
592
- }
593
- } else {
594
- $this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
595
- }
596
- }
597
- }
598
- break;
599
-
600
- case 'RVAD':
601
- // 4.12 RVAD Relative volume adjustment (ID3v2.3 only)
602
- // Increment/decrement %00fedcba
603
- // Bits used for volume descr. $xx
604
- // Relative volume change, right $xx xx (xx ...) // a
605
- // Relative volume change, left $xx xx (xx ...) // b
606
- // Peak volume right $xx xx (xx ...)
607
- // Peak volume left $xx xx (xx ...)
608
- // Relative volume change, right back $xx xx (xx ...) // c
609
- // Relative volume change, left back $xx xx (xx ...) // d
610
- // Peak volume right back $xx xx (xx ...)
611
- // Peak volume left back $xx xx (xx ...)
612
- // Relative volume change, center $xx xx (xx ...) // e
613
- // Peak volume center $xx xx (xx ...)
614
- // Relative volume change, bass $xx xx (xx ...) // f
615
- // Peak volume bass $xx xx (xx ...)
616
- if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
617
- $this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
618
- } else {
619
- $incdecflag .= '00';
620
- $incdecflag .= $source_data_array['incdec']['right'] ? '1' : '0'; // a - Relative volume change, right
621
- $incdecflag .= $source_data_array['incdec']['left'] ? '1' : '0'; // b - Relative volume change, left
622
- $incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
623
- $incdecflag .= $source_data_array['incdec']['leftrear'] ? '1' : '0'; // d - Relative volume change, left back
624
- $incdecflag .= $source_data_array['incdec']['center'] ? '1' : '0'; // e - Relative volume change, center
625
- $incdecflag .= $source_data_array['incdec']['bass'] ? '1' : '0'; // f - Relative volume change, bass
626
- $framedata .= chr(bindec($incdecflag));
627
- $framedata .= chr($source_data_array['bitsvolume']);
628
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
629
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
630
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
631
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'], ceil($source_data_array['bitsvolume'] / 8), false);
632
- if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
633
- $source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
634
- $source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
635
- $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
636
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
637
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
638
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
639
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'], ceil($source_data_array['bitsvolume']/8), false);
640
- }
641
- if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
642
- $source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
643
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
644
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
645
- }
646
- if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
647
- $framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
648
- $framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
649
- }
650
- }
651
- break;
652
-
653
- case 'EQU2':
654
- // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only)
655
- // Interpolation method $xx
656
- // $00 Band
657
- // $01 Linear
658
- // Identification <text string> $00
659
- // The following is then repeated for every adjustment point
660
- // Frequency $xx xx
661
- // Volume adjustment $xx xx
662
- if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
663
- $this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
664
- } else {
665
- $framedata .= chr($source_data_array['interpolationmethod']);
666
- $framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
667
- foreach ($source_data_array['data'] as $key => $val) {
668
- $framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
669
- $framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
670
- }
671
- }
672
- break;
673
-
674
- case 'EQUA':
675
- // 4.12 EQUA Equalisation (ID3v2.3 only)
676
- // Adjustment bits $xx
677
- // This is followed by 2 bytes + ('adjustment bits' rounded up to the
678
- // nearest byte) for every equalisation band in the following format,
679
- // giving a frequency range of 0 - 32767Hz:
680
- // Increment/decrement %x (MSB of the Frequency)
681
- // Frequency (lower 15 bits)
682
- // Adjustment $xx (xx ...)
683
- if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
684
- $this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
685
- } else {
686
- $framedata .= chr($source_data_array['adjustmentbits']);
687
- foreach ($source_data_array as $key => $val) {
688
- if ($key != 'bitsvolume') {
689
- if (($key > 32767) || ($key < 0)) {
690
- $this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
691
- } else {
692
- if ($val >= 0) {
693
- // put MSB of frequency to 1 if increment, 0 if decrement
694
- $key |= 0x8000;
695
- }
696
- $framedata .= getid3_lib::BigEndian2String($key, 2, false);
697
- $framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
698
- }
699
- }
700
- }
701
- }
702
- break;
703
-
704
- case 'RVRB':
705
- // 4.13 RVRB Reverb
706
- // Reverb left (ms) $xx xx
707
- // Reverb right (ms) $xx xx
708
- // Reverb bounces, left $xx
709
- // Reverb bounces, right $xx
710
- // Reverb feedback, left to left $xx
711
- // Reverb feedback, left to right $xx
712
- // Reverb feedback, right to right $xx
713
- // Reverb feedback, right to left $xx
714
- // Premix left to right $xx
715
- // Premix right to left $xx
716
- if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
717
- $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
718
- } elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
719
- $this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
720
- } elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
721
- $this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
722
- } elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
723
- $this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
724
- } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
725
- $this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
726
- } elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
727
- $this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
728
- } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
729
- $this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
730
- } elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
731
- $this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
732
- } elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
733
- $this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
734
- } elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
735
- $this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
736
- } else {
737
- $framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
738
- $framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
739
- $framedata .= chr($source_data_array['bouncesL']);
740
- $framedata .= chr($source_data_array['bouncesR']);
741
- $framedata .= chr($source_data_array['feedbackLL']);
742
- $framedata .= chr($source_data_array['feedbackLR']);
743
- $framedata .= chr($source_data_array['feedbackRR']);
744
- $framedata .= chr($source_data_array['feedbackRL']);
745
- $framedata .= chr($source_data_array['premixLR']);
746
- $framedata .= chr($source_data_array['premixRL']);
747
- }
748
- break;
749
-
750
- case 'APIC':
751
- // 4.14 APIC Attached picture
752
- // Text encoding $xx
753
- // MIME type <text string> $00
754
- // Picture type $xx
755
- // Description <text string according to encoding> $00 (00)
756
- // Picture data <binary data>
757
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
758
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
759
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
760
- } elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
761
- $this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
762
- } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
763
- $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
764
- } elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
765
- //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
766
- // probably should be an error, need to rewrite IsValidURL() to handle other encodings
767
- $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
768
- } else {
769
- $framedata .= chr($source_data_array['encodingid']);
770
- $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
771
- $framedata .= chr($source_data_array['picturetypeid']);
772
- $framedata .= (!empty($source_data_array['description']) ? $source_data_array['description'] : '').getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
773
- $framedata .= $source_data_array['data'];
774
- }
775
- break;
776
-
777
- case 'GEOB':
778
- // 4.15 GEOB General encapsulated object
779
- // Text encoding $xx
780
- // MIME type <text string> $00
781
- // Filename <text string according to encoding> $00 (00)
782
- // Content description <text string according to encoding> $00 (00)
783
- // Encapsulated object <binary data>
784
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
785
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
786
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
787
- } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
788
- $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
789
- } elseif (!$source_data_array['description']) {
790
- $this->errors[] = 'Missing Description in '.$frame_name;
791
- } else {
792
- $framedata .= chr($source_data_array['encodingid']);
793
- $framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
794
- $framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
795
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
796
- $framedata .= $source_data_array['data'];
797
- }
798
- break;
799
-
800
- case 'PCNT':
801
- // 4.16 PCNT Play counter
802
- // When the counter reaches all one's, one byte is inserted in
803
- // front of the counter thus making the counter eight bits bigger
804
- // Counter $xx xx xx xx (xx ...)
805
- $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
806
- break;
807
-
808
- case 'POPM':
809
- // 4.17 POPM Popularimeter
810
- // When the counter reaches all one's, one byte is inserted in
811
- // front of the counter thus making the counter eight bits bigger
812
- // Email to user <text string> $00
813
- // Rating $xx
814
- // Counter $xx xx xx xx (xx ...)
815
- if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
816
- $this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
817
- } elseif (!IsValidEmail($source_data_array['email'])) {
818
- $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
819
- } else {
820
- $framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
821
- $framedata .= chr($source_data_array['rating']);
822
- $framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
823
- }
824
- break;
825
-
826
- case 'RBUF':
827
- // 4.18 RBUF Recommended buffer size
828
- // Buffer size $xx xx xx
829
- // Embedded info flag %0000000x
830
- // Offset to next tag $xx xx xx xx
831
- if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
832
- $this->errors[] = 'Invalid Buffer Size in '.$frame_name;
833
- } elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
834
- $this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
835
- } else {
836
- $framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
837
- $flag .= '0000000';
838
- $flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
839
- $framedata .= chr(bindec($flag));
840
- $framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
841
- }
842
- break;
843
-
844
- case 'AENC':
845
- // 4.19 AENC Audio encryption
846
- // Owner identifier <text string> $00
847
- // Preview start $xx xx
848
- // Preview length $xx xx
849
- // Encryption info <binary data>
850
- if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
851
- $this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
852
- } elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
853
- $this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
854
- } else {
855
- $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
856
- $framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
857
- $framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
858
- $framedata .= $source_data_array['encryptioninfo'];
859
- }
860
- break;
861
-
862
- case 'LINK':
863
- // 4.20 LINK Linked information
864
- // Frame identifier $xx xx xx xx
865
- // URL <text string> $00
866
- // ID and additional data <text string(s)>
867
- if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
868
- $this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
869
- } elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
870
- //$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
871
- // probably should be an error, need to rewrite IsValidURL() to handle other encodings
872
- $this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
873
- } elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
874
- $this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
875
- } elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
876
- $this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
877
- } elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
878
- $this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
879
- } elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
880
- $this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
881
- } else {
882
- $framedata .= $source_data_array['frameid'];
883
- $framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
884
- switch ($source_data_array['frameid']) {
885
- case 'COMM':
886
- case 'SYLT':
887
- case 'USLT':
888
- case 'PRIV':
889
- case 'USER':
890
- case 'AENC':
891
- case 'APIC':
892
- case 'GEOB':
893
- case 'TXXX':
894
- $framedata .= $source_data_array['additionaldata'];
895
- break;
896
- case 'ASPI':
897
- case 'ETCO':
898
- case 'EQU2':
899
- case 'MCID':
900
- case 'MLLT':
901
- case 'OWNE':
902
- case 'RVA2':
903
- case 'RVRB':
904
- case 'SYTC':
905
- case 'IPLS':
906
- case 'RVAD':
907
- case 'EQUA':
908
- // no additional data required
909
- break;
910
- case 'RBUF':
911
- if ($this->majorversion == 3) {
912
- // no additional data required
913
- } else {
914
- $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
915
- }
916
-
917
- default:
918
- if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
919
- // no additional data required
920
- } else {
921
- $this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
922
- }
923
- break;
924
- }
925
- }
926
- break;
927
-
928
- case 'POSS':
929
- // 4.21 POSS Position synchronisation frame (ID3v2.3+ only)
930
- // Time stamp format $xx
931
- // Position $xx (xx ...)
932
- if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
933
- $this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
934
- } elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
935
- $this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
936
- } else {
937
- $framedata .= chr($source_data_array['timestampformat']);
938
- $framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
939
- }
940
- break;
941
-
942
- case 'USER':
943
- // 4.22 USER Terms of use (ID3v2.3+ only)
944
- // Text encoding $xx
945
- // Language $xx xx xx
946
- // The actual text <text string according to encoding>
947
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
948
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
949
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
950
- } elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
951
- $this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
952
- } else {
953
- $framedata .= chr($source_data_array['encodingid']);
954
- $framedata .= strtolower($source_data_array['language']);
955
- $framedata .= $source_data_array['data'];
956
- }
957
- break;
958
-
959
- case 'OWNE':
960
- // 4.23 OWNE Ownership frame (ID3v2.3+ only)
961
- // Text encoding $xx
962
- // Price paid <text string> $00
963
- // Date of purch. <text string>
964
- // Seller <text string according to encoding>
965
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
966
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
967
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
968
- } elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
969
- $this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
970
- } elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
971
- $this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
972
- } else {
973
- $framedata .= chr($source_data_array['encodingid']);
974
- $framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
975
- $framedata .= $source_data_array['purchasedate'];
976
- $framedata .= $source_data_array['seller'];
977
- }
978
- break;
979
-
980
- case 'COMR':
981
- // 4.24 COMR Commercial frame (ID3v2.3+ only)
982
- // Text encoding $xx
983
- // Price string <text string> $00
984
- // Valid until <text string>
985
- // Contact URL <text string> $00
986
- // Received as $xx
987
- // Name of seller <text string according to encoding> $00 (00)
988
- // Description <text string according to encoding> $00 (00)
989
- // Picture MIME type <string> $00
990
- // Seller logo <binary data>
991
- $source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
992
- if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
993
- $this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
994
- } elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
995
- $this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
996
- } elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
997
- $this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
998
- } elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
999
- $this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
1000
- } elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
1001
- $this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
1002
- } else {
1003
- $framedata .= chr($source_data_array['encodingid']);
1004
- unset($pricestring);
1005
- foreach ($source_data_array['price'] as $key => $val) {
1006
- if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
1007
- $pricestrings[] = $key.$val['value'];
1008
- } else {
1009
- $this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
1010
- }
1011
- }
1012
- $framedata .= implode('/', $pricestrings);
1013
- $framedata .= $source_data_array['pricevaliduntil'];
1014
- $framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
1015
- $framedata .= chr($source_data_array['receivedasid']);
1016
- $framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
1017
- $framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
1018
- $framedata .= $source_data_array['mime']."\x00";
1019
- $framedata .= $source_data_array['logo'];
1020
- }
1021
- break;
1022
-
1023
- case 'ENCR':
1024
- // 4.25 ENCR Encryption method registration (ID3v2.3+ only)
1025
- // Owner identifier <text string> $00
1026
- // Method symbol $xx
1027
- // Encryption data <binary data>
1028
- if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
1029
- $this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
1030
- } else {
1031
- $framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
1032
- $framedata .= ord($source_data_array['methodsymbol']);
1033
- $framedata .= $source_data_array['data'];
1034
- }
1035
- break;
1036
-
1037
- case 'GRID':
1038
- // 4.26 GRID Group identification registration (ID3v2.3+ only)
1039
- // Owner identifier <text string> $00
1040
- // Group symbol