Video streaming - dealing with header 206 (php to HTML5) - http-headers

As usual, it never works for me...
I try to stream video and I'm doing very well with the code below:
function send_back_video($video_path)
{
ob_clean();
$mime = "application/octet-stream"; // The MIME type of the file, this should be replaced with your own.
$size = filesize($video_path); // The size of the file
// Send the content type header
header('Content-type: ' . $mime);
// Check if it's a HTTP range request
if(isset($_SERVER['HTTP_RANGE']))
{
// Parse the range header to get the byte offset
$ranges = array_map(
'intval', // Parse the parts into integer
explode(
'-', // The range separator
substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header
)
);
// If the last range param is empty, it means the EOF (End of File)
if(!$ranges[1]){
$ranges[1] = $size - 1;
}
// Send the appropriate headers
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header('Content-Length: ' . ($ranges[1] - $ranges[0])); // The size of the range
// Send the ranges we offered
header(
sprintf(
'Content-Range: bytes %d-%d/%d', // The header format
$ranges[0], // The start range
$ranges[1], // The end range
$size // Total size of the file
)
);
// It's time to output the file
$f = fopen($video_path, 'rb'); // Open the file in binary mode
$chunkSize = 8192; // The size of each chunk to output
// Seek to the requested start range
fseek($f, $ranges[0]);
// Start outputting the data
while(true)
{
// Check if we have outputted all the data requested
if(ftell($f) >= $ranges[1])
{
break;
}
// Output the data
echo fread($f, $chunkSize);
// Flush the buffer immediately
#ob_flush();
flush();
}
}
else {
// It's not a range request, output the file anyway
header('Content-Length: ' . $size);
// Read the file
#readfile($file);
// and flush the buffer
#ob_flush();
flush();
}
}
"So if it works, what do you want?" - you might ask..
As I stated before, the code does a very good job, BUT there is 1 issue.
If I start to play video, php sends header('HTTP/1.1 206 Partial Content') and this (video) request stays open till EOF, what means, I am FORCED to download entire video even if don't want to (let's say I'm clicking on the next video).
My JS removes video tag (this should ABORT request) and creates new video tag with new link. ABORT part is not happening.
Here you can see GET the example:
1st - initial video get
2nd - play button pressed
3rd - navigate away - new DOM item created and waiting to appear.
So what I'm looking for, is some option to close the connection on navigate away.
I saw youtube does many GETs. As I understand every GET brings new "chunk" when previous "chunk" close to end. It's very good option, but it cancels buffering and I want to be able to buffer video.
P.S.
For sure I'm doing something wrong, I just can't see what.
P.P.S.
I'm not the author of the code.

Related

NAudio: How to accurately get the current play location when changing play position using AudioFileReader and WaveOutEvent

I'm creating an application that needs to allow the user to select the play position of an audio file while the file is playing. However, once the position is changed, I'm having trouble identifying the current position.
Here's an example program that shows how I'm going about it.
using NAudio.Utils;
using NAudio.Wave;
using System;
namespace NAudioTest
{
class Program
{
static void Main()
{
var audioFileReader = new AudioFileReader("test.mp3");
var waveOutEvent = new WaveOutEvent();
waveOutEvent.Init(audioFileReader);
waveOutEvent.Play();
while (true)
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter)
{
var playLocationSeconds =
waveOutEvent.GetPositionTimeSpan().TotalSeconds;
Console.WriteLine(
"Play location is " + playLocationSeconds + "seconds");
}
else if (key.Key == ConsoleKey.RightArrow)
{
audioFileReader.CurrentTime =
audioFileReader.CurrentTime.Add(TimeSpan.FromSeconds(30));
}
else
{
break;
}
}
}
}
}
Steps to reproduce the problem
Start the program: the audio file starts playing
Press the enter key: the current play time is written to the console
Press the right arrow key: the played audio jumps ahead (presumably) to the expected location
Press the Enter key again: a play time is written to the
console, but it looks to be the amount of time since the audio first
started playing, not the time of the current play position.
I have tried getting the value of AudioFileReader.CurrentTime instead of calling GetPositionTimeSpan on the WaveOutEvent. The problem with this approach is that the AudioFileReader.CurrentTime value proceeds in jumps, presumably because this underlying stream is buffered when used with WaveOutEvent so CurrentTime does not accurately reflect the play position, only the position in the underlying stream.
How do I support arbitrary play positioning yet continue to get an accurate play position current time?
The "CurrentTime" property of your audio file reader is good enough to tell the current position of playback, especially if your latency is not very high. I found the difference between it and waveOutEvent.GetPositionTimeSpan() to be 100-200 ms. at most.
You are indeed using the setter of the CurrentTime property to reposition within the stream. It would be consistent to use the getter to then query the current position as well. If you are concerned with precision, you can use lower latency.
The extension method "GetPositionTimeSpan()" does seem to return the total length of playback so far and not the position within the stream. Admittedly I do not know why this is so.

Rotativa pdf doesn't work when published to Azure Server

I have a web app with Asp.net MVC 5 and I used Rotativa for the pdf.
Rotativa doesn't work when published to Azure Server but it works on my local computer.
It is giving bellow error
500 - THE REQUEST TIMED OUT.
The web server failed to respond within the specified time.
Bellow is my code.
public ActionResult DoPdf(int id)
{
return new ActionAsPdf("PrintMyPdf", new { id = id }) { FileName = string.Format("Demo_{0}.pdf", id) };
}
Here is a list of frameworks and scenarios that have been found to be not be usable due to the restrictions byAzure Web App sandbox
https://github.com/projectkudu/kudu/wiki/Azure-Web-App-sandbox#unsupported-frameworks
Please suggest how can I do PDF of my HTML view in MVC 5?
I suggest you to use the HTML to PDF for Azure Websites from EvoPdf. You have full control over the HTML to PDF Azure cloud service because you are the owner of the service and you don't have to worry about thing like the security of the data you send for conversion. The code you are using in your website looks like below:
protected void convertToPdfButton_Click(object sender, EventArgs e)
{
// Get the server IP and port
String serverIP = textBoxServerIP.Text;
uint serverPort = uint.Parse(textBoxServerPort.Text);
// Create a HTML to PDF converter object with default settings
HtmlToPdfConverter htmlToPdfConverter = new HtmlToPdfConverter(serverIP, serverPort);
// Set optional service password
if (textBoxServicePassword.Text.Length > 0)
htmlToPdfConverter.ServicePassword = textBoxServicePassword.Text;
// Set HTML Viewer width in pixels which is the equivalent in converter of the browser window width
htmlToPdfConverter.HtmlViewerWidth = int.Parse(htmlViewerWidthTextBox.Text);
// Set HTML viewer height in pixels to convert the top part of a HTML page
// Leave it not set to convert the entire HTML
if (htmlViewerHeightTextBox.Text.Length > 0)
htmlToPdfConverter.HtmlViewerHeight = int.Parse(htmlViewerHeightTextBox.Text);
// Set PDF page size which can be a predefined size like A4 or a custom size in points
// Leave it not set to have a default A4 PDF page
htmlToPdfConverter.PdfDocumentOptions.PdfPageSize = SelectedPdfPageSize();
// Set PDF page orientation to Portrait or Landscape
// Leave it not set to have a default Portrait orientation for PDF page
htmlToPdfConverter.PdfDocumentOptions.PdfPageOrientation = SelectedPdfPageOrientation();
// Set the maximum time in seconds to wait for HTML page to be loaded
// Leave it not set for a default 60 seconds maximum wait time
htmlToPdfConverter.NavigationTimeout = int.Parse(navigationTimeoutTextBox.Text);
// Set an adddional delay in seconds to wait for JavaScript or AJAX calls after page load completed
// Set this property to 0 if you don't need to wait for such asynchcronous operations to finish
if (conversionDelayTextBox.Text.Length > 0)
htmlToPdfConverter.ConversionDelay = int.Parse(conversionDelayTextBox.Text);
// The buffer to receive the generated PDF document
byte[] outPdfBuffer = null;
if (convertUrlRadioButton.Checked)
{
string url = urlTextBox.Text;
// Convert the HTML page given by an URL to a PDF document in a memory buffer
outPdfBuffer = htmlToPdfConverter.ConvertUrl(url);
}
else
{
string htmlString = htmlStringTextBox.Text;
string baseUrl = baseUrlTextBox.Text;
// Convert a HTML string with a base URL to a PDF document in a memory buffer
outPdfBuffer = htmlToPdfConverter.ConvertHtml(htmlString, baseUrl);
}
// Send the PDF as response to browser
// Set response content type
Response.AddHeader("Content-Type", "application/pdf");
// Instruct the browser to open the PDF file as an attachment or inline
Response.AddHeader("Content-Disposition", String.Format("{0}; filename=Getting_Started.pdf; size={1}",
openInlineCheckBox.Checked ? "inline" : "attachment", outPdfBuffer.Length.ToString()));
// Write the PDF document buffer to HTTP response
Response.BinaryWrite(outPdfBuffer);
// End the HTTP response and stop the current page processing
Response.End();
}

TCPDF with FPDI templates and THEAD

I have a html table with THEAD output with writeHTML which should be displayed on every page at the top of the table. It works but when I use a template loaded by FPDI the head is white and disappears on the second page and further. I can mark the head field with the mouse but the appear white. The border only appears as a little point on the left.
I already tried to add $this->setPageMark() to the addPage method. But its still the same issue.
public function AddPage($orientation = '', $format = ''){
parent::AddPage($orientation, $format);
if($this->template != null){
$this->useTemplate($this->template);
$this->setPageMark();
}
}
you need to enable page breaks at the beginning which is causing the issue you are having;
this is a sample code snippet ;
require_once('tcpdf/tcpdf.php'); //main code
require_once('tcpdf/fpdi.php'); //read existing pdf and sends to fpdf
$pdf = new FPDI();
$pdf->setPrintHeader(false); //no header
$pdf->setPrintFooter(false);//no footer
$pdf->SetAutoPageBreak(FALSE, 0); // set auto page breaks
//loop starts here
{
$pdf->setSourceFile($page_background); //set page
$templateId = $pdf->importPage(1); //we only need the first page
$pdf->useTemplate($templateId); //use the imported page
//your write html code and any other tcpdf related code comes here
}
$pdf->Output(outcome.pdf, 'I'); //use F instead of I to show generated pdf.

Web audio API polyphony - using 2 different gain nodes not working?

I can't seem to create two oscillators with independent gain envelopes.
The code below creates two buttons which each play a sine tone at a different pitch. When I click on the first button, I hear the tone grow in volume as it should. But, when I click the second button, the tone reacts as if it is connected to the gain of the first tone. For example, if I click the second button (turning on the second tone) while the first tone is at volume 1, the second tone will enter at volume 1, even though it is supposed to envelope from 0 to 1 to 0 over the course of 10 seconds.
Can I only have one gain node per audio context? Or is there some other reason that the gains of these oscillators are being connected? In addition, after I play the tones once, I cannot play them again, which makes me especially think that I am doing something wrong. : )
Thanks. A link is below and the code is below that. This is my first post here so let me know if you need anything else. This code must be run in versions of Chrome or Safari that support the web audio api.
http://whitechord.org/just_mod/poly_test.html
WAAPI tests
<button onclick="play()">play one</button>
<button onclick="play2()">play two</button>
<script>
var context;
window.addEventListener('load', initAudio, false);
function initAudio() {
try {
context = new webkitAudioContext();
} catch(e) {
onError(e);
}
}
function play() {
var oscillator = context.createOscillator();
var gainNode = context.createGainNode();
gainNode.gain.value = 0.0;
oscillator.connect(gainNode);
gainNode.connect(context.destination);
oscillator.frequency.value = 700;
gainNode.gain.linearRampToValueAtTime(0.0, 0); // envelope
gainNode.gain.linearRampToValueAtTime(0.1, 5); // envelope
gainNode.gain.linearRampToValueAtTime(0.0, 10); // envelope
oscillator.noteOn(0);
}
function play2() {
var oscillator2 = context.createOscillator();
var gainNode2 = context.createGainNode();
gainNode2.gain.value = 0.0;
oscillator2.connect(gainNode2);
gainNode2.connect(context.destination);
oscillator2.frequency.value = 400;
gainNode2.gain.linearRampToValueAtTime(0.0, 0); // envelope
gainNode2.gain.linearRampToValueAtTime(0.1, 5); // envelope
gainNode2.gain.linearRampToValueAtTime(0.0, 10); // envelope
oscillator2.noteOn(0);
}
/* error */
function onError(e) {
alert(e);
}
</script>
</body>
</html>
Can I only have one gain node per audio context? Or is there some other reason that the gains of these oscillators are being connected? In addition, after I play the tones once, I cannot play them again, which makes me especially think that I am doing something wrong. : )
You can have as many gain nodes as you want (this is how you could achieve mixing bus-like setups, for example), so that's not the problem. Your problem is the following:
Remember that the second parameter to linearRampToValueAtTime() is time in the same time coordinate system as your context.currentTime.
And your context.currentTime is always moving forward in real time, so all your ramps, curves, etc. should be calculated relative to it.
If you want something to happen 4 seconds from now, you'd pass context.currentTime + 4 to the Web Audio API function.
So, change all your calls linearRampToValueAtTime() in your code, so that they look like:
gainNode2.gain.linearRampToValueAtTime(0.0, context.currentTime); // envelope
gainNode2.gain.linearRampToValueAtTime(0.1, context.currentTime + 5); // envelope
gainNode2.gain.linearRampToValueAtTime(0.0, context.currentTime + 10); // envelope
And that should take care of your issues.
BTW you have a stray double quote in your BODY opening markup tag.
Ask Will Conklin
gainNode2.gain.linearRampToValueAtTime(0.1, context.currentTime + 5); // envelope

How can I change the Input request stream

I want to change the input request stream.
Detail is:
I want to replace the 'Text 1' in the Input stream to 'Text 2'
The first: I use the Request.Filter as http://msdn.microsoft.com/en-us/library/system.web.httprequest.filter.aspx But this way I can only replace a byte data by an other byte, I can not replace a string by a string and I also can not change buffer size. I need to access and replace the old buffer by an other byte array (content and buffer.length)
The second: I try to use a ISAPI Filter, but It seem that the IIS 7 and above is no longer support for SF_NOTIFY_READ_RAW_DATA. I google and result is I should write a native HttpModule with CHttpModule::OnReadEntity event. Is it right ?
I would be very appreciate if you can tell me how I can change the input request stream.
Thank you very much.
Huy
I debugged and see that We will catch twice times per one request.
if I only read the entity body, then insert again, don't make any change, the second request will have exactly content and request run well. The source code as following:
// Retrieve the request body.
hr = pHttpRequest->ReadEntityBody(
pvRequestBody, byteCount, false, &byteCount, NULL);
pHttpRequest->InsertEntityBody(pvRequestBody, byteCount);
if I read the entity body, then replace 'ngo+duc+huy' by 'ngy+van+qua' (this is successful), the second request will have wrong content. This request have some strange content as: 'ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ'
so, the request haven't run property. The source code as following:
// Retrieve the request body.
hr = pHttpRequest->ReadEntityBody(
pvRequestBody, byteCount, false, &byteCount, NULL);
size_t found(0);
string requestContent = static_cast(pvRequestBody);
found = requestContent.find("ngo+duc+huy");
if(found !=string::npos)
{
requestContent = requestContent.replace(requestContent.find("ngo+duc+huy"), sizeof("ngo+duc+huy") - 1, "ngu+van+qua");
}
pvRequestBody = (void*)requestContent.c_str();<br />
pHttpRequest->InsertEntityBody(pvRequestBody, byteCount);<br />
you can download my simple solution at: http://www.4shared.com/file/OJr8GFJQ/HelloWorld_2.html