mp4 video doesn't play on Safari - safari

My frontend (React, videojs 7.20.1):
const playFunc = ()=> {
if (!player.src.src) {
player.src({
src: "http://192.168.0.86:8080/api/v1/" + "get-video/" + videoId,
type: "video/mp4"
});
}
player.play();
};
My backend (java 8):
#GetMapping(value = "/get-video/{video-id}", produces = "video/mp4")
public ResponseEntity<StreamingResponseBody> getVideo(#PathVariable("video-id") String videoId) throws IOException {
String filePath = "HLC_Promo_-Small.mp4";
byte[] bytes = Files.readAllBytes(Paths.get(filePath));
return ResponseEntity
.ok()
.contentType(MediaType.valueOf("video/mp4"))
.body(os -> os.write(bytes/*videoService.getVideo(videoId).getContent()*/));
}
I take data from a file in this example. It works for Chrome and Android Chrome, video plays. But not for Safari. This file also plays on QuickTime. And the file plays on Safari, if I drag and drop it on Safari. Please help me.

Related

How to upload and download Video using QuickBlox in Swift

I am uploading image and video to QuickBlox. My upload is successful but when i download the following scenarios occour:
1) In case of image i am able to download image and show (No issues in this)
2) My question is about video with below points
a) I am able upload video successfully
b) How would i be able to recognize the extension of video from server? I need sample code for uploading and downloading video from the QuickBlox. As if i upload .mov file then what should be the filetype and what should be in case of .mp4 and so on. And when i download the file how would i know the correct extension for the file and then how to get the correct file from the server and save it.
When uploading a video, set the type to "video" in the attachment and the corresponding content type, for example "video/mp4":
QBRequest.uploadFile(with: localVideoUrl, fileName: nameVideo, contentType: "video/mp4", isPublic: true,
successBlock: { (response: QBResponse, uploadedBlob: QBCBlob) -> Void in
let attachment = QBChatAttachment()
attachment.id = uploadedBlob.uid
attachment.name = uploadedBlob.name
attachment.type = "video"
attachment["size"] = "\(uploadedBlob.size)"
// send message with attachment
}, statusBlock: { (request : QBRequest?, status : QBRequestStatus?) -> Void in
let progress = CGFloat(Float(status.percentOfCompletion))
// show progress
}
}) { (response : QBResponse) -> Void in
//error handler
}
When you receive a message with attachment and attachment type is "videoā€¯, refer to this example:
let attachment = message.attachments?.first
if attachment.type == "video" {
QBRequest.downloadFile(withUID: attachment.id, successBlock: { (response: QBResponse, fileData: Data) in
let fileData = fileData as NSData
let fileName = ID + "_" + attachment.name
let filePath = NSTemporaryDirectory() + fileName
let fileURL = URL(fileURLWithPath: filePath)
if fileData.write(to: fileURL, atomically: true) == true {
//do what you need with the video - cache, save, play, etc.
} else {
print("failure write")
}
}, statusBlock: { (request: QBRequest, status: QBRequestStatus?) in
let progress = CGFloat(status.percentOfCompletion)
//show progress
}, errorBlock: { (response: QBResponse) in
//error handler
})
}

No sound in safari using Web Audio API webkitAudioContext()

I am trying to use the Web Audio API to play sound in my React application.
It's currently playing sound in all browsers except Safari v12.1.
I am aware Safari has restrictions on autoplay and requires user interaction to play sound, so I have a play button which calls the _play() function:
_play = (url, index) => {
this._getData(url);
this.source.start(index)
}
It's calling the _getData() function which looks like this:
_getData(url) {
this.source = this.audioContext.createBufferSource();
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = () => {
var audioData = request.response;
console.log(this.audioContext)
this.audioContext.decodeAudioData(audioData, buffer => {
this.source.buffer = buffer;
this.source.connect(this.audioContext.destination);
},
function(e){ console.log("Error with decoding audio data" + e.err); });
}
request.send();
}
this.audioContext is created in the component constructor using:
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
The console.log(this.audioContext) inside the request.onload outputs this before pressing play:
...and this after pressing play:
But no sound is playing (in Safari).
What am I doing wrong?
I think the problem that you ran into is that Safari does not allow you to modify the buffer anymore once you called start().
The following page does for example play a second of noise in Safari when you press the play button.
<!DOCTYPE html>
<html>
<body>
<button id="play-button">play</button>
<script>
document
.getElementById('play-button')
.addEventListener('click', () => {
const audioContext = new AudioContext();
const audioBufferSourceNode = audioContext.createBufferSource();
const sampleRate = audioContext.sampleRate;
const audioBuffer = audioContext.createBuffer(1, sampleRate, sampleRate);
const channelData = audioBuffer.getChannelData(0);
for (let i = 0; i < sampleRate; i += 1) {
channelData[i] = (Math.random() * 2) - 1;
}
audioBufferSourceNode.buffer = audioBuffer;
audioBufferSourceNode.connect(audioContext.destination);
audioBufferSourceNode.start(audioContext.currentTime);
});
</script>
</body>
</html>
But it doesn't work anymore if you modify it slightly. When starting the audioBufferSourceNode before assigning the buffer there will be no output anymore.
audioBufferSourceNode.connect(audioContext.destination);
audioBufferSourceNode.start(audioContext.currentTime);
audioBufferSourceNode.buffer = audioBuffer;
I guess you can get your code working by waiting for the HTTP response and the audio decoding before you start the source. Make sure to execute this.source.buffer = buffer before you execute this.source.start(index).
I hope this helps.

Open pdf from bytes array in angular 5

I was following the below links for displaying pdf page in new tab in my angular 5 application. But unable to achieve the result.
I am consuming the bytes array from spring controller api.
PDF Blob is not showing content, Angular 2
PDF Blob - Pop up window not showing content
Angular2 Displaying PDF
I tried the below options but none of them is working.
Trial 1
Consumed the response as json
component.ts
clickEvent(){
this.service.getPDF().subscribe((response)=>{
let file = new Blob([response.byteString], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
})
}
service.ts
getPDF(){
const url = `${this.serviceUrl}/pdf`;
const httpOptions = {
headers: new HttpHeaders(
{
'Accept': 'application/json',
'responseType':'blob'
}
)
};
return this.http.get<any>(url, httpOptions);
}
Trial 2
Consumed the response as json
component.ts
clickEvent(){
this.service.getPDF().subscribe((response)=>{
let file = new Blob([response.byteArray], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
})
}
service.ts
getPDF(){
const url = `${this.serviceUrl}/pdf`;
const httpOptions = {
headers: new HttpHeaders(
{
'Accept': 'application/json',
'responseType':'arraybuffer'
}
)
};
return this.http.get<any>(url, httpOptions);
}
Trial 3
Consumed the response as bytes
component.ts
clickEvent(){
this.service.getPDF().subscribe((response)=>{
let file = new Blob([response], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
})
}
service.ts
getPDF(){
const url = `${this.serviceUrl}/pdf`;
const httpOptions = {
headers: new HttpHeaders(
{
'responseType':'blob' //both combination
//'responseType' : 'arraybuffer'
}
)
};
return this.http.get<any>(url, httpOptions);
}
By all the combination I am only getting two results.
Empty pdf document or Failed to load PDF document.
For understanding posting java spring controller code.
controller.java
#GetMapping(value = "/pdf")
public ResTest generatePDF(HttpServletResponse response) throws IOException {
ResTest test = new ResTest();
ByteArrayOutputStream baos = docTypeService.createPdf();
test.setByteArray(baos.toByteArray());
test.setByteString(new String(baos.toByteArray()));
return test;
}
At last, I was able to render pdf. There were two small mistakes from my side.
1 st Problem was, I gave 'responseType' inside HttpHeaders which was wrong.
It should be outside as below.
2 nd Problem was, even though if you mention as responseType : 'arraybuffer', it was unable to take it. For that you need to mention as responseType : 'arraybuffer' as 'json'.(Reference)
The corrected and working code below.
Trial 3
component.ts (nochanges)
clickEvent(){
this.service.getPDF().subscribe((response)=>{
let file = new Blob([response], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
})
service.ts
getPDF(){
const url = `${this.serviceUrl}/pdf`;
const httpOptions = {
'responseType' : 'arraybuffer' as 'json'
//'responseType' : 'blob' as 'json' //This also worked
};
return this.http.get<any>(url, httpOptions);
}
Referred from the below link
https://github.com/angular/angular/issues/18586
I had the same problem with angular and pdf display. I will describe my solution - use base64 encoded string. All modern browsers support base64.
Use import java.util.Base64 to decode your byte array
byte[] bytes = baos.toByteArray();
String string = Base64.getEncoder().encodeToString(bytes);
test.setByteString(string);
On the frontend side use standard mime type for pdf and indicate that you are using base64 data:application/pdf;base64,.
Ref. to mime types: https://en.wikipedia.org/wiki/Media_type
If you need to open document in a new window:
let newPdfWindow = window.open("","Print");
let content = encodeURIComponent(response.byteString);
let iframeStart = "<\iframe width='100%' height='100%' src='data:application/pdf;base64, ";
let iframeEnd = "'><\/iframe>";
newPdfWindow.document.write(iframeStart + content + iframeEnd);
If you need to open in a new tab, you may simply provide to your html href:
let pdfHref = this.sanitizer.bypassSecurityTrustUrl('data:application/octet-stream;base64,' + content);
bypassSecurityTrustUrl will sanitize your url. As I remember there was some problem with angular security, that prevented me from seeing the content.
PS. before checking how it works with angular I would like to recommend you to store the pdf file on a drive and try to open it. I mean, that you should be certainly sure that you file is valid and you may open it with simple reader.
Update. The simpliest solution is to use pdf.js library https://github.com/mozilla/pdf.js
Have you looked for an angular component to wrap pdf.js?
https://github.com/VadimDez/ng2-pdf-viewer
Sample usage:
<pdf-viewer [src]="pdfSrc"
[render-text]="true"
style="display: block;">
</pdf-viewer>
pdfSrc can be a url string or a UInt8Array
When you make AJAX call to get PDF/file stream
var req = this.getYourPDFRequest(fd);
this.postData(environment.previewPDFRFR, req).then(res => {
res.blob().then(blob => {
const fileURL = URL.createObjectURL(blob);
window.open(fileURL, '', 'height=650,width=840');
})
});
If ur byte array comes from a .net backend u have to return
return File(doc.BinaryData, "application/pdf"); // page visible in typescript
, and not this :
return Ok(doc.BinaryData); // page blank in typescript

Share screen using getScreenId.js in WebRTC for two peers

I am trying to implement share screen function in webrtc video conferencing. From suggestion, I am now following muaz-khan's solution using https://www.webrtc-experiment.com/getScreenId/ . I can easily capture the application images of one peer, and replace the video stream with the capture stream. But it is a video conferencing experiment, so two browsers need to video conference with each other. For example, browser 1, has video streams A (local video), video streams B (remote video); browser 2 has video streams B (local video), video streams A (remote video). So when I am in browser 1 and trying to share the screen, the share screen stream should replace the local video in browser 1, and remote video in browser 2.
But right now, I can only make the share screen replace the local video in browser 1, browser 2 doesn't have any changes, cann't see any changes in its remote video (which is the local video in browser 1). I don't know how to trigger the changes in browser 2 as well. do i need to signal the share screen streams to server? and change the remote stream accordingly?
Here is my code in javascript:
$(function() {
var brokerController, ws, webRTC, localid;
// ws = new XSockets.WebSocket("wss://rtcplaygrouund.azurewebsites.net:443", ["connectionbroker"], {
ws = new XSockets.WebSocket("ws://localhost:4502", ["connectionbroker"], {
ctx: "152300ed-4d84-4e72-bc99-965052dc1e95"
});
var addRemoteVideo = function(peerId,mediaStream) {
var remoteVideo = document.createElement("video");
remoteVideo.setAttribute("autoplay", "true");
remoteVideo.setAttribute("rel",peerId);
attachMediaStream(remoteVideo, mediaStream);
remoteVideo.setAttribute("class", "col-md-3");
remoteVideo.setAttribute("height", $( document ).height() * 0.3);
remoteVideo.setAttribute("id", 'remoteVideo');
$("#videoscreen").append(remoteVideo);
};
var onConnectionLost = function (remotePeer) {
console.log("onconnectionlost");
var peerId = remotePeer.PeerId;
var videoToRemove = $("video[rel='" + peerId + "']");
videoToRemove.remove();
};
var oncConnectionCreated = function() {
console.log("oncconnectioncreated", arguments);
}
var onGetUerMedia = function(stream) {
console.log("Successfully got some userMedia , hopefully a goat will appear..");
webRTC.connectToContext(); // connect to the current context?
};
var onRemoteStream = function (remotePeer) {
addRemoteVideo(remotePeer.PeerId, remotePeer.stream);
console.log("Opps, we got a remote stream. lets see if its a goat..");
};
var onLocalStream = function(mediaStream) {
console.log("Got a localStream", mediaStream.id);
localid = mediaStream.id;
console.log("check this id: meadiastram id ", mediaStream.id);
var video = document.createElement("video");
video.setAttribute("height", "100%");
video.setAttribute("autoplay", "true");
video.setAttribute("id", "localvideo");
video.setAttribute("name", mediaStream.id);
attachMediaStream(video, mediaStream);
$("#videoscreen").append(video);
$('#share').click(function() {
getScreenId(function (error, sourceId, screen_constraints) {
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
navigator.getUserMedia(screen_constraints, function (stream) {
$('#localvideo').attr('src', URL.createObjectURL(stream));
}, function (error) {
console.error(error);
});
});
});
};
var onContextCreated = function(ctx) {
console.log("RTC object created, and a context is created - ", ctx);
webRTC.getUserMedia(webRTC.userMediaConstraints.hd(true), onGetUerMedia, onError);
};
var onOpen = function() {
console.log("Connected to the brokerController - 'connectionBroker'");
webRTC = new XSockets.WebRTC(this);
webRTC.onlocalstream = onLocalStream;
webRTC.oncontextcreated = onContextCreated;
webRTC.onconnectioncreated = oncConnectionCreated;
webRTC.onconnectionlost = onConnectionLost;
webRTC.onremotestream = onRemoteStream;
};
var onConnected = function() {
console.log("connection to the 'broker' server is established");
console.log("Try get the broker controller form server..");
brokerController = ws.controller("connectionbroker");
brokerController.onopen = onOpen;
};
ws.onconnected = onConnected;
});
I am using xsocket as the server, and the codes for click share and change the local stream with the share screen streams are just very simple as this:
$('#share').click(function() {
getScreenId(function (error, sourceId, screen_constraints) {
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
navigator.getUserMedia(screen_constraints, function (stream) {
$('#localvideo').attr('src', URL.createObjectURL(stream));
}, function (error) {
console.error(error);
});
});
Any help or suggestion would be grateful.
Thanks for pointing out the other post: How to addTrack in MediaStream in WebRTC, but I don't think they are the same. And also I am not sure how to renegotiate the remote connection in this case.
Xsocket.webrtc.js file for webrtc connection:
https://github.com/XSockets/XSockets.WebRTC/blob/master/src/js/XSockets.WebRTC.latest.js
How I could I renegotiate the remote connection in this case?
I figured out a work around solution by myself for this question, do not replace the local stream with the sharescreen stream, instead remove the old local stream from local div, then add the new sharescreen stream to local div. In the meantime, send the old local stream id by datachanel to the other peer, and remove that old remote video as well.
The most important thing is reflesh the streams (renegotiation), then sharescreen stream would display in remote peer.
Code:
$('#share').click(function() {
getScreenId(function (error, sourceId, screen_constraints) {
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
navigator.getUserMedia(screen_constraints, function (stream) {
webRTC.removeStream(webRTC.getLocalStreams()[0]);
var id = $('#localvideo').attr('name');
$('#localvideo').remove();
brokerController.invoke('updateremotevideo', id);
webRTC.addLocalStream(stream);
webRTC.getRemotePeers().forEach(function (p) {
webRTC.refreshStreams(p);
});
}, function (error) {
console.error(error);
});
});
});
after get the command to remove that old video stream from the server:
brokerController.on('updateremotevideo', function(streamid){
$(document.getElementById(streamid)).remove();
});
This solution works for me. Although if only like to replace the local video stream with share screen stream, we need to re create the offer with sdp, and send sdp to remote peer. It is more complicated.
getScreenId(function (error, sourceId, screen_constraints) {
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
navigator.getUserMedia(screen_constraints, function (stream) {
navigator.getUserMedia({audio: true}, function (audioStream) {
stream.addTrack(audioStream.getAudioTracks()[0]);
var mediaRecorder = new MediaStreamRecorder(stream);
mediaRecorder.mimeType = 'video/mp4'
mediaRecorder.stream = stream;
self.setState({recorder: mediaRecorder, startRecord: true, shareVideo: true, pauseRecord: false, resumeRecord: false, stopRecord: false, downloadRecord: false, updateRecord: false});
document.querySelector('video').src = URL.createObjectURL(stream);
var video = document.getElementById('screen-video')
if (video) {
video.src = URL.createObjectURL(stream);
video.width = 360;
video.height = 300;
}
}, function (error) {
alert(error);
});
}, function (error) {
alert(error);
});
});

Titanium - Get image file from filesystem on Android

I have a problem getting an image from filesystem. On iOS works fine.
First of all, I save a remote image in the filesystem with this function:
img.imagen = url from the remote image (e.g. http://onesite.es/img2.jpeg)
function descargarImagen(img, callback){
var path = img.imagen;
var filename = path.split("/").pop();
var xhr = Titanium.Network.createHTTPClient({
onload: function() {
// first, grab a "handle" to the file where you'll store the downloaded data
var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, filename);
f.write(this.responseData); // write to the file
Ti.API.debug("-- Imagen guardada: " + f.nativePath);
callback({path: f.nativePath});
},
timeout: 10000
});
xhr.open('GET', path);
xhr.send();
}
Now, I want to share this image creating an Android Intent:
args.image = f.nativePath(in the previous function)
var intent = null;
var intentType = null;
intent = Ti.Android.createIntent({
action: Ti.Android.ACTION_SEND
});
// add text status
if (args.status){
intent.putExtra(Ti.Android.EXTRA_TEXT, args.status);
}
// change type according to the content
if (args.image){
intent.type = "image/*";
intent.putExtraUri(Ti.Android.EXTRA_STREAM, args.image);
}else{
intent.type = "text/plain";
intent.addCategory(Ti.Android.CATEGORY_DEFAULT);
}
// launch intent
Ti.Android.currentActivity.startActivity(Ti.Android.createIntentChooser(intent, args.androidDialogTitle));
What I'm doing wrong?