<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[165536] trunk/LayoutTests</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/165536">165536</a></dd>
<dt>Author</dt> <dd>ossy@webkit.org</dd>
<dt>Date</dt> <dd>2014-03-13 09:30:06 -0700 (Thu, 13 Mar 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>[GStreamer] WebKit gets stalled when trying to play a stream
https://bugs.webkit.org/show_bug.cgi?id=125926
Patch by Andres Gomez <agomez@igalia.com> on 2014-03-13
Reviewed by Eric Carlson.
Added new test for checking the playback on Icecast/Shoutcast
streamed media.
* http/tests/media/media-play-stream-chunked-icy-expected.txt: Added.
* http/tests/media/media-play-stream-chunked-icy.html: Added.
* http/tests/media/resources/create-id3-db.php: Added.
* http/tests/media/resources/serve-video.php: Added support for
chunked streams and Icecast/Shoutcast headers.
* http/tests/resources/dir-helpers.php: Added.
* http/tests/resources/portabilityLayer.php: Added a couple of new
fallback functions when they are not available in old PHP
versions.
* media/content/metadata.db: Added.
* media/content/silence.mp3: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsmediaresourcesservevideophp">trunk/LayoutTests/http/tests/media/resources/serve-video.php</a></li>
<li><a href="#trunkLayoutTestshttptestsresourcesportabilityLayerphp">trunk/LayoutTests/http/tests/resources/portabilityLayer.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestsmediamediaplaystreamchunkedicyexpectedtxt">trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsmediamediaplaystreamchunkedicyhtml">trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy.html</a></li>
<li><a href="#trunkLayoutTestshttptestsmediaresourcescreateid3dbphp">trunk/LayoutTests/http/tests/media/resources/create-id3-db.php</a></li>
<li><a href="#trunkLayoutTestshttptestsresourcesdirhelpersphp">trunk/LayoutTests/http/tests/resources/dir-helpers.php</a></li>
<li><a href="#trunkLayoutTestsmediacontentmetadatadb">trunk/LayoutTests/media/content/metadata.db</a></li>
<li><a href="#trunkLayoutTestsmediacontentsilencemp3">trunk/LayoutTests/media/content/silence.mp3</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (165535 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-03-13 16:28:24 UTC (rev 165535)
+++ trunk/LayoutTests/ChangeLog        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2014-03-13 Andres Gomez <agomez@igalia.com>
+
+ [GStreamer] WebKit gets stalled when trying to play a stream
+ https://bugs.webkit.org/show_bug.cgi?id=125926
+
+ Reviewed by Eric Carlson.
+
+ Added new test for checking the playback on Icecast/Shoutcast
+ streamed media.
+
+ * http/tests/media/media-play-stream-chunked-icy-expected.txt: Added.
+ * http/tests/media/media-play-stream-chunked-icy.html: Added.
+ * http/tests/media/resources/create-id3-db.php: Added.
+ * http/tests/media/resources/serve-video.php: Added support for
+ chunked streams and Icecast/Shoutcast headers.
+ * http/tests/resources/dir-helpers.php: Added.
+ * http/tests/resources/portabilityLayer.php: Added a couple of new
+ fallback functions when they are not available in old PHP
+ versions.
+ * media/content/metadata.db: Added.
+ * media/content/silence.mp3: Added.
+
</ins><span class="cx"> 2014-03-12 Chris Fleizach <cfleizach@apple.com>
</span><span class="cx">
</span><span class="cx"> AX: accessibility data table heuristics fail on this jQuery table
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsmediamediaplaystreamchunkedicyexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy-expected.txt (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy-expected.txt         (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy-expected.txt        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+
+Test ended by:
+
+Play streamed media.
+Verify that the 'ended' event fires upon finishing the playback.
+
+EVENT(ended)
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsmediamediaplaystreamchunkedicyhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy.html (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy.html         (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-play-stream-chunked-icy.html        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+<!DOCTYPE html>
+<html>
+ <head>
+ <script src=/media-resources/video-test.js></script>
+ <script src=/media-resources/media-file.js></script>
+
+ <script>
+ function start()
+ {
+ var media = findMediaFile('audio', '../../../../media/content/silence');
+ var type = mimeTypeForExtension(media.split('.').pop());
+ var audio = document.querySelector("audio");
+
+ audio.src = 'http://127.0.0.1:8000/media/resources/serve-video.php?name=' + media + '&type=' + type + '&content-length=no&icy-data=yes';
+
+ waitForEventAndFail('error');
+ waitForEventAndEnd('ended');
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <audio controls autoplay></audio>
+ <p><b>Test ended by:</b>
+ <ol>
+ <li>Play streamed media.</li>
+ <li>Verify that the 'ended' event fires upon finishing the playback.</li>
+ </ol>
+ </p>
+ <br/>
+ </body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsmediaresourcescreateid3dbphp"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/media/resources/create-id3-db.php (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/resources/create-id3-db.php         (rev 0)
+++ trunk/LayoutTests/http/tests/media/resources/create-id3-db.php        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1,177 @@
</span><ins>+<?php
+
+ // This script is based on the work done by gadgetguru
+ // <david@vuistbijl.nl> at
+ // https://github.com/gadgetguru/PHP-Streaming-Audio and released
+ // under the Public Domain.
+
+
+ // This script creates, overwriting if needed, the "metadata.db"
+ // file used by scripts such as "serve-video.php".
+ //
+ // For creating or updating an existing file you have to run the
+ // http server used by the tests with:
+ //
+ // $ Tools/Scripts/run-webkit-httpd
+ //
+ // Then you will just have to use a browser to open an URL of the
+ // type:
+ //
+ // http://127.0.0.1:8000/media/resources/create-id3-db.php?name=../../../../media/content/silence.wav
+ //
+ // With this, a new "metadaba.db" file will be create in the
+ // directory containing the referred file. In this example, the
+ // database will be created at:
+ //
+ // $ LayoutTests/media/content/metadata.db
+
+
+ require_once '../../resources/portabilityLayer.php';
+ require_once '../../resources/dir-helpers.php';
+
+ // 500 on errors
+ $httpStatus = "500 Internal Server Error";
+ $htmlMessage = "";
+
+ if (!array_key_exists("name", $_GET)) {
+ trigger_error("You have not specified a 'name' parameter.", E_USER_WARNING);
+ goto answering;
+ }
+ $mediaDirectory = dirname($_GET["name"]);
+
+ if (!is_dir($mediaDirectory)) {
+ trigger_error("The provided directory path doesn't exist.", E_USER_WARNING);
+ goto answering;
+ }
+ $databaseFile = realpath($mediaDirectory) . "/metadata.db";
+
+ if (file_exists($databaseFile)) {
+ unlink($databaseFile);
+ $htmlMessage = $htmlMessage . "<p/>Deleted previously existing db file at: '" . $databaseFile . "'";
+ }
+
+ // We don't have getid3 in WebKit. If not currently present, we
+ // will try to download it now before making use of it.
+ //
+ // getid3 is available at http://getid3.sourceforge.net
+
+ $getId3DestDir = "getid3";
+ if (!file_exists($getId3DestDir)) {
+
+ $tmpDir = tmpdir(FALSE, "php");
+ if (!is_dir($tmpDir)) {
+ trigger_error("Impossible to create temporal directory.", E_USER_WARNING);
+ goto answering;
+ }
+
+ $id3DestPath = $tmpDir . "/getid3.zip";
+ $phpVersion = explode(".", PHP_VERSION);
+ if (($phpVersion[0] > 4) && ($phpVersion[1] > 0))
+ $id3Url = "http://sourceforge.net/projects/getid3/files/getID3%28%29%201.x/1.9.7/getID3-1.9.7.zip/download";
+ else
+ $id3Url = "http://sourceforge.net/projects/getid3/files/getID3%28%29%201.x/1.7.10/getid3-1.7.10-20090426.zip/download";
+ $htmlMessage = $htmlMessage . "<p/>getId3 not in the system.<p/>Downloading from: '" . $id3Url . "'";
+
+ $ctxtOpts = array("http" =>
+ array(
+ "method" => "GET",
+ "timeout" => 60,
+ )
+ );
+ $ctxt = stream_context_create($ctxtOpts);
+ $src = fopen($id3Url, "rb", false, $ctxt);
+ $dest = fopen($id3DestPath, "wb");
+ $copied = stream_copy_to_stream($src, $dest);
+ fclose($src);
+ fclose($dest);
+ if ($copied == 0) {
+ trigger_error("Impossible to copy the latest getid3 zip release file.", E_USER_WARNING);
+ goto answering;
+ }
+ $htmlMessage = $htmlMessage . "<p/>getId3 downloaded to: '" . $id3DestPath . "'";
+
+ if (!exec("unzip " . $id3DestPath . " -d " . $tmpDir)) {
+ trigger_error("Impossible to extract the downloaded zip file.", E_USER_WARNING);
+ goto answering;
+ }
+ $htmlMessage = $htmlMessage . "<p/>Zip file successfully extracted.";
+
+ $getId3SourceDir = first_dir($getId3DestDir, $tmpDir);
+ if (!$getId3SourceDir) {
+ trigger_error("Impossible to find the getid3 directory with the PHP code to copy.", E_USER_WARNING);
+ goto answering;
+ }
+ $htmlMessage = $htmlMessage . "<p/>getId3 directory to copy found at: '" . $getId3SourceDir . "'";
+
+
+ rcopy($getId3SourceDir, $getId3DestDir);
+ $htmlMessage = $htmlMessage . "<p/>getId3 directory copied to its final destination at: '" . realpath($getId3SourceDir) . "'";
+
+ rrmdir($tmpDir);
+ $htmlMessage = $htmlMessage . "<p/>Deleted all temporal files at: '" . $tmpDir . "'";
+ }
+
+
+ // getid3 downloaded. Let's create the database file.
+ require_once("getid3/getid3.php");
+ $getID3 = new getID3;
+
+ $id3FileNames = array_diff(scandir($mediaDirectory), array(".", ".."));
+
+ foreach ($id3FileNames as $id3FileName) {
+ $id3 = $getID3->analyze($mediaDirectory . "/" . $id3FileName);
+ $playfile = array(
+ "fileName" => $id3["filename"],
+ "fileSize" => $id3["filesize"],
+ "playTime" => $id3["playtime_seconds"],
+ "audioStart" => $id3["avdataoffset"],
+ "audioEnd" => $id3["avdataend"],
+ "audioLength" => $id3["avdataend"] - $id3["avdataoffset"],
+ "artist" => $id3["tags"]["id3v2"]["artist"][0],
+ "title" => $id3["tags"]["id3v2"]["title"][0],
+ "bitRate" => $id3["audio"]["bitrate"],
+ "mimeType" => $id3["mime_type"],
+ "fileFormat" => $id3["fileformat"],
+ );
+ if (empty($playfile["artist"]) || empty($playfile["title"]))
+ list($playfile["artist"], $playfile["title"]) = explode(" - ", substr($playfile["fileName"], 0 , -4));
+ $playFiles[] = $playfile;
+ }
+
+ if (!file_put_contents($databaseFile, serialize($playFiles))) {
+ trigger_error("Impossible to write the getid3 db.", E_USER_WARNING);
+ goto answering;
+ }
+ $htmlMessage = $htmlMessage . "<p/>Written new database file at: '" . $databaseFile . "'";
+
+
+ // Database created, everything OK.
+ $httpStatus = "200 OK";
+
+
+answering:
+
+ header("Status: " . $httpStatus);
+ header("HTTP/1.1 " . $httpStatus);
+ header("Connection: close");
+ header("Content-Type: text/html");
+
+ $htmlMessage = "<html><body><h1>" . $httpStatus . "</h1>" . $htmlMessage;
+
+ if ($httpStatus == "500 Internal Server Error") {
+ if (function_exists("error_get_last")) {
+ $errorArray = error_get_last();
+ if ($errorArray) {
+ $htmlMessage = sprintf("%s<p/>Error type: %d<p/>Error message: %s<p/>".
+ "Error file: %s<p/>Error line: %d",
+ $htmlMessage, $errorArray["type"], $errorArray["message"],
+ $errorArray["file"], $errorArray["line"]);
+ }
+ }
+ }
+ $htmlMessage = $htmlMessage . "</body><html>";
+
+ print($htmlMessage);
+ flush();
+ exit;
+?>
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsmediaresourcesservevideophp"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/media/resources/serve-video.php (165535 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/resources/serve-video.php        2014-03-13 16:28:24 UTC (rev 165535)
+++ trunk/LayoutTests/http/tests/media/resources/serve-video.php        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -1,49 +1,145 @@
</span><span class="cx"> <?php
</span><span class="cx">
</span><ins>+ // This script is based on the work done by gadgetguru
+ // <david@vuistbijl.nl> at
+ // https://github.com/gadgetguru/PHP-Streaming-Audio and released
+ // under the Public Domain.
+
+ // Set variables
+ $settings = array(
+ "chunkSize" => 1024 * 256,
+ "databaseFile" => "metadata.db",
+ "httpStatus" => "500 Internal Server Error",
+ "mediaDirectory" => array_key_exists("name", $_GET) ? dirname($_GET["name"]) : "",
+ "mimeType" => array_key_exists("type", $_GET) ? $_GET["type"] : "",
+ "radioGenre" => "Rock",
+ "radioName" => "WebKit Test Radio",
+ "radioUrl" => (array_key_exists("HTTPS", $_SERVER) ? "https" : "http") . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"],
+ "setContentLength" => array_key_exists("content-length", $_GET) ? $_GET["content-length"] : "yes",
+ "setIcyData" => array_key_exists("icy-data", $_GET) ? $_GET["icy-data"] : "no",
+ "supportRanges" => array_key_exists("ranges", $_GET) ? $_GET["ranges"] : "yes",
+ );
+
+ // 500 on errors
+ if (!array_key_exists("name", $_GET)) {
+ trigger_error("You have not specified a 'name' parameter.", E_USER_WARNING);
+ goto answering;
+ }
</ins><span class="cx"> $fileName = $_GET["name"];
</span><del>- $type = $_GET["type"];
- if (array_key_exists("ranges", $_GET))
- $ranges = $_GET["ranges"];
</del><span class="cx">
</span><ins>+ if (!file_exists($fileName)) {
+ trigger_error("The file to stream specified at 'name' doesn't exist.", E_USER_WARNING);
+ goto answering;
+ }
+ $settings["databaseFile"] = $settings["mediaDirectory"] . "/" . $settings["databaseFile"];
+
+ if ($settings["setIcyData"] != "yes" && $settings["mimeType"] == "") {
+ trigger_error("You have not specified a 'type' parameter.", E_USER_WARNING);
+ goto answering;
+ }
+
+ if ($settings["setIcyData"] == "yes") {
+ if (!file_exists($settings["databaseFile"])) {
+
+ // If the metadata database file doesn't exist it has to
+ // be create previously.
+ //
+ // Check the instructions about how to create it from the
+ // create-id3-db.php script file in this same directory.
+
+ trigger_error("The metadata database doesn't exist. To create one, check the script 'create-id3-db.php'.", E_USER_WARNING);
+ goto answering;
+ }
+
+ $playFiles = unserialize(file_get_contents($settings["databaseFile"]));
+ foreach ($playFiles as $i=>$playFile) {
+ if (basename($fileName) == $playFile["fileName"]) {
+ $fileInDB = true;
+ break;
+ }
+ }
+
+ if (!isset($fileInDB)) {
+ trigger_error("The requested file is not in the database.", E_USER_WARNING);
+ goto answering;
+ }
+ }
+
+ // There is everything needed to send the media file
</ins><span class="cx"> $fileSize = filesize($fileName);
</span><span class="cx"> $start = 0;
</span><span class="cx"> $end = $fileSize - 1;
</span><del>- if ($ranges != "no")
</del><ins>+ if ($settings["supportRanges"] != "no" && array_key_exists("HTTP_RANGE", $_SERVER))
</ins><span class="cx"> $contentRange = $_SERVER["HTTP_RANGE"];
</span><span class="cx"> if (isset($contentRange)) {
</span><span class="cx"> $range = explode("-", substr($contentRange, strlen("bytes=")));
</span><span class="cx"> $start = intval($range[0]);
</span><span class="cx"> if (!empty($range[1]))
</span><span class="cx"> $end = intval($range[1]);
</span><del>- $httpStatus = "206 Partial Content";
</del><ins>+ $settings["httpStatus"] = "206 Partial Content";
</ins><span class="cx"> } else
</span><del>- $httpStatus = "200 OK";
</del><ins>+ $settings["httpStatus"] = "200 OK";
</ins><span class="cx">
</span><del>- header("Status: " . $httpStatus);
- header("HTTP/1.1 " . $httpStatus);
</del><ins>+
+answering:
+
+ header("Status: " . $settings["httpStatus"]);
+ header("HTTP/1.1 " . $settings["httpStatus"]);
+ header("Connection: close");
+
+ if ($settings["httpStatus"] == "500 Internal Server Error") {
+ header("Content-Type: text/html");
+ $errorMessage = sprintf("<html><body><h1>%s</h1><p/>",
+ $settings["httpStatus"]);
+ if (function_exists("error_get_last")) {
+ $errorArray = error_get_last();
+ if ($errorArray) {
+ $errorMessage = sprintf("%sError type: %d<p/>Error message: %s<p/>".
+ "Error file: %s<p/>Error line: %d<p/>",
+ $errorMessage, $errorArray["type"], $errorArray["message"],
+ $errorArray["file"], $errorArray["line"]);
+ }
+ }
+ $errorMessage = $errorMessage . "</body><html>";
+ print($errorMessage);
+ flush();
+ exit;
+ }
+
</ins><span class="cx"> header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
</span><span class="cx"> header("Pragma: no-cache");
</span><span class="cx"> header("Etag: " . '"' . $fileSize . "-" . filemtime($fileName) . '"');
</span><del>- header("Content-Type: " . $type);
- if ($ranges != "no")
</del><ins>+ if ($settings["setIcyData"] == "yes") {
+ $bitRate = ceil($playFiles[$i]["bitRate"] / 1000);
+ if ($settings["mimeType"] == "")
+ $settings["mimeType"] = $playFiles[$i]["mimeType"];
+ header("icy-notice1: <BR>This stream requires a shoutcast/icecast compatible player.<BR>");
+ header("icy-notice2: WebKit Stream Test<BR>");
+ header("icy-name: " . $settings["radioName"]);
+ header("icy-genre: " . $settings["radioGenre"]);
+ header("icy-url: " . $settings["radioUrl"]);
+ header("icy-pub: 1");
+ header("icy-br: " . $bitRate);
+ }
+ header("Content-Type: " . $settings["mimeType"]);
+ if ($settings["supportRanges"] != "no")
</ins><span class="cx"> header("Accept-Ranges: bytes");
</span><del>- header("Content-Length: " . ($end - $start + 1));
</del><ins>+ if ($settings["setContentLength"] != "no")
+ header("Content-Length: " . ($end - $start + 1));
</ins><span class="cx"> if (isset($contentRange))
</span><span class="cx"> header("Content-Range: bytes " . $start . "-" . $end . "/" . $fileSize);
</span><del>- header("Connection: close");
</del><span class="cx">
</span><del>- $chunkSize = 1024 * 256;
</del><span class="cx"> $offset = $start;
</span><span class="cx">
</span><span class="cx"> $fn = fopen($fileName, "rb");
</span><span class="cx"> fseek($fn, $offset, 0);
</span><span class="cx">
</span><span class="cx"> while (!feof($fn) && $offset <= $end && connection_status() == 0) {
</span><del>- $readSize = min($chunkSize, ($end - $offset) + 1);
</del><ins>+ $readSize = min($settings["chunkSize"], ($end - $offset) + 1);
</ins><span class="cx"> $buffer = fread($fn, $readSize);
</span><span class="cx"> print($buffer);
</span><span class="cx"> flush();
</span><del>- $offset += $chunkSize;
</del><ins>+ $offset += $settings["chunkSize"];
</ins><span class="cx"> }
</span><span class="cx"> fclose($fn);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsresourcesdirhelpersphp"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/resources/dir-helpers.php (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resources/dir-helpers.php         (rev 0)
+++ trunk/LayoutTests/http/tests/resources/dir-helpers.php        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1,77 @@
</span><ins>+<?php
+require_once 'portabilityLayer.php';
+
+function tmpdir($dir=FALSE, $prefix='php')
+{
+ if ($dir) {
+ $tmpFile = tempnam($dir, $prefix);
+ } else {
+ if (!sys_get_temp_dir())
+ return FALSE;
+ $tmpFile = tempnam(sys_get_temp_dir(), $prefix);
+ }
+
+ if (!file_exists($tmpFile))
+ return FALSE;
+ unlink($tmpFile);
+ mkdir($tmpFile);
+ if (!is_dir($tmpFile))
+ return FALSE;
+
+ return $tmpFile;
+}
+
+function rrmdir($dir)
+{
+ if (is_dir($dir)) {
+ $objects = array_diff(scandir($dir), array(".", ".."));
+ foreach ($objects as $object)
+ (is_dir($dir . "/" . $object)) ? rrmdir($dir . "/" . $object) : unlink($dir . "/" . $object);
+ reset($objects);
+ rmdir($dir);
+ }
+}
+
+function rcopy($src, $dst)
+{
+ if (file_exists($dst))
+ rrmdir($dst);
+ if (is_dir($src)) {
+ mkdir($dst);
+ $files = array_diff(scandir($src), array(".", ".."));
+ foreach ($files as $file)
+ rcopy("$src/$file", "$dst/$file");
+ } else if (file_exists($src)) {
+ copy($src, $dst);
+ }
+}
+
+function first_dir($name, $dir=FALSE)
+{
+ $result = FALSE;
+ $root = $dir ? $dir : "./";
+ $queue = array(realpath($root));
+
+ while (sizeof($queue)) {
+ $vertex = array_pop($queue);
+ $objects = array_diff(scandir($vertex), array(".", ".."));
+ foreach ($objects as $object) {
+ $fullPath = $vertex . "/" . $object;
+ if (is_dir($fullPath)) {
+ if ($name == basename($fullPath)) {
+ $result = $fullPath;
+ goto cleanup;
+ } else {
+ array_unshift($queue, $fullPath);
+ }
+ }
+ }
+ }
+
+
+cleanup:
+
+ return $result;
+}
+?>
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsresourcesportabilityLayerphp"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/resources/portabilityLayer.php (165535 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/resources/portabilityLayer.php        2014-03-13 16:28:24 UTC (rev 165535)
+++ trunk/LayoutTests/http/tests/resources/portabilityLayer.php        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -32,4 +32,37 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+if (!function_exists('stream_copy_to_stream')) {
+ function stream_copy_to_stream($source, $dest)
+ {
+ $result = 0;
+ while (!feof($source)) {
+ $bytesWritten = fwrite($dest, fread($source, 8192));
+ if (!$bytesWritten)
+ return FALSE;
+ $result = $result + $bytesWritten;
+ }
+
+ return $result;
+ }
+}
+
+if (!function_exists('scandir')) {
+ function scandir($dir)
+ {
+ if (!is_dir($dir))
+ return FALSE;
+
+ $dh = opendir($dir);
+ while ($filename = readdir($dh)) {
+ $files[] = $filename;
+ }
+
+ closedir($dh);
+ sort($files);
+
+ return $files;
+ }
+}
+
</ins><span class="cx"> ?>
</span></span></pre></div>
<a id="trunkLayoutTestsmediacontentmetadatadb"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/content/metadata.db (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/content/metadata.db         (rev 0)
+++ trunk/LayoutTests/media/content/metadata.db        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1 @@
</span><ins>+a:37:{i:0;a:11:{s:8:"fileName";s:23:"720x576i-anamorphic.mov";s:8:"fileSize";i:226625;s:8:"playTime";i:5;s:10:"audioStart";i:3463;s:8:"audioEnd";i:226625;s:11:"audioLength";i:223162;s:6:"artist";s:19:"720x576i-anamorphic";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:9:"quicktime";}i:1;a:11:{s:8:"fileName";s:16:"CC+Subtitles.mov";s:8:"fileSize";i:545959;s:8:"playTime";i:30;s:10:"audioStart";i:32206;s:8:"audioEnd";i:545959;s:11:"audioLength";i:513753;s:6:"artist";s:12:"CC+Subtitles";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:2;a:11:{s:8:"fileName";s:7:"abe.png";s:8:"fileSize";i:12242;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:12242;s:11:"audioLength";i:12242;s:6:"artist";s:3:"abe";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:9:"image/png";s:10:"fileFormat";s:3:"png";}i:3;a:11:{s:8:"fileName";s:22:"counting-captioned.mov";s:8:"fileSize";i:314055;s:8:"playTime";d:9.8283333333333331;s:10:"audioStart";i:5885;s:8:"audioEnd";i:314055;s:11:"audioLength";i:308170;s:6:"artist";s:18:"counting-captioned";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:4;a:11:{s:8:"fileName";s:27:"counting-subtitled-kate.ogv";s:8:"fileSize";i:154649;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:154581;s:11:"audioLength";i:154581;s:6:"artist";s:23:"counting-subtitled-kate";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"application/ogg";s:10:"fileFormat";s:3:"ogg";}i:5;a:11:{s:8:"fileName";s:26:"counting-subtitled-srt.mkv";s:8:"fileSize";i:318669;s:8:"playTime";d:10.800000000000001;s:10:"audioStart";i:0;s:8:"audioEnd";i:318669;s:11:"audioLength";i:318669;s:6:"artist";s:22:"counting-subtitled-srt";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:16:"video/x-matroska";s:10:"fileFormat";s:8:"matroska";}i:6;a:11:{s:8:"fileName";s:22:"counting-subtitled.m4v";s:8:"fileSize";i:317064;s:8:"playTime";d:10.800000000000001;s:10:"audioStart";i:311344;s:8:"audioEnd";i:317064;s:11:"audioLength";i:5720;s:6:"artist";s:18:"counting-subtitled";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:7;a:11:{s:8:"fileName";s:12:"counting.jpg";s:8:"fileSize";i:6629;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:8:"counting";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:10:"image/jpeg";s:10:"fileFormat";N;}i:8;a:11:{s:8:"fileName";s:12:"counting.mp4";s:8:"fileSize";i:311336;s:8:"playTime";d:9.8283333333333331;s:10:"audioStart";i:3590;s:8:"audioEnd";i:311336;s:11:"audioLength";i:307746;s:6:"artist";s:8:"counting";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:9;a:11:{s:8:"fileName";s:12:"counting.ogv";s:8:"fileSize";i:187773;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:187773;s:11:"audioLength";i:187773;s:6:"artist";s:8:"counting";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";s:3:"ogg";}i:10;a:11:{s:8:"fileName";s:9:"empty.oga";s:8:"fileSize";i:0;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:5:"empty";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";N;}i:11;a:11:{s:8:"fileName";s:9:"empty.wav";s:8:"fileSize";i:0;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:5:"empty";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";N;}i:12;a:11:{s:8:"fileName";s:11:"garbage.mp4";s:8:"fileSize";i:22;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:7:"garbage";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";N;}i:13;a:11:{s:8:"fileName";s:11:"garbage.ogv";s:8:"fileSize";i:22;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:7:"garbage";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";N;}i:14;a:11:{s:8:"fileName";s:12:"greenbox.png";s:8:"fileSize";i:95;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:95;s:11:"audioLength";i:95;s:6:"artist";s:8:"greenbox";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:9:"image/png";s:10:"fileFormat";s:3:"png";}i:15;a:11:{s:8:"fileName";s:17:"scaled-matrix.mov";s:8:"fileSize";i:203856;s:8:"playTime";d:9.8283333333333331;s:10:"audioStart";i:1671;s:8:"audioEnd";i:203856;s:11:"audioLength";i:202185;s:6:"artist";s:13:"scaled-matrix";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:9:"quicktime";}i:16;a:11:{s:8:"fileName";s:9:"short.wav";s:8:"fileSize";i:4920;s:8:"playTime";d:0.027641723356009069;s:10:"audioStart";i:44;s:8:"audioEnd";i:4920;s:11:"audioLength";i:4876;s:6:"artist";s:5:"short";s:5:"title";N;s:7:"bitRate";i:1411200;s:8:"mimeType";s:12:"audio/x-wave";s:10:"fileFormat";s:4:"riff";}i:17;a:11:{s:8:"fileName";s:16:"silence-loop.mov";s:8:"fileSize";i:36443;s:8:"playTime";d:1.3844777777777777;s:10:"audioStart";i:1622;s:8:"audioEnd";i:36443;s:11:"audioLength";i:34821;s:6:"artist";s:12:"silence-loop";s:5:"title";N;s:7:"bitRate";d:201207.99659719269;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:9:"quicktime";}i:18;a:11:{s:8:"fileName";s:11:"silence.m4a";s:8:"fileSize";i:20447;s:8:"playTime";d:1.3699773242630386;s:10:"audioStart";i:4096;s:8:"audioEnd";i:20447;s:11:"audioLength";i:16351;s:6:"artist";s:7:"silence";s:5:"title";N;s:7:"bitRate";d:95481.87235169491;s:8:"mimeType";s:9:"audio/mp4";s:10:"fileFormat";s:3:"mp4";}i:19;a:11:{s:8:"fileName";s:11:"silence.mp3";s:8:"fileSize";i:17961;s:8:"playTime";d:1.4105833333333333;s:10:"audioStart";i:1034;s:8:"audioEnd";i:17961;s:11:"audioLength";i:16927;s:6:"artist";s:7:"silence";s:5:"title";N;s:7:"bitRate";i:96000;s:8:"mimeType";s:10:"audio/mpeg";s:10:"fileFormat";s:3:"mp3";}i:20;a:11:{s:8:"fileName";s:11:"silence.mpg";s:8:"fileSize";i:33227;s:8:"playTime";d:1.3844583333333333;s:10:"audioStart";i:0;s:8:"audioEnd";i:33227;s:11:"audioLength";i:33227;s:6:"artist";s:7:"silence";s:5:"title";N;s:7:"bitRate";i:192000;s:8:"mimeType";s:10:"audio/mpeg";s:10:"fileFormat";s:3:"mp2";}i:21;a:11:{s:8:"fileName";s:11:"silence.oga";s:8:"fileSize";i:12983;s:8:"playTime";d:1.3844897959183673;s:10:"audioStart";i:172;s:8:"audioEnd";i:10460;s:11:"audioLength";i:10288;s:6:"artist";s:7:"silence";s:5:"title";N;s:7:"bitRate";d:59447.16981132076;s:8:"mimeType";s:15:"application/ogg";s:10:"fileFormat";s:3:"ogg";}i:22;a:11:{s:8:"fileName";s:11:"silence.wav";s:8:"fileSize";i:120876;s:8:"playTime";d:1.3699773242630386;s:10:"audioStart";i:44;s:8:"audioEnd";i:120876;s:11:"audioLength";i:120832;s:6:"artist";s:7:"silence";s:5:"title";N;s:7:"bitRate";i:705600;s:8:"mimeType";s:12:"audio/x-wave";s:10:"fileFormat";s:4:"riff";}i:23;a:11:{s:8:"fileName";s:14:"test-25fps.mp4";s:8:"fileSize";i:167758;s:8:"playTime";i:10;s:10:"audioStart";i:7621;s:8:"audioEnd";i:167758;s:11:"audioLength";i:160137;s:6:"artist";s:10:"test-25fps";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:24;a:11:{s:8:"fileName";s:14:"test-25fps.ogv";s:8:"fileSize";i:191863;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:191863;s:11:"audioLength";i:191863;s:6:"artist";s:10:"test-25fps";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";s:3:"ogg";}i:25;a:11:{s:8:"fileName";s:17:"test-par-16-9.mp4";s:8:"fileSize";i:193661;s:8:"playTime";d:6.0430000000000001;s:10:"audioStart";i:48;s:8:"audioEnd";i:193661;s:11:"audioLength";i:193613;s:6:"artist";s:13:"test-par-16-9";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:26;a:11:{s:8:"fileName";s:17:"test-par-16-9.ogv";s:8:"fileSize";i:99394;s:8:"playTime";N;s:10:"audioStart";i:11528;s:8:"audioEnd";i:98473;s:11:"audioLength";i:86945;s:6:"artist";s:13:"test-par-16-9";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"application/ogg";s:10:"fileFormat";s:3:"ogg";}i:27;a:11:{s:8:"fileName";s:8:"test.mp4";s:8:"fileSize";i:192844;s:8:"playTime";d:6.0271999999999997;s:10:"audioStart";i:4345;s:8:"audioEnd";i:192844;s:11:"audioLength";i:188499;s:6:"artist";s:4:"test";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:28;a:11:{s:8:"fileName";s:8:"test.oga";s:8:"fileSize";i:20787;s:8:"playTime";d:7.5683749999999996;s:10:"audioStart";i:170;s:8:"audioEnd";i:19650;s:11:"audioLength";i:19480;s:6:"artist";s:4:"test";s:5:"title";N;s:7:"bitRate";d:20590.945876756901;s:8:"mimeType";s:15:"application/ogg";s:10:"fileFormat";s:3:"ogg";}i:29;a:11:{s:8:"fileName";s:8:"test.ogv";s:8:"fileSize";i:157839;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:157839;s:11:"audioLength";i:157839;s:6:"artist";s:4:"test";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";s:3:"ogg";}i:30;a:11:{s:8:"fileName";s:8:"test.wav";s:8:"fileSize";i:121138;s:8:"playTime";d:7.5683749999999996;s:10:"audioStart";i:44;s:8:"audioEnd";i:121138;s:11:"audioLength";i:121094;s:6:"artist";s:4:"test";s:5:"title";N;s:7:"bitRate";i:128000;s:8:"mimeType";s:12:"audio/x-wave";s:10:"fileFormat";s:4:"riff";}i:31;a:11:{s:8:"fileName";s:15:"test_yuv420.mp4";s:8:"fileSize";i:65537;s:8:"playTime";d:6.048;s:10:"audioStart";i:48;s:8:"audioEnd";i:65537;s:11:"audioLength";i:65489;s:6:"artist";s:11:"test_yuv420";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:32;a:11:{s:8:"fileName";s:15:"test_yuv420.ogv";s:8:"fileSize";i:98780;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:98780;s:11:"audioLength";i:98780;s:6:"artist";s:11:"test_yuv420";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";s:3:"ogg";}i:33;a:11:{s:8:"fileName";s:15:"test_yuv422.mp4";s:8:"fileSize";i:83353;s:8:"playTime";d:6.0510000000000002;s:10:"audioStart";i:48;s:8:"audioEnd";i:83353;s:11:"audioLength";i:83305;s:6:"artist";s:11:"test_yuv422";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";s:15:"video/quicktime";s:10:"fileFormat";s:3:"mp4";}i:34;a:11:{s:8:"fileName";s:15:"test_yuv422.ogv";s:8:"fileSize";i:164158;s:8:"playTime";N;s:10:"audioStart";i:0;s:8:"audioEnd";i:164158;s:11:"audioLength";i:164158;s:6:"artist";s:11:"test_yuv422";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";s:3:"ogg";}i:35;a:11:{s:8:"fileName";s:30:"two-audio-and-video-tracks.mkv";s:8:"fileSize";i:968566;s:8:"playTime";d:8.918000000000001;s:10:"audioStart";i:0;s:8:"audioEnd";i:968566;s:11:"audioLength";i:968566;s:6:"artist";s:26:"two-audio-and-video-tracks";s:5:"title";N;s:7:"bitRate";i:80000;s:8:"mimeType";s:16:"video/x-matroska";s:10:"fileFormat";s:8:"matroska";}i:36;a:11:{s:8:"fileName";s:21:"unsupported_track.mov";s:8:"fileSize";i:180;s:8:"playTime";N;s:10:"audioStart";N;s:8:"audioEnd";N;s:11:"audioLength";i:0;s:6:"artist";s:17:"unsupported_track";s:5:"title";N;s:7:"bitRate";N;s:8:"mimeType";N;s:10:"fileFormat";N;}}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTestsmediacontentsilencemp3"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/content/silence.mp3 (0 => 165536)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/content/silence.mp3         (rev 0)
+++ trunk/LayoutTests/media/content/silence.mp3        2014-03-13 16:30:06 UTC (rev 165536)
</span><span class="lines">@@ -0,0 +1,128 @@
</span><ins>+ID3 \xFF\xFBp\xC4 ` \xD0 #v\xB2\xA0\xB77\x908y̠@(
+\x81 (\xDC䐠HC\xF0\xD8P\xCC '_<\xEE\xE4\xD1\xC7\xDBЌ\x96_\xFC~G\x88e\xE7\x91ŷ\xEF\xE5\xEC\xF7q\xCDf\x8AE\xBF\xF9Ϙ\xA5~\xDA\xE48H!P\xE4\xC2
+G\xFF\xD8~5b\xDDCMd\x88\xA0
+\x80S\xFF\xFF\xFF\xE5\x98ߖc\xB6\xDE0\xC6ɡU`\x9F\xFF\xFF\xFF\xFFo\\xD73\xC33\x82j\xC1 ?\xB2\xA33]|\xFF\xFF\xFF\xFF\xFF\xE7\xFE\xFB\xCB=>ޟj^\xC0)U\x83\xAF\xC6?\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFB,_\xE6r\xFC\xF3\xB1\xFF\xFF
+\xB4\xC4\xD0T b\x9D\xBB"R;\xED\xAEj\xFF\xFE\xFF\xF1 \xBA7)d6J/[\xF5VAB)q/\xAC\xB6\xCEA\x86\xE3\x82\xC9`\x82u\xC5\x85\xC8B`!\xCA\xE6(\xD1q\xBD3A\x80\x9D\x96\xE1j\xAAG\xAF\xD4-[\xB6\xAF\xDE\x87\xFF\xFBr\xC4!\x80I{)\xBC\xF7\x80        \xFC-e\xBD\x84\x8D\xF9\xAFK\x9F\\xCD\xF5\xB8U\xD5\xF3I3OK\xDA\xF0aZ\xEB\xCF#\xB8\xE2\x97?!f<\xDE7lv\xDFv\xF7ĵ/J\xD65\x87o\x86\xB3I        lF\x92ek#\x9B\xC6;\xA8NuB\xB9D\xAF]\xBC\xA2\xDBl\xFE,\xED\xEF\x91m\xAA7\xFF.\xE2:"\x8FQ\xAD        \xF5\x95\xADo$\x95\x99sg\xD7u)\x9D\xE5،\xD5\xE6\x9BE?\xD4b\xE9B\xA5/\x9B\T$0\xCC\xD9l\x95vC\x90t\xD9#"\x80\xCA\xC1\xB1\xC2eN4\xC6@5Q#<&bH        \xD1%t\x93XҊ\xC36iF\xD6_\xA1vG\xA4\x92\xE7_~m\xD9 \x80\xC7\\x83\x8B\xF8\xD9 'k\x9C\x8F:GP1BЩϚC'5\x89\xED\xC9,45l\x93\x85\x96\x85\xE6\xC3O7\xC5v\x90\xEA5\x954!K\x95ϑ\xD8t\xD2\\\xD5jD\xF7}\x88\xAAe@\xA2\xAEZ
++AÊ\xC7\xE3M<\xDD\xE6)\x97#D\x87\xE1\x8A)E\x80\xE6.Ҕ\xFDi$H\x93\\\x95\xFF\xFBp\xC4 Ay%̤Ԃh\xAF#\xA0\xF4\x9A\xB8d:\xAA\x96\x92~\x98\xED\xA6a\x820\xA8\xEF\xEBI\xDA\xD0\xCA
+-\xBDh\xA99ny\xFF\x98\xF0\xCEo\xD8A\x9B\xCC͠\x83k\xBF\x9DnչL\xEDtZ772>\xBD\xAD\xA3\xBFڪ\xC8ǯ\xF1\x85\xB2V\x85g\x8Dwc0\xE6 \x90 \xFB^.\x8EE\xE4\xD8iE\x9AG{8\x8B\xF08\xC8i\x92\xA0\xE5&\xE7\xC9        8\x8B\x9AYp\xC9G\xB3\xF6\xFA:\x9B\x82{4\xB19
+h\xDC=h\x9CFmD\xCF\xF6\xC1fk\x8Ab\xCC\xFEI\x8AZ\xB6\xE1d\xEC\xEE\x95L\x97H\xC5)Ax\xC6J\x88"t^b\x94A\x98\xB1ED\xB42\xAB\xBEV\x86L6M\x90\xF3xK\xE8Ù9\x95\xDELv\x91\xBA\xB2\xBA\x8C\xDD(\xB3_Fu\xF3!\xDD6%I\xA2s\xD5'F\x85A+\x99V\xCF\xD6yw\x90iD;p<\xCC\xF6^\xB11\xCAQ\xB5\x83lPH\xE2 }        
+\xC7a\xE1\xC6\xD64y\x92\xB9\x9AX)VEU\xCF+\xFF\xFBr\xC4\x80M}#Ն \xBC\xB1\xE53\xF0 [v\xEB        \xED"\x{1D3F86}ɝ\x95\xB4\xBF\xA3F\xE6es\x85\x8A5Ν-cj\xCE\xFD\xF6)e\x91U$\xF5VfXr&J;\xB9O\x9A\xD5}\xB1\xFA\xBAە\xF2\xAA\xEF}\x9B\xFB_\xB4\xF8\x9E\x9D̵\xAF\xB3Y\xBBr\xC52\xAC\xCF0\xE5\xD0ꚑ8\xEB\xF5w\xE9U\xEB\xCF\xDEY\x92JP\x81\x92\xD5(@#)\xC9s\xDCT0\xA8O        \xE9\x90\xE8\xB7\x86t\xE1p,@\xE6,\xAEi\x85\x92Ƚ\xA6GLw\x86\xF2-P\x9F\xC3;\x92\x91X\xB0\xC8v\xB4E\xD3ZiB\xF2XǪq\xD5
+\xEB\xC1m2`\xA9p\xCFLlo[\x9B\x99\xB6\xA6\x9A\xF0aI\x8A3;\xF8\xFE\x8Ef\xD4\xF4\xB1b֑\xF5\xBFz\xB6ĭ-4ہ\xB4Y\xDD\xFC\xDF/`Ǐh\xFB\xA4\xB3^\xED\xFA\xBD\xED\xBFJƑ\xE4\xF5\xFB\xF6\xD6q\x9CE\x96\xBB\xA6-\xFF\xFE7\x9A\x91\xEFz\xFF\xFF\xBD\xFD\xFBj,Uvx\x88f4Cvtf\xB76\xDAI$*\xFF\xFBp\xC4 9q;\xF8\xF5\x80 \xAA\xA4\x9E\xF0\xA1ń\xD3\xF2n4L 8\xE9z3\xE3
+"\xBDH\x9BDw.?\x87h\x96"iq\xB9\xA4=\x88\xEF\x82q(\xA1\xA8\x9B\xA4H\xB2\xA4a\xB9\x99a\xD3)\x84
+\xCDWs\xA1
+\x9B\xED\x93'4Y3\xC7#{\xCC֙\xDA\xDBr\xAE\x86!Y\xFE\xF75δN\xFC\xBB\xDF\xDF=\xAC\xC9}\xD3M\xDBHܵ\xCFl[fg\xFEo\\xD0Ә\xAF\xD9\xFF\xDB\xCBN\xEB\xA0P(\x81 \x9C\xAC\xAA\xC1\xC0\xBF\x89qCVh\xA8h\x81ϕY8=\x90\xF1i;\x8Eh\x97Z\xACJny\xBC\xDA\xF9\x93\xD3S\xD7\x9E\xAC\x90b\xCF-\xE3\xCB5_|0Y\xE3\x8Cf=\xB1\x9A\xC7Ϭ{K\xF3\xF0\xF7<\x837\xA4LS\xA2\xDA\xFF9l\xC6w\x88\xBF9~\xEE\xF1uZޞԒ\xF2\xEAw\x91\x9FH\xED\x86i-\x8Cn\xDB\xD61X\xCF\xCF\xF9\xDE'\x81&3\xAD淕\xED\xA5t'.z\xCA\xE2!֊D\x97\x9C\x8C\x82<E\xC5\xF9>\xC1
+/nd\xA11\xB4\xFF\xFBr\xC4
+ \xD9e'\xA7\x98w\x85-cp\xF4\x9A\xF0\xF1\xDC\xD0ʲ\xCC\xC6;T\xF0NMA\xA4".\xD2\xD7x\x99\x99$ :ӂ[R[*הM\x83\xAA\xA3
+V\x93\xD7/v6\x89?.J:'%\x8C\xCF\xE9\x87[\x99\xC6E\xB7\x94q\xF2<\x88F\xCE"\x83\x818\xE7\xBAR\xFC\xCD\xC9}\xCDVxW\xB8UȠBQ