Reputation: 11
I am currently working on a project that involves capturing the screen content of a Samsung TV running Tizen OS in real-time and transmitting the color information to a connected Hyperion server for ambient lighting purposes. Here is a summary of what I have achieved so far and the challenges I am facing: What I've Achieved:
Also, if someone wants the code from what I achieved sending screenshots from video to Hyperion here is the code (run it on Tizen Studio) main.js:
// Main
document.addEventListener('DOMContentLoaded', function () {
log('Document loaded and DOM fully parsed');
document.getElementById('startCapture').addEventListener('click', startCapture);
document.getElementById('remoteButton').addEventListener('click', startCapture);
document.getElementById('testImageButton').addEventListener('click', sendTestImage);
document.getElementById('sendColorButton').addEventListener('click', sendRandomColor);
document.getElementById('startVideoCaptureButton').addEventListener('click', startVideoCapture);
document.getElementById('stopVideoCaptureButton').addEventListener('click', stopVideoCapture);
document.addEventListener('keydown', function(event) {
switch (event.key) {
case 'Enter':
log('Remote control button pressed');
document.activeElement.click();
break;
case 'ArrowUp':
focusPreviousElement();
break;
case 'ArrowDown':
focusNextElement();
break;
}
});
initializeWebSocket();
});
function focusPreviousElement() {
let focusable = document.querySelectorAll('[tabindex]');
let index = Array.prototype.indexOf.call(focusable, document.activeElement);
if (index > 0) {
focusable[index - 1].focus();
}
}
function focusNextElement() {
let focusable = document.querySelectorAll('[tabindex]');
let index = Array.prototype.indexOf.call(focusable, document.activeElement);
if (index < focusable.length - 1) {
focusable[index + 1].focus();
}
}
function startCapture() {
log('Start Capture button clicked');
captureScreen();
}
function captureScreen() {
log('Capturing screen');
const captureArea = document.getElementById('captureArea');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const fixedWidth = 120;
const fixedHeight = 120;
canvas.width = fixedWidth;
canvas.height = fixedHeight;
try {
// Colores alternativos para asegurar que la imagen no sea negra
const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange'];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
context.fillStyle = randomColor;
context.fillRect(0, 0, canvas.width, canvas.height);
const text = captureArea.innerText || captureArea.textContent;
context.font = '30px Arial';
context.fillStyle = 'white';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(text, canvas.width / 2, canvas.height / 2);
const imageData = canvas.toDataURL('image/jpeg', 0.03).split(',')[1]; // Lower quality to 0.03
log('Screenshot captured, Base64 length: ' + imageData.length);
sendToHyperion(imageData);
} catch (error) {
log('Error capturing screen: ' + error);
}
}
function sendTestImage() {
log('Sending test image to Hyperion');
const testImageUrl = 'https://eligeeducar.cl/content/uploads/2019/10/Imagen-que-dice-rojo-sobre-un-fondo-azul-y-azul-sobre-un-fondo-rojo--1920x550.jpg';
fetch(testImageUrl)
.then(response => response.blob())
.then(blob => compressImage(blob))
.then(imageData => sendToHyperion(imageData))
.catch(error => {
log('Error fetching test image: ' + error);
});
}
function compressImage(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
const img = new Image();
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const maxWidth = 120;
const maxHeight = 120;
let width = img.width;
let height = img.height;
if (width > height) {
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
} else {
if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
}
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
const imageData = canvas.toDataURL('image/jpeg', 0.03).split(',')[1]; // Lower quality to 0.03
log('Compressed image captured, Base64 length: ' + imageData.length);
resolve(imageData);
};
img.onerror = reject;
img.src = reader.result;
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
let ws;
let messageQueue = [];
function sendToHyperion(imageData) {
const jsonData = {
command: "image",
imagedata: imageData,
name: "TizenScreenCapture",
format: "auto",
origin: "TizenApp",
priority: 50,
duration: 30000 // Duración de 30 segundos
};
log('Sending screenshot to Hyperion');
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(jsonData));
} else {
messageQueue.push(JSON.stringify(jsonData));
}
}
function initializeWebSocket() {
const ipHyperion = '192.168.88.101';
const puertoHyperion = 8090;
ws = new WebSocket(`ws://${ipHyperion}:${puertoHyperion}/jsonrpc`);
ws.onopen = function () {
log('WebSocket connection opened');
while (messageQueue.length > 0) {
ws.send(messageQueue.shift());
}
};
ws.onmessage = function (event) {
log('Message from server: ' + event.data);
};
ws.onerror = function (error) {
log('WebSocket error: ' + error.message);
};
ws.onclose = function () {
log('WebSocket connection closed');
// Intentar reconectar después de 1 segundo
setTimeout(initializeWebSocket, 1000);
};
}
function sendRandomColor() {
const ipHyperion = '192.168.88.101';
const puertoHyperion = 8090;
const colors = [
[255, 0, 0], // Red
[0, 255, 0], // Green
[0, 0, 255], // Blue
[255, 255, 0], // Yellow
[255, 0, 255], // Magenta
[0, 255, 255] // Cyan
];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
const jsonData = {
command: "color",
color: randomColor,
duration: 120000, // 2 minutes
priority: 20,
origin: "TizenApp"
};
log('Sending color to Hyperion: ' + JSON.stringify(jsonData));
fetch(`http://${ipHyperion}:${puertoHyperion}/json-rpc`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonData)
})
.then(response => response.json())
.then(data => log('Response from Hyperion: ' + JSON.stringify(data)))
.catch(error => log('Error sending color to Hyperion: ' + error));
}
// Configuración del reproductor de YouTube
let player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'Gt6wKDnG0xA',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
let captureInterval;
function startVideoCapture() {
if (!player || !player.getIframe) {
log('YouTube Player not ready');
return;
}
log('Starting video capture');
const iframe = player.getIframe();
const videoElement = iframe.contentWindow.document.querySelector('video');
if (!videoElement) {
log('Video element not found');
return;
}
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 120;
canvas.height = 120;
captureInterval = setInterval(() => {
try {
context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
const imageData = canvas.toDataURL('image/jpeg', 0.03).split(',')[1]; // Lower quality to 0.03
sendToHyperion(imageData);
} catch (error) {
log('Error capturing video frame: ' + error);
clearInterval(captureInterval);
}
}, 100); // Capturar cada 100 ms
}
function stopVideoCapture() {
log('Stopping video capture');
clearInterval(captureInterval);
}
function onPlayerReady(event) {
event.target.playVideo();
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.PLAYING) {
log('YouTube Player playing');
startVideoCapture();
} else {
log('YouTube Player paused or ended');
stopVideoCapture();
}
}
function log(message) {
const logs = document.getElementById('logs');
const logMessage = document.createElement('p');
logMessage.textContent = message;
logs.appendChild(logMessage);
console.log(message);
}
index.html:
<!DOCTYPE html>
<html>
<head>
<title>SamyGrabber</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #121212;
color: white;
text-align: center;
padding: 20px;
}
#captureArea {
width: 100%;
height: 300px;
border: 1px solid #ccc;
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
}
#logs {
background-color: #222;
padding: 10px;
border-radius: 5px;
margin-top: 20px;
max-height: 200px;
overflow-y: scroll;
text-align: left;
}
.button-container {
display: flex;
justify-content: center;
gap: 20px;
}
button {
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 5px;
background-color: #6200ee;
color: white;
cursor: pointer;
}
button:hover {
background-color: #3700b3;
}
</style>
</head>
<body>
<h1>SamyGrabber</h1>
<div id="captureArea" contenteditable="true">
<span>Texto</span>
</div>
<div class="button-container">
<button id="startCapture" tabindex="1">Iniciar Captura</button>
<button id="remoteButton" tabindex="2">Captura con Control Remoto</button>
<button id="testImageButton" tabindex="3">Enviar Imagen de Prueba</button>
<button id="sendColorButton" tabindex="4">Enviar Color Aleatorio</button>
<button id="startVideoCaptureButton" tabindex="5">Iniciar Captura de Video</button>
<button id="stopVideoCaptureButton" tabindex="6">Detener Captura de Video</button>
</div>
<div id="logs"></div>
<div id="player"></div>
<script src="https://www.youtube.com/iframe_api"></script>
<script src="main.js"></script>
</body>
</html>
Challenges:
Any guidance, documentation, or examples would be immensely helpful. Thank you in advance for your assistance!
Upvotes: 1
Views: 1211
Reputation: 1
Upvotes: 0
Reputation: 36
In general, third-party companies are not allowed to use JavaScript while in the background. Allowing JavaScript behavior in the background requires very high privileges, and if this feature is absolutely necessary, you will need to contact your app manager for assistance.
It appears that you are trying to capture a canvas that exists within an app. From your question, it seems that you would also like to capture content that is outside of your app. However, apps are unable to capture anything external to themselves due to policy restrictions. This function is not supported.
Upvotes: 1