Ticket #12943: 0075-Mythweb-Add-HTML5-streaming-support-v7.patch

File 0075-Mythweb-Add-HTML5-streaming-support-v7.patch, 34.2 KB (added by warpme, 3 years ago)

Patch for current master

  • mythweb/includes/utils.php

    diff -Naur mythweb/includes/utils.php mythweb/includes/utils.php
     
    246246            case 'flvp': return "$url.flvp";
    247247            case 'flv' : return "$url.flv";
    248248            case 'mp4' : return "$url.mp4";
     249            case 'ogv' : return "$url.ogv";
     250            case 'webm': return "$url.webm";
    249251        }
    250252    // No more dsmyth filters, so return the URL no matter what the browser is.
    251253        return $url;
  • mythweb/modules/mythweb/set_flvplayer.php

    diff -Naur mythweb/modules/mythweb/set_flvplayer.php mythweb/modules/mythweb/set_flvplayer.php
     
    2323    // Bitrates
    2424        setting('WebFLV_vb', null, $_POST['vbitrate'] > 0 ? $_POST['vbitrate'] : 256);
    2525        setting('WebFLV_ab', null, $_POST['abitrate'] > 0 ? $_POST['abitrate'] : 64);
     26    // HTML5 streaming on/off
     27        setting('WebHTML5Stream_on', null, $_POST['HTML5stream'] ? 1 : 0);
    2628
    2729    }
    2830
  • mythweb/modules/mythweb/tmpl/default/set_flvplayer.php

    diff -Naur mythweb/modules/mythweb/tmpl/default/set_flvplayer.php mythweb/modules/mythweb/tmpl/default/set_flvplayer.php
     
    2424        <?php
    2525            $ffmpeg = '';
    2626            foreach (preg_split ('/:/', getenv ('PATH').':/usr/local/bin:/usr/bin') as $path) {
    27                 if (file_exists ($path."/ffmpeg")) {
    28                     $ffmpeg = $path."/ffmpeg";
     27                if (file_exists ($path."/mythffmpeg")) {
     28                    $ffmpeg = $path."/mythffmpeg";
    2929                    break;
    3030                }
    31                 elseif (php_uname ('s') == 'Darwin' && file_exists ($path."/ffmpeg.app")) {
    32                     $ffmpeg = $path."/ffmpeg".app;
     31                elseif (php_uname ('s') == 'Darwin' && file_exists ($path."/mythffmpeg.app")) {
     32                    $ffmpeg = $path."/mythffmpeg".app;
    3333                    break;
    3434                }
    3535            }
     
    6464         value="<?php echo html_entities(_or(setting('WebFLV_ab'), 64)) ?>" />
    6565         kbps</td>
    6666</tr><tr>
     67    <th><?php echo t('HTML5 Streaming') ?>:</th>
     68    <td><input class="radio" type="checkbox" name="HTML5stream"
     69         title="Enable HTML5 Streaming (with Flash Video as fallback)"
     70         <?php if (setting('WebHTML5Stream_on')) echo ' CHECKED' ?>></td>
     71</tr><tr>
    6772    <td align="right"><input type="reset"  class="submit" value="<?php echo t('Reset') ?>"></td>
    6873    <td align="center"><input type="submit" class="submit" name="save" value="<?php echo t('Save') ?>"></td>
    6974</tr>
  • mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php

    diff -Naur mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php
     
    4747         value="<?php echo html_entities(_or(setting('WebFLV_ab'), 64)) ?>" />
    4848         kbps</td>
    4949</tr><tr>
     50    <th><?php echo t('HTML5 Streaming') ?>:</th>
     51    <td><input class="radio" type="checkbox" name="HTML5stream"
     52         title="Enable HTML5 Streaming (with Flash Video as fallback)"
     53         <?php if (setting('WebHTML5Stream_on')) echo ' CHECKED' ?>></td>
     54</tr><tr>
    5055    <td align="right"><input type="reset"  class="submit" value="<?php echo t('Reset') ?>"></td>
    5156    <td align="center"><input type="submit" class="submit" name="save" value="<?php echo t('Save') ?>"></td>
    5257</tr>
  • mythweb/modules/_shared/lang/English.lang

    diff -Naur mythweb/modules/_shared/lang/English.lang mythweb/modules/_shared/lang/English.lang
     
    3232"%s Songs (%s)"
    3333"(height is calculated automatically from the recording aspect ratio)"
    3434"1 - Lowest"
     35"1080"
    3536"4 - Highest"
     37"720"
    3638"Aborted"
    3739"Aborting"
    3840"Action"
    3941"Activate"
    4042"Add"
    4143"Add Album to Current Playlist"
     44"Add Artist to Current Playlist"
    4245"Add Mix"
    4346"Add Song to Current Playlist"
    4447"add string"
     
    4952"Advanced"
    5053"Advanced Options"
    5154"Advanced Search"
    52 "airdate"
    5355"Airdate"
     56"airdate"
    5457"Airtime"
    5558"Album Listing"
    5659"Album Tracks"
     
    134137"Cancelled"
    135138"Caps Lock"
    136139"Cast"
    137 "category"
    138140"Category"
     141"category"
    139142"Category Legend"
    140143"CC"
    141144"Chan. Callsign"
    142145"Chan. Name"
    143146"Chan. Number"
    144 "channel"
    145147"Channel"
     148"channel"
    146149"Channel &quot;Jump to&quot;"
    147150"Channel Detail"
    148151"Channel Info"
     
    240243"End Late"
    241244"Enter"
    242245"Enter a new name for your playlist"
    243 "enter your search terms"
    244246"Enter your search terms"
     247"enter your search terms"
    245248"Episode"
    246249"Episode Number"
    247250"Error"
     
    310313"Hosted by"
    311314"Hour"
    312315"Hour Format"
     316"HTML5 Streaming"
    313317"hue"
    314318"Identifiable episode"
    315319"Ignore generic shows"
     
    325329"info: dont record"
    326330"info: flvplayer"
    327331    <p>
    328     Flash video playback is currently only a proof-of-concept and should be
    329     considered <b>EXTREMELY</b> experimental, which is why it has been disabled by
    330     default.
    331     </p>
     332    Video streaming in mythweb is currently based on HTML5 webm/ogg and flash flv.
     333    <strong>NOTE</strong>: Today there is no single format supported by all browsers.
     334    By this different browsers needs different configuration<br>
     335    mythweb provides following streaming support:
     336    <ul>
     337    <li><strong>Google Chrome:</strong>  Highly unstable. Flash fall-back adviced. Uncheck "HTML5 Streaming"</li>
     338    <li><strong>Mozilla Firefox:</strong>  v57 of Mozilla works well. Enable HTML5 streaming by checking "HTML5 Streaming"</li>
     339    <li><strong>Safari (MacOS):</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
     340    <li><strong>Safati (iOS):</strong>  Flash is not supported so no fall-back possible. Use HLS client"</li>
     341    <li><strong>IE8 and older:</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
     342    <li><strong>IE9 and newer:</strong>  Highly unstable. Prefer Flash fall-back by unchecking "HTML5 Streaming"</li>
     343    </ul>
    332344    <p>
    333     It currently expects that ffmpeg is installed and compiled with mp3
    334     support, and that the recordings files are accessible to your webserver
    335     userid.  It probably won't work with Nupplevideo files, and in the end,
    336     it may just not work at all (or maybe even worse).
     345    This feature expects that myth is build with: libx264, libvpx and libtheora libraries. Also for
     346    flash fallback, Adobe Flash player must be installed on client side.
    337347    </p>
    338348    <p>
    339     Enable this feature at your own risk, and don't expect too much
    340     official help until it has left the experimental phase.
     349    Enabling HTML5 streaming may cause not stable streaming as majority browsers (except Mozzilla)
     350    offers not good video playabck stability. By this prefered mode is Flash fall-back mode
     351    (with "HTML5 Streaming" option disabled).
    341352    </p>
    342353"info: hidden advanced schedule"
    343354    The advanced scheduling options are currently hidden.
     
    358369"Internet Reference #"
    359370"Jump"
    360371"Jump Points"
    361 "Jump to"
    362372"Jump To"
     373"Jump to"
    363374"JumpPoints Editor"
    364375"Key Bindings"
    365376"Key bindings"
     
    377388"Left"
    378389"left"
    379390"leftl"
    380 "length"
    381391"Length"
     392"length"
    382393"Length (min)"
    383394"Length in minutes"
    384395"Listing &quot;Jump to&quot;"
     
    414425"Modify unidentified episodes"
    415426"Monday"
    416427"Mono"
    417 "more"
    418428"More"
     429"more"
    419430"Move Item Down in Playlist"
    420431"Move Item Up in Playlist"
    421432"movie"
     
    423434"Movies"
    424435"Movies, 3&frac12; Stars or more"
    425436"Movies, Stinkers (2 Stars or less)"
     437"mplexid"
    426438"Music"
    427439"Music Specials"
    428440"My Session"
     
    515528"Play"
    516529"Play Recording on Frontend"
    517530"Play this Album Now"
     531"Play this Artist Now"
    518532"Play This Playlist Now"
    519533"Play this Playlist Now"
    520534"Play this Song Now"
     
    563577"Priority for movies by the year of release"
    564578"Priority when shown once"
    565579"Produced by"
    566 "profile"
    567580"Profile"
     581"profile"
    568582"Program Categories"
    569583"Program Detail"
    570584"Program ID"
     
    582596"Rating:"
    583597"Recently Added Albums"
    584598"Recently completed jobs"
    585 "Recently Played Songs"
    586599"Recently Played Albums"
     600"Recently Played Songs"
    587601"recgroup"
    588602"Recommend Videos"
    589603"Recommended"
     
    688702"Reset template and skin to defaults"
    689703"Retry"
    690704"Return to Statistics Page"
    691 "right"
    692705"Right"
     706"right"
    693707"Root Directory"
    694708"Rows to show between timeslot info"
    695709"Running"
     
    732746"Select the correct show"
    733747"Server returned invalid data when attempting to retrieve metadata."
    734748"Server Statistics"
    735 "mplexid"
    736749"serviceid"
    737750"Set Host"
    738 "settings"
    739751"Settings"
     752"settings"
    740753"Settings Table"
    741754"settings/stream: protocol"
    742755    Many media players are incapable of playing files that are streamed from<br/>
     
    829842"Toggle Interactive Mode"
    830843"Too Many"
    831844"Top $1"
    832 "Top Played Songs"
    833845"Top Played Albums"
    834846"Top Played Artist"
     847"Top Played Songs"
    835848"Top Rated Songs"
    836849"Total Length"
    837850"Total Recorded"
     
    849862"TV"
    850863"TV functions, including recorded programs."
    851864"TV.com"
    852 "type"
    853865"Type"
     866"type"
    854867"Uncategorized"
    855868"Undelete"
    856869"Undelete: $1"
  • mythweb/modules/_shared/lang/Polish.lang

    diff -Naur mythweb/modules/_shared/lang/Polish.lang mythweb/modules/_shared/lang/Polish.lang
     
    4444"%s Songs (%s)"
    4545"(height is calculated automatically from the recording aspect ratio)"
    4646"1 - Lowest"
     47"1080"
    4748"4 - Highest"
     49"720"
    4850"Aborted"
    4951"Aborting"
    5052"Action"
     
    5355    Uaktywnij
    5456"Add"
    5557"Add Album to Current Playlist"
     58"Add Artist to Current Playlist"
    5659"Add Mix"
    5760"Add Song to Current Playlist"
    5861"add string"
     
    6669    Zaawansowane opcje
    6770"Advanced Search"
    6871    Szukanie zaawansowane
    69 "airdate"
    7072"Airdate"
     73"airdate"
    7174"Airtime"
    7275"Album Listing"
    7376"Album Tracks"
     
    162165"Caps Lock"
    163166"Cast"
    164167    Obsada
    165 "category"
    166     kategoria
    167168"Category"
    168169    Kategoria
     170"category"
     171    kategoria
    169172"Category Legend"
    170173    Legenda kategorie
    171174"CC"
     
    228231"Custom Schedule"
    229232"Custom Search"
    230233"Customize Screens"
     234"Damaged"
    231235"Data"
    232236    Dane
    233237"Database"
     
    258262"DELETE this Saved Playlist"
    259263"DeleteKey"
    260264"Depending on the Search type this is where you enter actual main search commands"
    261 "description"
    262     opis
    263265"Description"
    264266    Opis
     267"description"
     268    opis
    265269"Destination"
    266270    Cel
    267271"Details for: $1"
     
    379383    Godzina
    380384"Hour Format"
    381385    Format godziny
     386"HTML5 Streaming"
    382387"hue"
    383388    kolor
    384389"Identifiable episode"
     
    392397"info: default recording"
    393398"info: dont record"
    394399"info: flvplayer"
     400    <p>
     401    Video streaming in mythweb is currently based on HTML5 webm/ogg and flash flv.
     402    <strong>NOTE</strong>: Today there is no single format supported by all browsers..
     403    By this different browsers needs different configuration<br>
     404    mythweb provides following streaming support:
     405    <ul>
     406    <li><strong>Google Chrome:</strong>  Highly unstable. Flash fall-back adviced. Uncheck "HTML5 Streaming"</li>
     407    <li><strong>Mozilla Firefox:</strong>  v52.0 or newer is stable and plays perfectly. Enable HTML5 streaming by checking "HTML5 Streaming"</li>
     408    <li><strong>Safari (MacOS):</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
     409    <li><strong>Safati (iOS):</strong>  Flash is not supported so no fall-back possible. Use HLS client"</li>
     410    <li><strong>IE8 and older:</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
     411    <li><strong>IE9 and newer:</strong>  Highly unstable. Prefer Flash fall-back by unchecking "HTML5 Streaming"</li>
     412    </ul>
     413    <p>
     414    This feature expects that myth is build with libx264, libvpx and libtheora libraries. Also for
     415    flash fallback, Adobe Flash player must be installed on client side.
     416    </p>
     417    <p>
     418    Enabling HTML5 streaming may cause not stable streaming as majority browsers (except Mozzilla).
     419    offers not good video playabck stability. By this prefered mode is Flash fall-back mode.
     420    (with "HTML5 Streaming" option disabled).
     421    </p>
    395422"info: hidden advanced schedule"
    396423"info: record this"
    397424"info:forget old"
     
    403430"Jump"
    404431    Skocz
    405432"Jump Points"
    406 "Jump to"
    407     Skocz do
    408433"Jump To"
    409434    Skocz Do
     435"Jump to"
     436    Skocz do
    410437"JumpPoints Editor"
    411438"Key Bindings"
    412439"Key bindings"
     
    423450"Last recording"
    424451"Last showing of each episode"
    425452"Later"
    426 "left"
    427453"Left"
     454"left"
    428455"leftl"
    429 "length"
    430     długość
    431456"Length"
    432457    Długość
     458"length"
     459    długość
    433460"Length (min)"
    434461    Czas trwania (minut):
    435462"Length in minutes"
     
    463490"Missing Cover"
    464491"Modify priority by star rating (0.0 to 1.0 for movies only)"
    465492"Modify priority for a station on an input"
    466 "Modify priority for all inputs on a card"
    467493"Modify priority for an input (Input priority)"
    468494"Modify priority for every card on a host"
    469495"Modify unidentified episodes"
     
    481507"Movies, 3&frac12; Stars or more"
    482508    Filmy 3&frac12; gwiazdy lub więcej
    483509"Movies, Stinkers (2 Stars or less)"
     510"mplexid"
    484511"Music"
    485512    Muzyka
    486513"Music Specials"
     
    520547    Nowe tytuły, premiery
    521548"No"
    522549"NO DATA"
     550"No episodes"
    523551"No Frontends allow remote control."
    524552"No Genre"
    525553"No matches found"
     
    579607"Play"
    580608"Play Recording on Frontend"
    581609"Play this Album Now"
     610"Play this Artist Now"
    582611"Play This Playlist Now"
    583612"Play this Playlist Now"
    584613"Play this Song Now"
     
    649678"Rating:"
    650679"Recently Added Albums"
    651680"Recently completed jobs"
     681"Recently Played Albums"
    652682"Recently Played Songs"
    653683"recgroup"
    654684"Recommend Videos"
     
    764794"Reset template and skin to defaults"
    765795"Retry"
    766796"Return to Statistics Page"
    767 "right"
    768797"Right"
     798"right"
    769799"Root Directory"
    770800"Rows to show between timeslot info"
    771801"Running"
     
    820850"Server returned invalid data when attempting to retrieve metadata."
    821851"Server Statistics"
    822852"serviceid"
    823 "mplexid"
    824853"Set Host"
    825854"Settings"
    826855    Ustawienia
     
    909938"Time Span"
    910939"Time Stretch"
    911940"Timeslot size"
    912 "title"
    913     tytuł
    914941"Title"
    915942    Tytuł
     943"title"
     944    tytuł
    916945"Title Match"
    917 "Title search"
    918946"Title Search"
    919947    Szukanie Tytułu
     948"Title search"
    920949"Title Search:"
    921950"Title:"
    922951"Toggle Interactive Mode"
    923952"Too Many"
    924953"Top $1"
    925954    Szczyt $1
     955"Top Played Albums"
     956"Top Played Artist"
    926957"Top Played Songs"
    927958"Top Rated Songs"
    928959"Total Length"
     
    932963    Całkowity Czas
    933964"Total Time:  %s"
    934965"Track #%s from the album '%s'"
     966"Trakt.tv Home Page"
    935967"Transcode"
    936968"Transcoded"
    937 "transcoder"
    938969"Transcoder"
     970"transcoder"
    939971"Tuesday"
    940972    Wtorek
    941973"Tuner Busy"
    942974"TV"
    943975"TV functions, including recorded programs."
    944976"TV.com"
    945 "type"
    946     typ
    947977"Type"
    948978    Typ
     979"type"
     980    typ
    949981"Uncategorized"
    950982"Undelete"
    951983"Undelete: $1"
  • mythweb/modules/stream/handler.pl

    diff -Naur mythweb/modules/stream/handler.pl mythweb/modules/stream/handler.pl
     
    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?
  • mythweb/modules/stream/stream_ogv.pl

    diff -Naur mythweb/modules/stream/stream_ogv.pl mythweb/modules/stream/stream_ogv.pl
     
     1#!/usr/bin/perl
     2#
     3# MythWeb Streaming/Download module
     4#
     5# @url       $URL$
     6# @date      $2016/11/23$
     7# @version   $v1.0$
     8# @author    $Piotr Oniszczuk$
     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
     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_pid) if ($ffmpeg_pid);
     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
     62# auto-detect height based on aspect ratio
     63    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
     64                        'AND starttime=FROM_UNIXTIME(?) AND type=30 ' .
     65                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
     66    $sh->execute($chanid,$starttime);
     67    $x = $sh->fetchrow_array;           # type = 30
     68    $sh->finish();
     69
     70    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
     71                        'AND starttime=FROM_UNIXTIME(?) AND type=31 ' .
     72                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
     73    $sh->execute($chanid,$starttime);
     74    $y = $sh->fetchrow_array if ($x);   # type = 31
     75    $sh->finish();
     76
     77    if (!$x || !$y || $x <= 720) {      # <=720 means SD
     78        $sh = $dbh->prepare('SELECT recordedmarkup.type, ' .
     79               'recordedmarkup.data '.
     80               'FROM recordedmarkup ' .
     81               'WHERE recordedmarkup.chanid = ? ' .
     82               'AND recordedmarkup.starttime = FROM_UNIXTIME(?)  ' .
     83               'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' .
     84               'GROUP BY recordedmarkup.type  ' .
     85               'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' .
     86               '   FROM recordedmarkup AS rm ' .
     87               '   WHERE rm.chanid = recordedmarkup.chanid ' .
     88               '   AND rm.starttime = recordedmarkup.starttime ' .
     89               '   AND rm.type IN (10, 11, 12, 13, 14)  ' .
     90               '   AND rm.mark > recordedmarkup.mark ' .
     91               '   ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' .
     92               'LIMIT 1');
     93        $sh->execute($chanid,$starttime);
     94        $aspect = $sh->fetchrow_hashref;
     95        $sh->finish();
     96
     97        if( $aspect->{'type'} ) {
     98            if( $aspect->{'type'} == 10 ) {
     99                $x = $y = 1;
     100            } elsif( $aspect->{'type'}== 11 ) {
     101                $x = 4; $y = 3;
     102            } elsif( $aspect->{'type'}== 12 ) {
     103                $x = 16; $y = 9;
     104            } elsif( $aspect->{'type'}== 13 ) {
     105                $x = 2.21; $y = 1;
     106            } elsif( $aspect->{'type'}== 14 ) {
     107                $x = $aspect->{'data'}; $y = 1000000;
     108            } else {
     109                $x = 4; $y = 3;
     110            }
     111        }
     112        else {
     113            $x = 4; $y = 3;
     114        }
     115
     116    }
     117    $height = round_even($width * ($y/$x));
     118
     119    $width    = 320 unless ($width    && $width    > 1);
     120    $height   = 240 unless ($height   && $height   > 1);
     121    $vbitrate = 256 unless ($vbitrate && $vbitrate > 1);
     122    $abitrate = 64  unless ($abitrate && $abitrate > 1);
     123
     124# build appropriate encoder commad
     125    my $ffmpeg_command = $ffmpeg
     126                        .' -y'
     127                        .' -i '.shell_escape("$filename")
     128                        .' -s '.shell_escape("${width}x${height}")
     129                        .' -g 30'
     130                        .' -r 24'
     131                        .' -f ogg'
     132                        .' -codec:a vorbis'
     133                        .' -codec:v libtheora'
     134                        .' -strict -2'
     135                        .' -deinterlace'
     136                        .' -async 2'
     137                        .' -ac 2'
     138                        .' -ar 44100'
     139                        .' -b:a '.shell_escape("${abitrate}k")
     140                        .' -b:v '.shell_escape("${vbitrate}k")
     141                        .' -sn'
     142                        .' /dev/stdout 2>/dev/null |';
     143
     144# start to encode content
     145    $ffmpeg_pid = open(DATA, $ffmpeg_command);
     146    unless ($ffmpeg_pid) {
     147        print header(),
     148                "Can't execute ffmpeg. Command was: $!\n${ffmpeg_command}";
     149        exit;
     150    }
     151
     152# Guess the filesize based on duration and bitrate. This allows progressive download. Print header with
     153# guessed file size
     154    my $lengthSec;
     155    my $length;
     156    my $range;
     157    $dur = `$ffmpeg -i $filename 2>&1 | /usr/bin/grep "Duration" | /usr/bin/cut -d ' ' -f 4 | /bin/sed s/,//`;
     158    if ($dur && $dur =~ /\d*:\d*:.*/) {
     159        @times = split(':',$dur);
     160        $lengthSec = $times[0]*3600+$times[1]*60+$times[2];
     161        $length = int($lengthSec);
     162        $size = int(0.85*$lengthSec*($vbitrate*1024+$abitrate*1024)/8);
     163        $range = $size-1;
     164        print header(-type                  => 'video/ogg',
     165                     -Content_length        => $size,
     166                     -Accept_Ranges         => 'bytes',
     167                     -Timing_Allow_Origin   => '*',
     168                     -Content_Duration      => $length,
     169                     -Content_Range         => 'bytes 0-'.$range.'/'.$size,
     170                     -X_Content_Duration    => $length
     171                    );
     172    } else {
     173        print header(-type                  => 'video/ogg');
     174
     175    }
     176
     177    if ($ENV{'REQUEST_METHOD'} eq 'HEAD') {
     178        exit;
     179    }
     180
     181# send encoded data to browser
     182    my $buffer;
     183    while (read DATA, $buffer, 262144) {
     184        unless (print $buffer ) {
     185            last;
     186        }
     187    }
     188
     189    close DATA;
     190
  • mythweb/modules/stream/stream_webm.pl

    diff -Naur mythweb/modules/stream/stream_webm.pl mythweb/modules/stream/stream_webm.pl
     
     1#!/usr/bin/perl
     2#
     3# MythWeb Streaming/Download module
     4#
     5# @url       $URL$
     6# @date      $2016/11/23$
     7# @version   $v1.0$
     8# @author    $Piotr Oniszczuk$
     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
     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_pid) if ($ffmpeg_pid);
     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
     62# auto-detect height based on aspect ratio
     63    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
     64                        'AND starttime=FROM_UNIXTIME(?) AND type=30 ' .
     65                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
     66    $sh->execute($chanid,$starttime);
     67    $x = $sh->fetchrow_array;           # type = 30
     68    $sh->finish();
     69
     70    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
     71                        'AND starttime=FROM_UNIXTIME(?) AND type=31 ' .
     72                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
     73    $sh->execute($chanid,$starttime);
     74    $y = $sh->fetchrow_array if ($x);   # type = 31
     75    $sh->finish();
     76
     77    if (!$x || !$y || $x <= 720) {      # <=720 means SD
     78        $sh = $dbh->prepare('SELECT recordedmarkup.type, ' .
     79               'recordedmarkup.data '.
     80               'FROM recordedmarkup ' .
     81               'WHERE recordedmarkup.chanid = ? ' .
     82               'AND recordedmarkup.starttime = FROM_UNIXTIME(?)  ' .
     83               'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' .
     84               'GROUP BY recordedmarkup.type  ' .
     85               'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' .
     86               '   FROM recordedmarkup AS rm ' .
     87               '   WHERE rm.chanid = recordedmarkup.chanid ' .
     88               '   AND rm.starttime = recordedmarkup.starttime ' .
     89               '   AND rm.type IN (10, 11, 12, 13, 14)  ' .
     90               '   AND rm.mark > recordedmarkup.mark ' .
     91               '   ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' .
     92               'LIMIT 1');
     93        $sh->execute($chanid,$starttime);
     94        $aspect = $sh->fetchrow_hashref;
     95        $sh->finish();
     96
     97        if( $aspect->{'type'} ) {
     98            if( $aspect->{'type'} == 10 ) {
     99                $x = $y = 1;
     100            } elsif( $aspect->{'type'}== 11 ) {
     101                $x = 4; $y = 3;
     102            } elsif( $aspect->{'type'}== 12 ) {
     103                $x = 16; $y = 9;
     104            } elsif( $aspect->{'type'}== 13 ) {
     105                $x = 2.21; $y = 1;
     106            } elsif( $aspect->{'type'}== 14 ) {
     107                $x = $aspect->{'data'}; $y = 1000000;
     108            } else {
     109                $x = 4; $y = 3;
     110            }
     111        }
     112        else {
     113            $x = 4; $y = 3;
     114        }
     115    }
     116    $height = round_even($width * ($y/$x));
     117
     118    $width    = 320 unless ($width    && $width    > 1);
     119    $height   = 240 unless ($height   && $height   > 1);
     120    $vbitrate = 256 unless ($vbitrate && $vbitrate > 1);
     121    $abitrate = 64  unless ($abitrate && $abitrate > 1);
     122
     123# build appropriate encoder commad
     124    my $ffmpeg_command = $ffmpeg
     125                        .' -y'
     126                        .' -i '.shell_escape("$filename")
     127                        .' -s '.shell_escape("${width}x${height}")
     128                        .' -g 30'
     129                        .' -r 24'
     130                        .' -f webm'
     131                        .' -codec:a vorbis'
     132                        .' -codec:v libvpx'
     133                        .' -cpu-used -8'
     134                        .' -deadline realtime'
     135                        .' -strict -2'
     136                        .' -deinterlace'
     137                        .' -async 2'
     138                        .' -ac 2'
     139                        .' -ar 44100'
     140                        .' -b:a '.shell_escape("${abitrate}k")
     141                        .' -b:v '.shell_escape("${vbitrate}k")
     142                        .' -sn'
     143                        .' /dev/stdout 2>/dev/null |';
     144
     145# start to encode content
     146    $ffmpeg_pid = open(DATA, $ffmpeg_command);
     147    unless ($ffmpeg_pid) {
     148        print header(),
     149                "Can't execute ffmpeg. Command was: $!\n${ffmpeg_command}";
     150        exit;
     151    }
     152
     153# Guess the filesize based on duration and bitrate. This allows progressive download. Print header with
     154# guessed file size
     155    my $lengthSec;
     156    my $length;
     157    my $range;
     158    $dur = `$ffmpeg -i $filename 2>&1 | /usr/bin/grep "Duration" | /usr/bin/cut -d ' ' -f 4 | /bin/sed s/,//`;
     159    if ($dur && $dur =~ /\d*:\d*:.*/) {
     160        @times = split(':',$dur);
     161        $lengthSec = $times[0]*3600+$times[1]*60+$times[2];
     162        $length = int($lengthSec);
     163        $size = int(0.90*$lengthSec*($vbitrate*1024+$abitrate*1024)/8);
     164        $range = $size-1;
     165        print header(-type                  => 'video/webm',
     166                     -Content_length        => $size,
     167                     -Accept_Ranges         => 'bytes',
     168                     -Timing_Allow_Origin   => '*',
     169                     -Content_Duration      => $length,
     170                     -Content_Range         => 'bytes 0-'.$range.'/'.$size,
     171                     -X_Content_Duration    => $length
     172                    );
     173    } else {
     174        print header(-type                  => 'video/webm');
     175
     176    }
     177
     178    if ($ENV{'REQUEST_METHOD'} eq 'HEAD') {
     179        exit;
     180    }
     181
     182# send encoded data to browser
     183    my $buffer;
     184    while (read DATA, $buffer, 262144) {
     185        unless (print $buffer ) {
     186            last;
     187        }
     188    }
     189
     190    close DATA;
     191
  • mythweb/modules/tv/tmpl/default/detail.php

    diff -Naur mythweb/modules/tv/tmpl/default/detail.php mythweb/modules/tv/tmpl/default/detail.php
     
    3737<script type="text/javascript">
    3838<!--
    3939
     40// Android devices need the following to play HTML5 video. Taken from ticket #10529
     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
     
    665671
    666672            <div class="x-pixmap">
    667673<?php   if (setting('WebFLV_on')) { ?>
     674<?php   if (setting('WebHTML5Stream_on')) { ?>
     675            <video id="video" controls="controls" preload="metadata" width="<?php echo $flv_w ?>" height="<?php echo $flv_h ?>" poster="<?php echo $program->thumb_url($flv_w,0) ?>">
     676                <source src="<?php echo video_url($program, 'webm'); ?>" type="video/webm" />
     677                <source src="<?php echo video_url($program, 'ogv'); ?>" type="video/ogg" />
     678<?php       } ?>
    668679<?php       if (file_exists('js/libs/flowplayer/flowplayer.swf')) { ?>
    669 
    670 
    671           <!-- this A tag is where your Flowplayer will be placed. it can be anywhere -->
    672             <a href=""
    673                 style="display:block;width:<?php echo $flv_w ?>px;height:<?php echo $flv_h ?>px"
    674                 id="player">
    675             </a>
    676 
    677             <!-- this will install flowplayer inside previous A- tag. -->
    678             <script>
    679                 flowplayer(
    680                     "player",
    681                     "<?php echo root_url ?>js/libs/flowplayer/flowplayer.swf", {
    682                     playlist: [
     680                <a href="" style="display:block;width:<?php echo $flv_w ?>px;height:<?php echo $flv_h ?>px" id="flash-player"></a>
     681                <script>
     682                    flowplayer("flash-player","<?php echo root_url ?>js/libs/flowplayer/flowplayer.swf", {
     683                        playlist: [
    683684                        // this first PNG clip works as a splash image
    684685                        {
    685686                            url: '<?php echo $program->thumb_url($flv_w,0) ?>',
    686687                            scaling: 'orig'
    687                             },
     688                        },
    688689                        // Then we have the video
    689690                        {
    690691                            url: "<?php echo video_url($program, 'flv'); ?>",
     
    694695                            // Would be nice to auto-buffer, but we don't want to
    695696                            // waste bandwidth and CPU on the remote machine.
    696697                            autoBuffering: false
    697                             }
     698                        }
    698699                        ]}
    699700                    );
    700             </script>
     701                </script>
    701702<?php       } elseif (file_exists('modules/tv/MFPlayer.swf')) { ?>
    702703                    <script language="JavaScript" type="text/javascript">
    703704                    <!--
     
    813814<?php   } else { ?>
    814815                <a href="<?php echo $program->url ?>" title="<?php echo t('Direct Download') ?>"
    815816                    ><img src="<?php echo $program->thumb_url($flv_w,0) ?>" width="<?php echo $flv_w ?>"></a>
    816 <?php   } ?></td>
     817<?php   } ?>
     818<?php   if (setting('WebHTML5Stream_on')) { ?>
     819            </video>
     820<?php   } ?>
     821
     822        </td>
    817823            </div>
    818824            <div class="x-links">
    819825                <a href="<?php echo video_url($program, 'asx') ?>" title="<?php echo t('ASX Stream') ?>"
  • mythweb/mythweb.conf.apache

    diff -Naur mythweb/mythweb.conf.apache mythweb/mythweb.conf.apache
     
    194194    # those are, so we should tell it.
    195195        AddType video/nuppelvideo   .nuv
    196196
     197    # Support HTML5 video formats which can be encoded and streamed "on-the-fly"
     198        AddType video/ogg           .ogv .ogg
     199        AddType video/webm          .webm
     200
    197201    # Specify the MIME type for favicon.ico in case the server configuration
    198202    # doesn't or in case the server configuration uses the IANA-approved MIME type
    199203    # (image/vnd.microsoft.icon)--which most browsers won't recognize.