Ticket #12943: 0026-mythweb-add-HTML5-streaming-support-v3.patch

File 0026-mythweb-add-HTML5-streaming-support-v3.patch, 34.9 KB (added by warpme@…, 3 years ago)

V3 patch with improved/corrected content-size heuristics

Line 
1diff -Naur mythtv-master-build-old/mythplugins/mythweb/includes/utils.php mythtv-master-build-new/mythplugins/mythweb/includes/utils.php
2--- mythtv-master-build-old/mythplugins/mythweb/includes/utils.php      2016-11-25 11:02:01.000000000 +0100
3+++ mythtv-master-build-new/mythplugins/mythweb/includes/utils.php      2016-11-25 11:04:40.046666653 +0100
4@@ -246,6 +246,8 @@
5             case 'flvp': return "$url.flvp";
6             case 'flv' : return "$url.flv";
7             case 'mp4' : return "$url.mp4";
8+            case 'ogv' : return "$url.ogv";
9+            case 'webm': return "$url.webm";
10         }
11     // No more dsmyth filters, so return the URL no matter what the browser is.
12         return $url;
13diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/set_flvplayer.php mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/set_flvplayer.php
14--- mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/set_flvplayer.php       2016-11-25 11:02:02.000000000 +0100
15+++ mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/set_flvplayer.php       2016-11-25 11:05:17.746666670 +0100
16@@ -23,6 +23,8 @@
17     // Bitrates
18         setting('WebFLV_vb', null, $_POST['vbitrate'] > 0 ? $_POST['vbitrate'] : 256);
19         setting('WebFLV_ab', null, $_POST['abitrate'] > 0 ? $_POST['abitrate'] : 64);
20+    // HTML5 streaming on/off
21+        setting('WebHTML5Stream_on', null, $_POST['HTML5stream'] ? 1 : 0);
22 
23     }
24 
25diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/tmpl/default/set_flvplayer.php mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/tmpl/default/set_flvplayer.php
26--- mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/tmpl/default/set_flvplayer.php  2016-11-25 11:02:40.000000000 +0100
27+++ mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/tmpl/default/set_flvplayer.php  2016-11-25 11:05:17.746666670 +0100
28@@ -64,6 +64,11 @@
29          value="<?php echo html_entities(_or(setting('WebFLV_ab'), 64)) ?>" />
30          kbps</td>
31 </tr><tr>
32+    <th><?php echo t('HTML5 Streaming') ?>:</th>
33+    <td><input class="radio" type="checkbox" name="HTML5stream"
34+         title="Enable HTML5 Streaming (with Flash Video as fallback)"
35+         <?php if (setting('WebHTML5Stream_on')) echo ' CHECKED' ?>></td>
36+</tr><tr>
37     <td align="right"><input type="reset"  class="submit" value="<?php echo t('Reset') ?>"></td>
38     <td align="center"><input type="submit" class="submit" name="save" value="<?php echo t('Save') ?>"></td>
39 </tr>
40diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php
41--- mythtv-master-build-old/mythplugins/mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php     2016-11-25 11:02:02.000000000 +0100
42+++ mythtv-master-build-new/mythplugins/mythweb/modules/mythweb/tmpl/lite/set_flvplayer.php     2016-11-25 11:05:17.746666670 +0100
43@@ -47,6 +47,11 @@
44          value="<?php echo html_entities(_or(setting('WebFLV_ab'), 64)) ?>" />
45          kbps</td>
46 </tr><tr>
47+    <th><?php echo t('HTML5 Streaming') ?>:</th>
48+    <td><input class="radio" type="checkbox" name="HTML5stream"
49+         title="Enable HTML5 Streaming (with Flash Video as fallback)"
50+         <?php if (setting('WebHTML5Stream_on')) echo ' CHECKED' ?>></td>
51+</tr><tr>
52     <td align="right"><input type="reset"  class="submit" value="<?php echo t('Reset') ?>"></td>
53     <td align="center"><input type="submit" class="submit" name="save" value="<?php echo t('Save') ?>"></td>
54 </tr>
55diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/_shared/lang/English.lang mythtv-master-build-new/mythplugins/mythweb/modules/_shared/lang/English.lang
56--- mythtv-master-build-old/mythplugins/mythweb/modules/_shared/lang/English.lang       2016-11-25 11:02:40.000000000 +0100
57+++ mythtv-master-build-new/mythplugins/mythweb/modules/_shared/lang/English.lang       2016-11-25 11:57:42.000000000 +0100
58@@ -32,13 +32,16 @@
59 "%s Songs (%s)"
60 "(height is calculated automatically from the recording aspect ratio)"
61 "1 - Lowest"
62+"1080"
63 "4 - Highest"
64+"720"
65 "Aborted"
66 "Aborting"
67 "Action"
68 "Activate"
69 "Add"
70 "Add Album to Current Playlist"
71+"Add Artist to Current Playlist"
72 "Add Mix"
73 "Add Song to Current Playlist"
74 "add string"
75@@ -49,8 +52,8 @@
76 "Advanced"
77 "Advanced Options"
78 "Advanced Search"
79-"airdate"
80 "Airdate"
81+"airdate"
82 "Airtime"
83 "Album Listing"
84 "Album Tracks"
85@@ -134,15 +137,15 @@
86 "Cancelled"
87 "Caps Lock"
88 "Cast"
89-"category"
90 "Category"
91+"category"
92 "Category Legend"
93 "CC"
94 "Chan. Callsign"
95 "Chan. Name"
96 "Chan. Number"
97-"channel"
98 "Channel"
99+"channel"
100 "Channel &quot;Jump to&quot;"
101 "Channel Detail"
102 "Channel Info"
103@@ -240,8 +243,8 @@
104 "End Late"
105 "Enter"
106 "Enter a new name for your playlist"
107-"enter your search terms"
108 "Enter your search terms"
109+"enter your search terms"
110 "Episode"
111 "Episode Number"
112 "Error"
113@@ -310,6 +313,7 @@
114 "Hosted by"
115 "Hour"
116 "Hour Format"
117+"HTML5 Streaming"
118 "hue"
119 "Identifiable episode"
120 "Ignore generic shows"
121@@ -325,19 +329,26 @@
122 "info: dont record"
123 "info: flvplayer"
124     <p>
125-    Flash video playback is currently only a proof-of-concept and should be
126-    considered <b>EXTREMELY</b> experimental, which is why it has been disabled by
127-    default.
128-    </p>
129+    Video streaming in mythweb is currently based on HTML5 webm/ogg and flash flv.
130+    <strong>NOTE</strong>: Today there is no single format supported by all browsers.
131+    By this different browsers needs different configuration<br>
132+    mythweb provides following streaming support:
133+    <ul>
134+    <li><strong>Google Chrome:</strong>  Highly unstable. Flash fall-back adviced. Uncheck "HTML5 Streaming"</li>
135+    <li><strong>Mozilla Firefox:</strong>  Quite stable. Enable HTML5 streaming by checking "HTML5 Streaming"</li>
136+    <li><strong>Safari (MacOS):</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
137+    <li><strong>Safati (iOS):</strong>  Flash is not supported so no fall-back possible. Use HLS client"</li>
138+    <li><strong>IE8 and older:</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
139+    <li><strong>IE9 and newer:</strong>  Highly unstable. Prefer Flash fall-back by unchecking "HTML5 Streaming"</li>
140+    </ul>
141     <p>
142-    It currently expects that ffmpeg is installed and compiled with mp3
143-    support, and that the recordings files are accessible to your webserver
144-    userid.  It probably won't work with Nupplevideo files, and in the end,
145-    it may just not work at all (or maybe even worse).
146+    This feature expects that myth is build with libx264, libvpx and libtheora libraries. Also for
147+    flash fallback, Adobe Flash player must be installed on client side.
148     </p>
149     <p>
150-    Enable this feature at your own risk, and don't expect too much
151-    official help until it has left the experimental phase.
152+    Enabling HTML5 streaming may cause not stable streaming as majority browsers (except Mozzilla)
153+    offers not good video playabck stability. By this prefered mode is Flash fall-back mode
154+    (with "HTML5 Streaming" option disabled).
155     </p>
156 "info: hidden advanced schedule"
157     The advanced scheduling options are currently hidden.
158@@ -358,8 +369,8 @@
159 "Internet Reference #"
160 "Jump"
161 "Jump Points"
162-"Jump to"
163 "Jump To"
164+"Jump to"
165 "JumpPoints Editor"
166 "Key Bindings"
167 "Key bindings"
168@@ -377,8 +388,8 @@
169 "Left"
170 "left"
171 "leftl"
172-"length"
173 "Length"
174+"length"
175 "Length (min)"
176 "Length in minutes"
177 "Listing &quot;Jump to&quot;"
178@@ -414,8 +425,8 @@
179 "Modify unidentified episodes"
180 "Monday"
181 "Mono"
182-"more"
183 "More"
184+"more"
185 "Move Item Down in Playlist"
186 "Move Item Up in Playlist"
187 "movie"
188@@ -423,6 +434,7 @@
189 "Movies"
190 "Movies, 3&frac12; Stars or more"
191 "Movies, Stinkers (2 Stars or less)"
192+"mplexid"
193 "Music"
194 "Music Specials"
195 "My Session"
196@@ -457,6 +469,7 @@
197 "New Titles, Premieres"
198 "No"
199 "NO DATA"
200+"No episodes"
201 "No Frontends allow remote control."
202 "No Genre"
203 "No matches found"
204@@ -515,6 +528,7 @@
205 "Play"
206 "Play Recording on Frontend"
207 "Play this Album Now"
208+"Play this Artist Now"
209 "Play This Playlist Now"
210 "Play this Playlist Now"
211 "Play this Song Now"
212@@ -563,8 +577,8 @@
213 "Priority for movies by the year of release"
214 "Priority when shown once"
215 "Produced by"
216-"profile"
217 "Profile"
218+"profile"
219 "Program Categories"
220 "Program Detail"
221 "Program ID"
222@@ -582,8 +596,8 @@
223 "Rating:"
224 "Recently Added Albums"
225 "Recently completed jobs"
226-"Recently Played Songs"
227 "Recently Played Albums"
228+"Recently Played Songs"
229 "recgroup"
230 "Recommend Videos"
231 "Recommended"
232@@ -688,8 +702,8 @@
233 "Reset template and skin to defaults"
234 "Retry"
235 "Return to Statistics Page"
236-"right"
237 "Right"
238+"right"
239 "Root Directory"
240 "Rows to show between timeslot info"
241 "Running"
242@@ -732,11 +746,10 @@
243 "Select the correct show"
244 "Server returned invalid data when attempting to retrieve metadata."
245 "Server Statistics"
246-"mplexid"
247 "serviceid"
248 "Set Host"
249-"settings"
250 "Settings"
251+"settings"
252 "Settings Table"
253 "settings/stream: protocol"
254     Many media players are incapable of playing files that are streamed from<br/>
255@@ -829,9 +842,9 @@
256 "Toggle Interactive Mode"
257 "Too Many"
258 "Top $1"
259-"Top Played Songs"
260 "Top Played Albums"
261 "Top Played Artist"
262+"Top Played Songs"
263 "Top Rated Songs"
264 "Total Length"
265 "Total Recorded"
266@@ -849,8 +862,8 @@
267 "TV"
268 "TV functions, including recorded programs."
269 "TV.com"
270-"type"
271 "Type"
272+"type"
273 "Uncategorized"
274 "Undelete"
275 "Undelete: $1"
276diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/_shared/lang/Polish.lang mythtv-master-build-new/mythplugins/mythweb/modules/_shared/lang/Polish.lang
277--- mythtv-master-build-old/mythplugins/mythweb/modules/_shared/lang/Polish.lang        2016-11-25 11:02:40.000000000 +0100
278+++ mythtv-master-build-new/mythplugins/mythweb/modules/_shared/lang/Polish.lang        2016-11-25 12:00:51.000000000 +0100
279@@ -44,7 +44,9 @@
280 "%s Songs (%s)"
281 "(height is calculated automatically from the recording aspect ratio)"
282 "1 - Lowest"
283+"1080"
284 "4 - Highest"
285+"720"
286 "Aborted"
287 "Aborting"
288 "Action"
289@@ -53,6 +55,7 @@
290     Uaktywnij
291 "Add"
292 "Add Album to Current Playlist"
293+"Add Artist to Current Playlist"
294 "Add Mix"
295 "Add Song to Current Playlist"
296 "add string"
297@@ -66,8 +69,8 @@
298     Zaawansowane opcje
299 "Advanced Search"
300     Szukanie zaawansowane
301-"airdate"
302 "Airdate"
303+"airdate"
304 "Airtime"
305 "Album Listing"
306 "Album Tracks"
307@@ -162,10 +165,10 @@
308 "Caps Lock"
309 "Cast"
310     Obsada
311-"category"
312-    kategoria
313 "Category"
314     Kategoria
315+"category"
316+    kategoria
317 "Category Legend"
318     Legenda kategorie
319 "CC"
320@@ -228,6 +231,7 @@
321 "Custom Schedule"
322 "Custom Search"
323 "Customize Screens"
324+"Damaged"
325 "Data"
326     Dane
327 "Database"
328@@ -258,10 +262,10 @@
329 "DELETE this Saved Playlist"
330 "DeleteKey"
331 "Depending on the Search type this is where you enter actual main search commands"
332-"description"
333-    opis
334 "Description"
335     Opis
336+"description"
337+    opis
338 "Destination"
339     Cel
340 "Details for: $1"
341@@ -379,6 +383,7 @@
342     Godzina
343 "Hour Format"
344     Format godziny
345+"HTML5 Streaming"
346 "hue"
347     kolor
348 "Identifiable episode"
349@@ -392,6 +397,28 @@
350 "info: default recording"
351 "info: dont record"
352 "info: flvplayer"
353+    <p>
354+    Video streaming in mythweb is currently based on HTML5 webm/ogg and flash flv.
355+    <strong>NOTE</strong>: Today there is no single format supported by all browsers..
356+    By this different browsers needs different configuration<br>
357+    mythweb provides following streaming support:
358+    <ul>
359+    <li><strong>Google Chrome:</strong>  Highly unstable. Flash fall-back adviced. Uncheck "HTML5 Streaming"</li>
360+    <li><strong>Mozilla Firefox:</strong>  v52.0 or newer is stable and plays perfectly. Enable HTML5 streaming by checking "HTML5 Streaming"</li>
361+    <li><strong>Safari (MacOS):</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
362+    <li><strong>Safati (iOS):</strong>  Flash is not supported so no fall-back possible. Use HLS client"</li>
363+    <li><strong>IE8 and older:</strong>  Only Flash fall-back possible so uncheck "HTML5 Streaming"</li>
364+    <li><strong>IE9 and newer:</strong>  Highly unstable. Prefer Flash fall-back by unchecking "HTML5 Streaming"</li>
365+    </ul>
366+    <p>
367+    This feature expects that myth is build with libx264, libvpx and libtheora libraries. Also for
368+    flash fallback, Adobe Flash player must be installed on client side.
369+    </p>
370+    <p>
371+    Enabling HTML5 streaming may cause not stable streaming as majority browsers (except Mozzilla).
372+    offers not good video playabck stability. By this prefered mode is Flash fall-back mode.
373+    (with "HTML5 Streaming" option disabled).
374+    </p>
375 "info: hidden advanced schedule"
376 "info: record this"
377 "info:forget old"
378@@ -403,10 +430,10 @@
379 "Jump"
380     Skocz
381 "Jump Points"
382-"Jump to"
383-    Skocz do
384 "Jump To"
385     Skocz Do
386+"Jump to"
387+    Skocz do
388 "JumpPoints Editor"
389 "Key Bindings"
390 "Key bindings"
391@@ -423,13 +450,13 @@
392 "Last recording"
393 "Last showing of each episode"
394 "Later"
395-"left"
396 "Left"
397+"left"
398 "leftl"
399-"length"
400-    długość
401 "Length"
402     Długość
403+"length"
404+    długość
405 "Length (min)"
406     Czas trwania (minut):
407 "Length in minutes"
408@@ -463,7 +490,6 @@
409 "Missing Cover"
410 "Modify priority by star rating (0.0 to 1.0 for movies only)"
411 "Modify priority for a station on an input"
412-"Modify priority for all inputs on a card"
413 "Modify priority for an input (Input priority)"
414 "Modify priority for every card on a host"
415 "Modify unidentified episodes"
416@@ -481,6 +507,7 @@
417 "Movies, 3&frac12; Stars or more"
418     Filmy 3&frac12; gwiazdy lub więcej
419 "Movies, Stinkers (2 Stars or less)"
420+"mplexid"
421 "Music"
422     Muzyka
423 "Music Specials"
424@@ -520,6 +547,7 @@
425     Nowe tytuły, premiery
426 "No"
427 "NO DATA"
428+"No episodes"
429 "No Frontends allow remote control."
430 "No Genre"
431 "No matches found"
432@@ -579,6 +607,7 @@
433 "Play"
434 "Play Recording on Frontend"
435 "Play this Album Now"
436+"Play this Artist Now"
437 "Play This Playlist Now"
438 "Play this Playlist Now"
439 "Play this Song Now"
440@@ -649,6 +678,7 @@
441 "Rating:"
442 "Recently Added Albums"
443 "Recently completed jobs"
444+"Recently Played Albums"
445 "Recently Played Songs"
446 "recgroup"
447 "Recommend Videos"
448@@ -764,8 +794,8 @@
449 "Reset template and skin to defaults"
450 "Retry"
451 "Return to Statistics Page"
452-"right"
453 "Right"
454+"right"
455 "Root Directory"
456 "Rows to show between timeslot info"
457 "Running"
458@@ -820,7 +850,6 @@
459 "Server returned invalid data when attempting to retrieve metadata."
460 "Server Statistics"
461 "serviceid"
462-"mplexid"
463 "Set Host"
464 "Settings"
465     Ustawienia
466@@ -909,20 +938,22 @@
467 "Time Span"
468 "Time Stretch"
469 "Timeslot size"
470-"title"
471-    tytuł
472 "Title"
473     Tytuł
474+"title"
475+    tytuł
476 "Title Match"
477-"Title search"
478 "Title Search"
479     Szukanie Tytułu
480+"Title search"
481 "Title Search:"
482 "Title:"
483 "Toggle Interactive Mode"
484 "Too Many"
485 "Top $1"
486     Szczyt $1
487+"Top Played Albums"
488+"Top Played Artist"
489 "Top Played Songs"
490 "Top Rated Songs"
491 "Total Length"
492@@ -932,20 +963,21 @@
493     Całkowity Czas
494 "Total Time:  %s"
495 "Track #%s from the album '%s'"
496+"Trakt.tv Home Page"
497 "Transcode"
498 "Transcoded"
499-"transcoder"
500 "Transcoder"
501+"transcoder"
502 "Tuesday"
503     Wtorek
504 "Tuner Busy"
505 "TV"
506 "TV functions, including recorded programs."
507 "TV.com"
508-"type"
509-    typ
510 "Type"
511     Typ
512+"type"
513+    typ
514 "Uncategorized"
515 "Undelete"
516 "Undelete: $1"
517diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/stream/handler.pl mythtv-master-build-new/mythplugins/mythweb/modules/stream/handler.pl
518--- mythtv-master-build-old/mythplugins/mythweb/modules/stream/handler.pl       2016-11-25 11:02:02.000000000 +0100
519+++ mythtv-master-build-new/mythplugins/mythweb/modules/stream/handler.pl       2016-11-25 11:04:40.046666653 +0100
520@@ -12,14 +12,35 @@
521 
522     require "modules/$Path[0]/tv.pl";
523 
524+# Use the MythTV Services API URL if the $filename URL is not local
525     unless ($filename) {
526-        print header(),
527-              "$basename does not exist in any recognized storage group directories for this host.";
528-        exit;
529+        # Retrieve the backend IP and port
530+        $sh = $dbh->prepare('SELECT data FROM settings WHERE value=?');
531+        $sh->execute('BackendServerIP');
532+        my ($backend_server_ip)   = $sh->fetchrow_array;
533+        $sh->execute('BackendStatusPort');
534+        my ($backend_status_port) = $sh->fetchrow_array;
535+        $sh->finish();
536+
537+        # Reformat the recording start time
538+        use HTTP::Date qw(time2isoz);
539+        $starttime_isoz = time2isoz($starttime);
540+        $starttime_isoz =~ s/ /T/g;
541+
542+        # Generate the MythTV Services API URL
543+        $filename = "http://${backend_server_ip}:${backend_status_port}/Content/GetRecording?ChanId=${chanid}&StartTime=${starttime_isoz}";
544     }
545 
546+# HTML5 video/ogv
547+    if ($ENV{'REQUEST_URI'} =~ /\.ogv$/i) {
548+        require "modules/$Path[0]/stream_ogv.pl";
549+    }
550+# HTML5 video/webm
551+    elsif ($ENV{'REQUEST_URI'} =~ /\.webm$/i) {
552+        require "modules/$Path[0]/stream_webm.pl";
553+    }
554 # ASX mode?
555-    if ($ENV{'REQUEST_URI'} =~ /\.asx$/i) {
556+    elsif ($ENV{'REQUEST_URI'} =~ /\.asx$/i) {
557         require "modules/$Path[0]/stream_asx.pl";
558     }
559 # Flash?
560diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/stream/stream_ogv.pl mythtv-master-build-new/mythplugins/mythweb/modules/stream/stream_ogv.pl
561--- mythtv-master-build-old/mythplugins/mythweb/modules/stream/stream_ogv.pl    1970-01-01 01:00:00.000000000 +0100
562+++ mythtv-master-build-new/mythplugins/mythweb/modules/stream/stream_ogv.pl    2016-11-25 11:04:40.046666653 +0100
563@@ -0,0 +1,186 @@
564+#!/usr/bin/perl
565+#
566+# MythWeb Streaming/Download module
567+#
568+# @url       $URL$
569+# @date      $2016/11/23$
570+# @version   $v1.0$
571+# @author    $Piotr Oniszczuk$
572+#
573+
574+    use POSIX qw(ceil floor);
575+
576+# round to the nearest even integer
577+    sub round_even {
578+        my ($in) = @_;
579+        my $n = floor($in);
580+        return ($n % 2 == 0) ? $n : ceil($in);
581+    }
582+
583+    our $ffmpeg_pid;
584+    our $ffmpeg_pgid;
585+
586+# Shutdown cleanup
587+    $ffmpeg_pgid = setpgrp(0,0);
588+    $SIG{'TERM'} = \&shutdown_handler;
589+    $SIG{'PIPE'} = \&shutdown_handler;
590+    END {
591+        shutdown_handler();
592+    }
593+    sub shutdown_handler {
594+        kill(1, $ffmpeg_pid) if ($ffmpeg_pid);
595+        kill(-1, $ffmpeg_pid) if ($ffmpeg_pid);
596+    }
597+
598+# Find ffmpeg
599+    $ffmpeg = '';
600+    foreach my $path (split(/:/, $ENV{'PATH'}.':/usr/local/bin:/usr/bin'), '.') {
601+        if (-e "$path/mythffmpeg") {
602+            $ffmpeg = "$path/mythffmpeg";
603+            last;
604+        }
605+        if (-e "$path/ffmpeg") {
606+            $ffmpeg = "$path/ffmpeg";
607+            last;
608+        }
609+        elsif ($^O eq 'darwin' && -e "$path/ffmpeg.app") {
610+            $ffmpeg = "$path/ffmpeg.app";
611+            last;
612+        }
613+    }
614+
615+# Load some conversion settings from the database
616+    $sh = $dbh->prepare('SELECT data FROM settings WHERE value=? AND hostname IS NULL');
617+    $sh->execute('WebFLV_w');
618+    my ($width)    = $sh->fetchrow_array;
619+    $sh->execute('WebFLV_vb');
620+    my ($vbitrate) = $sh->fetchrow_array;
621+    $sh->execute('WebFLV_ab');
622+    my ($abitrate) = $sh->fetchrow_array;
623+    $sh->finish();
624+
625+# auto-detect height based on aspect ratio
626+    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
627+                        'AND starttime=FROM_UNIXTIME(?) AND type=30 ' .
628+                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
629+    $sh->execute($chanid,$starttime);
630+    $x = $sh->fetchrow_array;           # type = 30
631+    $sh->finish();
632+
633+    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
634+                        'AND starttime=FROM_UNIXTIME(?) AND type=31 ' .
635+                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
636+    $sh->execute($chanid,$starttime);
637+    $y = $sh->fetchrow_array if ($x);   # type = 31
638+    $sh->finish();
639+
640+    if (!$x || !$y || $x <= 720) {      # <=720 means SD
641+        $sh = $dbh->prepare('SELECT recordedmarkup.type, ' .
642+               'recordedmarkup.data '.
643+               'FROM recordedmarkup ' .
644+               'WHERE recordedmarkup.chanid = ? ' .
645+               'AND recordedmarkup.starttime = FROM_UNIXTIME(?)  ' .
646+               'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' .
647+               'GROUP BY recordedmarkup.type  ' .
648+               'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' .
649+               '   FROM recordedmarkup AS rm ' .
650+               '   WHERE rm.chanid = recordedmarkup.chanid ' .
651+               '   AND rm.starttime = recordedmarkup.starttime ' .
652+               '   AND rm.type IN (10, 11, 12, 13, 14)  ' .
653+               '   AND rm.mark > recordedmarkup.mark ' .
654+               '   ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' .
655+               'LIMIT 1');
656+        $sh->execute($chanid,$starttime);
657+        $aspect = $sh->fetchrow_hashref;
658+        $sh->finish();
659+
660+        if( $aspect->{'type'} == 10 ) {
661+            $x = $y = 1;
662+        } elsif( $aspect->{'type'}== 11 ) {
663+            $x = 4; $y = 3;
664+        } elsif( $aspect->{'type'}== 12 ) {
665+            $x = 16; $y = 9;
666+        } elsif( $aspect->{'type'}== 13 ) {
667+            $x = 2.21; $y = 1;
668+        } elsif( $aspect->{'type'}== 14 ) {
669+            $x = $aspect->{'data'}; $y = 1000000;
670+        } else {
671+            $x = 4; $y = 3;
672+        }
673+    }
674+    $height = round_even($width * ($y/$x));
675+
676+    $width    = 320 unless ($width    && $width    > 1);
677+    $height   = 240 unless ($height   && $height   > 1);
678+    $vbitrate = 256 unless ($vbitrate && $vbitrate > 1);
679+    $abitrate = 64  unless ($abitrate && $abitrate > 1);
680+
681+# build appropriate encoder commad
682+    my $ffmpeg_command = $ffmpeg
683+                        .' -y'
684+                        .' -i '.shell_escape("$filename")
685+                        .' -s '.shell_escape("${width}x${height}")
686+                        .' -g 30'
687+                        .' -r 24'
688+                        .' -f ogg'
689+                        .' -codec:a libvorbis'
690+                        .' -codec:v libtheora'
691+                        .' -strict -2'
692+                        .' -deinterlace'
693+                        .' -async 2'
694+                        .' -ac 2'
695+                        .' -ar 44100'
696+                        .' -b:a '.shell_escape("${abitrate}k")
697+                        .' -b:v '.shell_escape("${vbitrate}k")
698+                        .' -sn'
699+                        .' /dev/stdout 2>/dev/null |';
700+
701+# start to encode content
702+    $ffmpeg_pid = open(DATA, $ffmpeg_command);
703+    unless ($ffmpeg_pid) {
704+        print header(),
705+                "Can't execute ffmpeg. Command was: $!\n${ffmpeg_command}";
706+        exit;
707+    }
708+
709+# Guess the filesize based on duration and bitrate. This allows progressive download. Print header with
710+# guessed file size
711+    my $lengthSec;
712+    my $length;
713+    my $range;
714+    $dur = `$ffmpeg -i $filename 2>&1 | /usr/bin/grep "Duration" | /usr/bin/cut -d ' ' -f 4 | /bin/sed s/,//`;
715+    if ($dur && $dur =~ /\d*:\d*:.*/) {
716+        @times = split(':',$dur);
717+        $lengthSec = $times[0]*3600+$times[1]*60+$times[2];
718+        $length = int($lengthSec);
719+        $size = int(0.85*$lengthSec*($vbitrate*1024+$abitrate*1024)/8);
720+        $range = $size-1;
721+        print header(-type                  => 'video/ogg',
722+                     -Content_length        => $size,
723+                     -Accept_Ranges         => 'bytes',
724+                     -Timing_Allow_Origin   => '*',
725+                     -Content_Duration      => $length,
726+                     -Content_Range         => 'bytes 0-'.$range.'/'.$size,
727+                     -X_Content_Duration    => $length
728+                    );
729+    } else {
730+        print header(-type                  => 'video/ogg');
731+
732+    }
733+
734+    if ($ENV{'REQUEST_METHOD'} eq 'HEAD') {
735+        exit;
736+    }
737+
738+# send encoded data to browser
739+    my $buffer;
740+    while (read DATA, $buffer, 262144) {
741+        unless (print $buffer ) {
742+            last;
743+        }
744+    }
745+
746+    close DATA;
747+
748+    1;
749diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/stream/stream_webm.pl mythtv-master-build-new/mythplugins/mythweb/modules/stream/stream_webm.pl
750--- mythtv-master-build-old/mythplugins/mythweb/modules/stream/stream_webm.pl   1970-01-01 01:00:00.000000000 +0100
751+++ mythtv-master-build-new/mythplugins/mythweb/modules/stream/stream_webm.pl   2016-11-25 11:04:40.046666653 +0100
752@@ -0,0 +1,186 @@
753+#!/usr/bin/perl
754+#
755+# MythWeb Streaming/Download module
756+#
757+# @url       $URL$
758+# @date      $2016/11/23$
759+# @version   $v1.0$
760+# @author    $Piotr Oniszczuk$
761+#
762+
763+    use POSIX qw(ceil floor);
764+
765+# round to the nearest even integer
766+    sub round_even {
767+        my ($in) = @_;
768+        my $n = floor($in);
769+        return ($n % 2 == 0) ? $n : ceil($in);
770+    }
771+
772+    our $ffmpeg_pid;
773+    our $ffmpeg_pgid;
774+
775+# Shutdown cleanup
776+    $ffmpeg_pgid = setpgrp(0,0);
777+    $SIG{'TERM'} = \&shutdown_handler;
778+    $SIG{'PIPE'} = \&shutdown_handler;
779+    END {
780+        shutdown_handler();
781+    }
782+    sub shutdown_handler {
783+        kill(1, $ffmpeg_pid) if ($ffmpeg_pid);
784+        kill(-1, $ffmpeg_pid) if ($ffmpeg_pid);
785+    }
786+
787+# Find ffmpeg
788+    $ffmpeg = '';
789+    foreach my $path (split(/:/, $ENV{'PATH'}.':/usr/local/bin:/usr/bin'), '.') {
790+        if (-e "$path/mythffmpeg") {
791+            $ffmpeg = "$path/mythffmpeg";
792+            last;
793+        }
794+        if (-e "$path/ffmpeg") {
795+            $ffmpeg = "$path/ffmpeg";
796+            last;
797+        }
798+        elsif ($^O eq 'darwin' && -e "$path/ffmpeg.app") {
799+            $ffmpeg = "$path/ffmpeg.app";
800+            last;
801+        }
802+    }
803+
804+# Load some conversion settings from the database
805+    $sh = $dbh->prepare('SELECT data FROM settings WHERE value=? AND hostname IS NULL');
806+    $sh->execute('WebFLV_w');
807+    my ($width)    = $sh->fetchrow_array;
808+    $sh->execute('WebFLV_vb');
809+    my ($vbitrate) = $sh->fetchrow_array;
810+    $sh->execute('WebFLV_ab');
811+    my ($abitrate) = $sh->fetchrow_array;
812+    $sh->finish();
813+
814+# auto-detect height based on aspect ratio
815+    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
816+                        'AND starttime=FROM_UNIXTIME(?) AND type=30 ' .
817+                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
818+    $sh->execute($chanid,$starttime);
819+    $x = $sh->fetchrow_array;           # type = 30
820+    $sh->finish();
821+
822+    $sh = $dbh->prepare('SELECT data FROM recordedmarkup WHERE chanid=? ' .
823+                        'AND starttime=FROM_UNIXTIME(?) AND type=31 ' .
824+                        'AND data IS NOT NULL ORDER BY mark LIMIT 1');
825+    $sh->execute($chanid,$starttime);
826+    $y = $sh->fetchrow_array if ($x);   # type = 31
827+    $sh->finish();
828+
829+    if (!$x || !$y || $x <= 720) {      # <=720 means SD
830+        $sh = $dbh->prepare('SELECT recordedmarkup.type, ' .
831+               'recordedmarkup.data '.
832+               'FROM recordedmarkup ' .
833+               'WHERE recordedmarkup.chanid = ? ' .
834+               'AND recordedmarkup.starttime = FROM_UNIXTIME(?)  ' .
835+               'AND recordedmarkup.type IN (10, 11, 12, 13, 14) ' .
836+               'GROUP BY recordedmarkup.type  ' .
837+               'ORDER BY SUM((SELECT IFNULL(rm.mark, recordedmarkup.mark) ' .
838+               '   FROM recordedmarkup AS rm ' .
839+               '   WHERE rm.chanid = recordedmarkup.chanid ' .
840+               '   AND rm.starttime = recordedmarkup.starttime ' .
841+               '   AND rm.type IN (10, 11, 12, 13, 14)  ' .
842+               '   AND rm.mark > recordedmarkup.mark ' .
843+               '   ORDER BY rm.mark ASC LIMIT 1)- recordedmarkup.mark) DESC ' .
844+               'LIMIT 1');
845+        $sh->execute($chanid,$starttime);
846+        $aspect = $sh->fetchrow_hashref;
847+        $sh->finish();
848+
849+        if( $aspect->{'type'} == 10 ) {
850+            $x = $y = 1;
851+        } elsif( $aspect->{'type'}== 11 ) {
852+            $x = 4; $y = 3;
853+        } elsif( $aspect->{'type'}== 12 ) {
854+            $x = 16; $y = 9;
855+        } elsif( $aspect->{'type'}== 13 ) {
856+            $x = 2.21; $y = 1;
857+        } elsif( $aspect->{'type'}== 14 ) {
858+            $x = $aspect->{'data'}; $y = 1000000;
859+        } else {
860+            $x = 4; $y = 3;
861+        }
862+    }
863+    $height = round_even($width * ($y/$x));
864+
865+    $width    = 320 unless ($width    && $width    > 1);
866+    $height   = 240 unless ($height   && $height   > 1);
867+    $vbitrate = 256 unless ($vbitrate && $vbitrate > 1);
868+    $abitrate = 64  unless ($abitrate && $abitrate > 1);
869+
870+# build appropriate encoder commad
871+    my $ffmpeg_command = $ffmpeg
872+                        .' -y'
873+                        .' -i '.shell_escape("$filename")
874+                        .' -s '.shell_escape("${width}x${height}")
875+                        .' -g 30'
876+                        .' -r 24'
877+                        .' -f webm'
878+                        .' -codec:a libvorbis'
879+                        .' -codec:v libvpx'
880+                        .' -cpu-used -8'
881+                        .' -deadline realtime'
882+                        .' -strict -2'
883+                        .' -deinterlace'
884+                        .' -async 2'
885+                        .' -ac 2'
886+                        .' -ar 44100'
887+                        .' -b:a '.shell_escape("${abitrate}k")
888+                        .' -b:v '.shell_escape("${vbitrate}k")
889+                        .' -sn'
890+                        .' /dev/stdout 2>/dev/null |';
891+
892+# start to encode content
893+    $ffmpeg_pid = open(DATA, $ffmpeg_command);
894+    unless ($ffmpeg_pid) {
895+        print header(),
896+                "Can't execute ffmpeg. Command was: $!\n${ffmpeg_command}";
897+        exit;
898+    }
899+
900+# Guess the filesize based on duration and bitrate. This allows progressive download. Print header with
901+# guessed file size
902+    my $lengthSec;
903+    my $length;
904+    my $range;
905+    $dur = `$ffmpeg -i $filename 2>&1 | /usr/bin/grep "Duration" | /usr/bin/cut -d ' ' -f 4 | /bin/sed s/,//`;
906+    if ($dur && $dur =~ /\d*:\d*:.*/) {
907+        @times = split(':',$dur);
908+        $lengthSec = $times[0]*3600+$times[1]*60+$times[2];
909+        $length = int($lengthSec);
910+        $size = int(0.90*$lengthSec*($vbitrate*1024+$abitrate*1024)/8);
911+        $range = $size-1;
912+        print header(-type                  => 'video/webm',
913+                     -Content_length        => $size,
914+                     -Accept_Ranges         => 'bytes',
915+                     -Timing_Allow_Origin   => '*',
916+                     -Content_Duration      => $length,
917+                     -Content_Range         => 'bytes 0-'.$range.'/'.$size,
918+                     -X_Content_Duration    => $length
919+                    );
920+    } else {
921+        print header(-type                  => 'video/webm');
922+
923+    }
924+
925+    if ($ENV{'REQUEST_METHOD'} eq 'HEAD') {
926+        exit;
927+    }
928+
929+# send encoded data to browser
930+    my $buffer;
931+    while (read DATA, $buffer, 262144) {
932+        unless (print $buffer ) {
933+            last;
934+        }
935+    }
936+
937+    close DATA;
938+
939+    1;
940diff -Naur mythtv-master-build-old/mythplugins/mythweb/modules/tv/tmpl/default/detail.php mythtv-master-build-new/mythplugins/mythweb/modules/tv/tmpl/default/detail.php
941--- mythtv-master-build-old/mythplugins/mythweb/modules/tv/tmpl/default/detail.php      2016-11-25 11:02:40.000000000 +0100
942+++ mythtv-master-build-new/mythplugins/mythweb/modules/tv/tmpl/default/detail.php      2016-11-25 11:05:17.746666670 +0100
943@@ -37,6 +37,12 @@
944 <script type="text/javascript">
945 <!--
946 
947+// Android devices need the following to play HTML5 video. Taken from ticket #10529
948+    var video = document.getElementById('video');
949+    video.addEventListener('click', function(){
950+        video.play();
951+    }, false);
952+
953 // Keep track of the autoexpire flag
954     var autoexpire = <?php echo $program->auto_expire ? 1 : 0 ?>;
955 
956@@ -665,26 +671,21 @@
957 
958             <div class="x-pixmap">
959 <?php   if (setting('WebFLV_on')) { ?>
960+<?php   if (setting('WebHTML5Stream_on')) { ?>
961+            <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) ?>">
962+                <source src="<?php echo video_url($program, 'webm'); ?>" type="video/webm" />
963+                <source src="<?php echo video_url($program, 'ogv'); ?>" type="video/ogg" />
964+<?php       } ?>
965 <?php       if (file_exists('js/libs/flowplayer/flowplayer.swf')) { ?>
966-
967-
968-          <!-- this A tag is where your Flowplayer will be placed. it can be anywhere -->
969-            <a href=""
970-                style="display:block;width:<?php echo $flv_w ?>px;height:<?php echo $flv_h ?>px"
971-                id="player">
972-            </a>
973-
974-            <!-- this will install flowplayer inside previous A- tag. -->
975-            <script>
976-                flowplayer(
977-                    "player",
978-                    "<?php echo root_url ?>js/libs/flowplayer/flowplayer.swf", {
979-                    playlist: [
980+                <a href="" style="display:block;width:<?php echo $flv_w ?>px;height:<?php echo $flv_h ?>px" id="flash-player"></a>
981+                <script>
982+                    flowplayer("flash-player","<?php echo root_url ?>js/libs/flowplayer/flowplayer.swf", {
983+                        playlist: [
984                         // this first PNG clip works as a splash image
985                         {
986                             url: '<?php echo $program->thumb_url($flv_w,0) ?>',
987                             scaling: 'orig'
988-                            },
989+                        },
990                         // Then we have the video
991                         {
992                             url: "<?php echo video_url($program, 'flv'); ?>",
993@@ -694,10 +695,10 @@
994                             // Would be nice to auto-buffer, but we don't want to
995                             // waste bandwidth and CPU on the remote machine.
996                             autoBuffering: false
997-                            }
998+                        }
999                         ]}
1000                     );
1001-            </script>
1002+                </script>
1003 <?php       } elseif (file_exists('modules/tv/MFPlayer.swf')) { ?>
1004                     <script language="JavaScript" type="text/javascript">
1005                     <!--
1006@@ -813,7 +814,12 @@
1007 <?php   } else { ?>
1008                 <a href="<?php echo $program->url ?>" title="<?php echo t('Direct Download') ?>"
1009                     ><img src="<?php echo $program->thumb_url($flv_w,0) ?>" width="<?php echo $flv_w ?>"></a>
1010-<?php   } ?></td>
1011+<?php   } ?>
1012+<?php   if (setting('WebHTML5Stream_on')) { ?>
1013+            </video>
1014+<?php   } ?>
1015+
1016+        </td>
1017             </div>
1018             <div class="x-links">
1019                 <a href="<?php echo video_url($program, 'asx') ?>" title="<?php echo t('ASX Stream') ?>"
1020diff -Naur mythtv-master-build-old/mythplugins/mythweb/mythweb.conf.apache mythtv-master-build-new/mythplugins/mythweb/mythweb.conf.apache
1021--- mythtv-master-build-old/mythplugins/mythweb/mythweb.conf.apache     2016-11-25 11:02:01.000000000 +0100
1022+++ mythtv-master-build-new/mythplugins/mythweb/mythweb.conf.apache     2016-11-25 11:04:40.053333319 +0100
1023@@ -194,6 +194,10 @@
1024     # those are, so we should tell it.
1025         AddType video/nuppelvideo   .nuv
1026 
1027+    # Support HTML5 video formats which can be encoded and streamed "on-the-fly"
1028+        AddType video/ogg           .ogv .ogg
1029+        AddType video/webm          .webm
1030+
1031     # Specify the MIME type for favicon.ico in case the server configuration
1032     # doesn't or in case the server configuration uses the IANA-approved MIME type
1033     # (image/vnd.microsoft.icon)--which most browsers won't recognize.