Ticket #10529: mythweb-fixes-0.26-html5-video.patch

File mythweb-fixes-0.26-html5-video.patch, 16.1 KB (added by amessina@…, 11 years ago)

MythWeb fixes/0.26 updated html5 video patch

  • 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  
    242242            case 'flvp': return "$url.flvp";
    243243            case 'flv' : return "$url.flv";
    244244            case 'mp4' : return "$url.mp4";
     245            case 'ogv' : return "$url.ogv";
     246            case 'webm': return "$url.webm";
    245247        }
    246248    // No more dsmyth filters, so return the URL no matter what the browser is.
    247249        return $url;
  • modules/stream/handler.pl

    diff --git a/modules/stream/handler.pl b/modules/stream/handler.pl
    index 9015c62..f449624 100755
    a b  
    1212
    1313    require "modules/$Path[0]/tv.pl";
    1414
     15# Use the MythTV Services API URL if the $filename URL is not local
    1516    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}";
    1932    }
    2033
     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    }
    2142# ASX mode?
    22     if ($ENV{'REQUEST_URI'} =~ /\.asx$/i) {
     43    elsif ($ENV{'REQUEST_URI'} =~ /\.asx$/i) {
    2344        require "modules/$Path[0]/stream_asx.pl";
    2445    }
    2546# 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  
    3737<script type="text/javascript">
    3838<!--
    3939
     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
    4046// Keep track of the autoexpire flag
    4147    var autoexpire = <?php echo $program->auto_expire ? 1 : 0 ?>;
    4248
     
    649655
    650656            <div class="x-pixmap">
    651657<?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" />
    652662<?php       if (file_exists('js/libs/flowplayer/flowplayer.swf')) { ?>
    653663
    654664
     
    658668                id="player">
    659669            </a>
    660670
     671          <!--Close HTML5 video tag-->
     672            </video>
    661673            <!-- this will install flowplayer inside previous A- tag. -->
    662674            <script>
    663675                flowplayer(
  • mythweb.conf.apache

    diff --git a/mythweb.conf.apache b/mythweb.conf.apache
    index c2a4f00..d7dade2 100644
    a b  
    184184    # those are, so we should tell it.
    185185        AddType video/nuppelvideo   .nuv
    186186
     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
    187191    # Specify the MIME type for favicon.ico in case the server configuration
    188192    # doesn't or in case the server configuration uses the IANA-approved MIME type
    189193    # (image/vnd.microsoft.icon)--which most browsers won't recognize.