Ticket #10529: mythweb-fixes-0.26-html5-video.patch
File mythweb-fixes-0.26-html5-video.patch, 16.1 KB (added by , 11 years ago) |
---|
-
includes/utils.php
includes/utils.php | 2 + modules/stream/handler.pl | 29 ++++++- modules/stream/stream_ogv.pl | 162 +++++++++++++++++++++++++++++++++++++ modules/stream/stream_webm.pl | 162 +++++++++++++++++++++++++++++++++++++ modules/tv/tmpl/default/detail.php | 12 +++ mythweb.conf.apache | 4 + 6 files changed, 367 insertions(+), 4 deletions(-) diff --git a/includes/utils.php b/includes/utils.php index 472a862..77727f3 100644
a b 242 242 case 'flvp': return "$url.flvp"; 243 243 case 'flv' : return "$url.flv"; 244 244 case 'mp4' : return "$url.mp4"; 245 case 'ogv' : return "$url.ogv"; 246 case 'webm': return "$url.webm"; 245 247 } 246 248 // No more dsmyth filters, so return the URL no matter what the browser is. 247 249 return $url; -
modules/stream/handler.pl
diff --git a/modules/stream/handler.pl b/modules/stream/handler.pl index 9015c62..f449624 100755
a b 12 12 13 13 require "modules/$Path[0]/tv.pl"; 14 14 15 # Use the MythTV Services API URL if the $filename URL is not local 15 16 unless ($filename) { 16 print header(), 17 "$basename does not exist in any recognized storage group directories for this host."; 18 exit; 17 # Retrieve the backend IP and port 18 $sh = $dbh->prepare('SELECT data FROM settings WHERE value=?'); 19 $sh->execute('BackendServerIP'); 20 my ($backend_server_ip) = $sh->fetchrow_array; 21 $sh->execute('BackendStatusPort'); 22 my ($backend_status_port) = $sh->fetchrow_array; 23 $sh->finish(); 24 25 # Reformat the recording start time 26 use HTTP::Date qw(time2isoz); 27 $starttime_isoz = time2isoz($starttime); 28 $starttime_isoz =~ s/ /T/g; 29 30 # Generate the MythTV Services API URL 31 $filename = "http://${backend_server_ip}:${backend_status_port}/Content/GetRecording?ChanId=${chanid}&StartTime=${starttime_isoz}"; 19 32 } 20 33 34 # HTML5 video/ogv 35 if ($ENV{'REQUEST_URI'} =~ /\.ogv$/i) { 36 require "modules/$Path[0]/stream_ogv.pl"; 37 } 38 # HTML5 video/webm 39 elsif ($ENV{'REQUEST_URI'} =~ /\.webm$/i) { 40 require "modules/$Path[0]/stream_webm.pl"; 41 } 21 42 # ASX mode? 22 if ($ENV{'REQUEST_URI'} =~ /\.asx$/i) {43 elsif ($ENV{'REQUEST_URI'} =~ /\.asx$/i) { 23 44 require "modules/$Path[0]/stream_asx.pl"; 24 45 } 25 46 # Flash? -
new file modules/stream/stream_ogv.pl
diff --git a/modules/stream/stream_ogv.pl b/modules/stream/stream_ogv.pl new file mode 100755 index 0000000..f901b07
- + 1 #!/usr/bin/perl 2 # 3 # MythWeb Streaming/Download module 4 # 5 # @url $URL$ 6 # @date $Date$ 7 # @version $Revision$ 8 # @author $Author$ 9 # 10 11 use POSIX qw(ceil floor); 12 13 # round to the nearest even integer 14 sub round_even { 15 my ($in) = @_; 16 my $n = floor($in); 17 return ($n % 2 == 0) ? $n : ceil($in); 18 } 19 20 our $ffmpeg_pid; 21 our $ffmpeg_pgid; 22 23 # Shutdown cleanup, of various types 24 $ffmpeg_pgid = setpgrp(0,0); 25 $SIG{'TERM'} = \&shutdown_handler; 26 $SIG{'PIPE'} = \&shutdown_handler; 27 END { 28 shutdown_handler(); 29 } 30 sub shutdown_handler { 31 kill(1, $ffmpeg_pid) if ($ffmpeg_pid); 32 kill(-1, $ffmpeg_pgid) if ($ffmpeg_pgid); 33 } 34 35 # Find ffmpeg 36 $ffmpeg = ''; 37 foreach my $path (split(/:/, $ENV{'PATH'}.':/usr/local/bin:/usr/bin'), '.') { 38 if (-e "$path/mythffmpeg") { 39 $ffmpeg = "$path/mythffmpeg"; 40 last; 41 } 42 if (-e "$path/ffmpeg") { 43 $ffmpeg = "$path/ffmpeg"; 44 last; 45 } 46 elsif ($^O eq 'darwin' && -e "$path/ffmpeg.app") { 47 $ffmpeg = "$path/ffmpeg.app"; 48 last; 49 } 50 } 51 52 # Load some conversion settings from the database 53 $sh = $dbh->prepare('SELECT data FROM settings WHERE value=? AND hostname IS NULL'); 54 $sh->execute('WebFLV_w'); 55 my ($width) = $sh->fetchrow_array; 56 $sh->execute('WebFLV_vb'); 57 my ($vbitrate) = $sh->fetchrow_array; 58 $sh->execute('WebFLV_ab'); 59 my ($abitrate) = $sh->fetchrow_array; 60 $sh->finish(); 61 # auto-detect height based on aspect ratio 62 $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' . 63 'AND starttime=FROM_UNIXTIME(?) AND type=30 ' . 64 'AND data IS NOT NULL ORDER BY mark LIMIT 1'); 65 $sh->execute($chanid,$starttime); 66 $x = $sh->fetchrow_array; # type = 30 67 $sh->finish(); 68 69 $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' . 70 'AND starttime=FROM_UNIXTIME(?) AND type=31 ' . 71 'AND data IS NOT NULL ORDER BY mark LIMIT 1'); 72 $sh->execute($chanid,$starttime); 73 $y = $sh->fetchrow_array if ($x); # type = 31 74 $sh->finish(); 75 76 if (!$x || !$y || $x <= 720) { # <=720 means SD 77 $sh = $dbh->prepare('SELECT recordedmarkup.type, ' . 78 'recordedmarkup.data '. 79 'FROM recordedmarkup ' . 80 'WHERE recordedmarkup.chanid = ? ' . 81 'AND recordedmarkup.starttime = FROM_UNIXTIME(?) ' . 82 'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' . 83 'GROUP BY recordedmarkup.type ' . 84 'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' . 85 ' FROM recordedmarkup AS rm ' . 86 ' WHERE rm.chanid = recordedmarkup.chanid ' . 87 ' AND rm.starttime = recordedmarkup.starttime ' . 88 ' AND rm.type IN (10, 11, 12, 13, 14) ' . 89 ' AND rm.mark > recordedmarkup.mark ' . 90 ' ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' . 91 'LIMIT 1'); 92 $sh->execute($chanid,$starttime); 93 $aspect = $sh->fetchrow_hashref; 94 $sh->finish(); 95 96 if( $aspect->{'type'} == 10 ) { 97 $x = $y = 1; 98 } elsif( $aspect->{'type'}== 11 ) { 99 $x = 4; $y = 3; 100 } elsif( $aspect->{'type'}== 12 ) { 101 $x = 16; $y = 9; 102 } elsif( $aspect->{'type'}== 13 ) { 103 $x = 2.21; $y = 1; 104 } elsif( $aspect->{'type'}== 14 ) { 105 $x = $aspect->{'data'}; $y = 10000; 106 } else { 107 $x = 4; $y = 3; 108 } 109 } 110 $height = round_even($width * ($y/$x)); 111 112 $width = 320 unless ($width && $width > 1); 113 $height = 240 unless ($height && $height > 1); 114 $vbitrate = 256 unless ($vbitrate && $vbitrate > 1); 115 $abitrate = 64 unless ($abitrate && $abitrate > 1); 116 117 my $ffmpeg_command = $ffmpeg 118 .' -y' 119 .' -i '.shell_escape("$filename") 120 .' -s '.shell_escape("${width}x${height}") 121 .' -g 30' 122 .' -r 24' 123 .' -f ogg' 124 .' -acodec libvorbis' 125 .' -vcodec libtheora' 126 .' -deinterlace' 127 .' -async 2' 128 .' -ac 2' 129 .' -ar 11025' 130 .' -ab '.shell_escape("${abitrate}k") 131 .' -b '.shell_escape("${vbitrate}k") 132 .' /dev/stdout 2>/dev/null |'; 133 134 # Print the movie 135 $ffmpeg_pid = open(DATA, $ffmpeg_command); 136 unless ($ffmpeg_pid) { 137 print header(), 138 "Can't do ffmpeg: $!\n${ffmpeg_command}"; 139 exit; 140 } 141 142 # Guess the filesize based on runtime and bitrate. This allows for progressive download behavior 143 my $sh = $dbh->prepare('SELECT (endtime-starttime)/100*60 FROM recorded '. 144 'WHERE starttime=FROM_UNIXTIME(?) '. 145 'AND recorded.chanid=?'); 146 $sh->execute($starttime, $chanid); 147 my ($runtime_secs) = $sh->fetchrow_array(); 148 $sh->finish; 149 150 if ($runtime_secs) { 151 $size = int(1.05*$runtime_secs*($vbitrate*1024+$abitrate*1024)/8); 152 print header(-type => 'video/ogg','Content-Length' => $size); 153 } else { 154 print header(-type => 'video/ogg'); 155 } 156 157 while (<DATA>) { 158 print $_; 159 } 160 close DATA; 161 162 1; -
new file modules/stream/stream_webm.pl
diff --git a/modules/stream/stream_webm.pl b/modules/stream/stream_webm.pl new file mode 100755 index 0000000..9f5b1ca
- + 1 #!/usr/bin/perl 2 # 3 # MythWeb Streaming/Download module 4 # 5 # @url $URL$ 6 # @date $Date$ 7 # @version $Revision$ 8 # @author $Author$ 9 # 10 11 use POSIX qw(ceil floor); 12 13 # round to the nearest even integer 14 sub round_even { 15 my ($in) = @_; 16 my $n = floor($in); 17 return ($n % 2 == 0) ? $n : ceil($in); 18 } 19 20 our $ffmpeg_pid; 21 our $ffmpeg_pgid; 22 23 # Shutdown cleanup, of various types 24 $ffmpeg_pgid = setpgrp(0,0); 25 $SIG{'TERM'} = \&shutdown_handler; 26 $SIG{'PIPE'} = \&shutdown_handler; 27 END { 28 shutdown_handler(); 29 } 30 sub shutdown_handler { 31 kill(1, $ffmpeg_pid) if ($ffmpeg_pid); 32 kill(-1, $ffmpeg_pgid) if ($ffmpeg_pgid); 33 } 34 35 # Find ffmpeg 36 $ffmpeg = ''; 37 foreach my $path (split(/:/, $ENV{'PATH'}.':/usr/local/bin:/usr/bin'), '.') { 38 if (-e "$path/mythffmpeg") { 39 $ffmpeg = "$path/mythffmpeg"; 40 last; 41 } 42 if (-e "$path/ffmpeg") { 43 $ffmpeg = "$path/ffmpeg"; 44 last; 45 } 46 elsif ($^O eq 'darwin' && -e "$path/ffmpeg.app") { 47 $ffmpeg = "$path/ffmpeg.app"; 48 last; 49 } 50 } 51 52 # Load some conversion settings from the database 53 $sh = $dbh->prepare('SELECT data FROM settings WHERE value=? AND hostname IS NULL'); 54 $sh->execute('WebFLV_w'); 55 my ($width) = $sh->fetchrow_array; 56 $sh->execute('WebFLV_vb'); 57 my ($vbitrate) = $sh->fetchrow_array; 58 $sh->execute('WebFLV_ab'); 59 my ($abitrate) = $sh->fetchrow_array; 60 $sh->finish(); 61 # auto-detect height based on aspect ratio 62 $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' . 63 'AND starttime=FROM_UNIXTIME(?) AND type=30 ' . 64 'AND data IS NOT NULL ORDER BY mark LIMIT 1'); 65 $sh->execute($chanid,$starttime); 66 $x = $sh->fetchrow_array; # type = 30 67 $sh->finish(); 68 69 $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' . 70 'AND starttime=FROM_UNIXTIME(?) AND type=31 ' . 71 'AND data IS NOT NULL ORDER BY mark LIMIT 1'); 72 $sh->execute($chanid,$starttime); 73 $y = $sh->fetchrow_array if ($x); # type = 31 74 $sh->finish(); 75 76 if (!$x || !$y || $x <= 720) { # <=720 means SD 77 $sh = $dbh->prepare('SELECT recordedmarkup.type, ' . 78 'recordedmarkup.data '. 79 'FROM recordedmarkup ' . 80 'WHERE recordedmarkup.chanid = ? ' . 81 'AND recordedmarkup.starttime = FROM_UNIXTIME(?) ' . 82 'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' . 83 'GROUP BY recordedmarkup.type ' . 84 'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' . 85 ' FROM recordedmarkup AS rm ' . 86 ' WHERE rm.chanid = recordedmarkup.chanid ' . 87 ' AND rm.starttime = recordedmarkup.starttime ' . 88 ' AND rm.type IN (10, 11, 12, 13, 14) ' . 89 ' AND rm.mark > recordedmarkup.mark ' . 90 ' ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' . 91 'LIMIT 1'); 92 $sh->execute($chanid,$starttime); 93 $aspect = $sh->fetchrow_hashref; 94 $sh->finish(); 95 96 if( $aspect->{'type'} == 10 ) { 97 $x = $y = 1; 98 } elsif( $aspect->{'type'}== 11 ) { 99 $x = 4; $y = 3; 100 } elsif( $aspect->{'type'}== 12 ) { 101 $x = 16; $y = 9; 102 } elsif( $aspect->{'type'}== 13 ) { 103 $x = 2.21; $y = 1; 104 } elsif( $aspect->{'type'}== 14 ) { 105 $x = $aspect->{'data'}; $y = 10000; 106 } else { 107 $x = 4; $y = 3; 108 } 109 } 110 $height = round_even($width * ($y/$x)); 111 112 $width = 320 unless ($width && $width > 1); 113 $height = 240 unless ($height && $height > 1); 114 $vbitrate = 256 unless ($vbitrate && $vbitrate > 1); 115 $abitrate = 64 unless ($abitrate && $abitrate > 1); 116 117 my $ffmpeg_command = $ffmpeg 118 .' -y' 119 .' -i '.shell_escape("$filename") 120 .' -s '.shell_escape("${width}x${height}") 121 .' -g 30' 122 .' -r 24' 123 .' -f webm' 124 .' -acodec libvorbis' 125 .' -vcodec libvpx' 126 .' -deinterlace' 127 .' -async 2' 128 .' -ac 2' 129 .' -ar 11025' 130 .' -ab '.shell_escape("${abitrate}k") 131 .' -b '.shell_escape("${vbitrate}k") 132 .' /dev/stdout 2>/dev/null |'; 133 134 # Print the movie 135 $ffmpeg_pid = open(DATA, $ffmpeg_command); 136 unless ($ffmpeg_pid) { 137 print header(), 138 "Can't do ffmpeg: $!\n${ffmpeg_command}"; 139 exit; 140 } 141 142 # Guess the filesize based on runtime and bitrate. This allows for progressive download behavior 143 my $sh = $dbh->prepare('SELECT (endtime-starttime)/100*60 FROM recorded '. 144 'WHERE starttime=FROM_UNIXTIME(?) '. 145 'AND recorded.chanid=?'); 146 $sh->execute($starttime, $chanid); 147 my ($runtime_secs) = $sh->fetchrow_array(); 148 $sh->finish; 149 150 if ($runtime_secs) { 151 $size = int(1.05*$runtime_secs*($vbitrate*1024+$abitrate*1024)/8); 152 print header(-type => 'video/webm','Content-Length' => $size); 153 } else { 154 print header(-type => 'video/webm'); 155 } 156 157 while (<DATA>) { 158 print $_; 159 } 160 close DATA; 161 162 1; -
modules/tv/tmpl/default/detail.php
diff --git a/modules/tv/tmpl/default/detail.php b/modules/tv/tmpl/default/detail.php index 8e9bfc5..eb9aa3c 100644
a b 37 37 <script type="text/javascript"> 38 38 <!-- 39 39 40 // Android devices need the following to play HTML5 video 41 var video = document.getElementById('video'); 42 video.addEventListener('click', function(){ 43 video.play(); 44 }, false); 45 40 46 // Keep track of the autoexpire flag 41 47 var autoexpire = <?php echo $program->auto_expire ? 1 : 0 ?>; 42 48 … … 649 655 650 656 <div class="x-pixmap"> 651 657 <?php if (setting('WebFLV_on')) { ?> 658 <!--Embed HTML5 video if supported--> 659 <video id="video" controls="controls" preload="none" width="<?php echo $flv_w ?>" height="<?php echo $flv_h ?>" poster="<?php echo $program->thumb_url($flv_w,0) ?>"> 660 <source src="<?php echo video_url($program, 'webm'); ?>" type="video/webm" /> 661 <source src="<?php echo video_url($program, 'ogv'); ?>" type="video/ogg" /> 652 662 <?php if (file_exists('js/libs/flowplayer/flowplayer.swf')) { ?> 653 663 654 664 … … 658 668 id="player"> 659 669 </a> 660 670 671 <!--Close HTML5 video tag--> 672 </video> 661 673 <!-- this will install flowplayer inside previous A- tag. --> 662 674 <script> 663 675 flowplayer( -
mythweb.conf.apache
diff --git a/mythweb.conf.apache b/mythweb.conf.apache index c2a4f00..d7dade2 100644
a b 184 184 # those are, so we should tell it. 185 185 AddType video/nuppelvideo .nuv 186 186 187 # Support HTML5 video formats which can be encoded and streamed "on-the-fly" 188 AddType video/ogg .ogv .ogg 189 AddType video/webm .webm 190 187 191 # Specify the MIME type for favicon.ico in case the server configuration 188 192 # doesn't or in case the server configuration uses the IANA-approved MIME type 189 193 # (image/vnd.microsoft.icon)--which most browsers won't recognize.