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@…, 9 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.