Skip to content

Commit

Permalink
1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-j-davies committed Apr 28, 2021
1 parent 56d1946 commit 277e47a
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 75 deletions.
1 change: 0 additions & 1 deletion HAPRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ function cleanEV() {
}
}

console.info(' Saving current Characteristics...');
const CharacteristicCache = {};

for (let i = 0; i < AccessoryIDs.length; i++) {
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ library, without it, projects like this one are not possible.

## Version History

- **1.3.0**
- AccessoryType in the API response now matches the AccessoryType in events.
- Exposed accessory specific actions in the accessory info UI
- Improvements to Camera code.
- Added Backup/Restore features

- **1.2.2**
- Added **.github** and **.gitattributes** to package exclusion

Expand Down
4 changes: 4 additions & 0 deletions core/accessories/Camera/Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ class Camera extends BaseAccessory {
}
Camera.prototype.setCharacteristics = Set;

Camera.prototype.KillStreams = function(){
this.CameraDelegate.KillStreams();
}

module.exports = {
Camera:Camera
}
157 changes: 96 additions & 61 deletions core/accessories/Camera/CameraSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ const CameraSource = function (Config) {

}

CameraSource.prototype.KillStreams = function(){

Object.keys(this.ongoingSessions).forEach((SI) =>{

let OS = this.ongoingSessions[SI];
this.controller.forceStopStreamingSession(OS.SessionID);
OS.FFMPEG.kill('SIGKILL');
delete this.ongoingSessions[SI]

})
}

CameraSource.prototype.attachController = function (Controller) {
this.controller = Controller;
}
Expand All @@ -31,7 +43,7 @@ CameraSource.prototype.handleSnapshotRequest = async function (request, callback
const Now = new Date().getTime();
const Diff = (Now - this.lastSnapshotTime) / 1000;

// 1 Minute snapshot cache
// snapshot cache life
if (parseInt(Diff) < this.config.snapshotCacheTime) {
callback(undefined, this.imageCache)
return;
Expand All @@ -40,6 +52,7 @@ CameraSource.prototype.handleSnapshotRequest = async function (request, callback
}

let imageBuffer = Buffer.alloc(0);
var CB = callback;

const CMD = [];
CMD.push('-analyzeduration 1')
Expand All @@ -49,23 +62,39 @@ CameraSource.prototype.handleSnapshotRequest = async function (request, callback
CMD.push('-f image2')
CMD.push('-')

const ffmpeg = spawn(this.config.processor, CMD.join(' ').split(' '), {
env: process.env
})
const ffmpeg = spawn(this.config.processor, CMD.join(' ').split(' '), {env: process.env, stderr:'ignore' })

ffmpeg.stdout.on('data', function (data) {
imageBuffer = Buffer.concat([imageBuffer, data])
})

ffmpeg.on('close', (c) => {

this.lastSnapshotTime = new Date().getTime();
this.imageCache = undefined;
this.imageCache = imageBuffer
callback(undefined, this.imageCache)

ffmpeg.on('error', (error) => {
if(CB){
CB(new Error('Snapshot failed'));
CB = undefined
}
})

ffmpeg.on('exit',(Code, Signal) =>{

let _Error = ExitDueToError(Code, Signal);

if(!_Error){

this.lastSnapshotTime = new Date().getTime();
this.imageCache = undefined;
this.imageCache = imageBuffer
if(CB){
CB(undefined, this.imageCache)
CB = undefined
}
}else{
if(CB){
CB(new Error("Return Code: " + Code + ", SIGNAL:" + Signal));
CB = undefined
}
}
})
}

CameraSource.prototype.prepareStream = async function (request, callback) {
Expand Down Expand Up @@ -115,7 +144,6 @@ CameraSource.prototype.prepareStream = async function (request, callback) {
const currentAddress = IP.address()

const addressResp = {

address: currentAddress,
}

Expand All @@ -131,6 +159,14 @@ CameraSource.prototype.prepareStream = async function (request, callback) {
callback(null, response);
}

function ExitDueToError(Code, Signal){

if(Code == null && Signal === 'SIGKILL') {return false}
if(Code === 255 && Signal == null) {return false}
if(Code > 0 && Code < 255 && Signal == null) {return true}

}

CameraSource.prototype.handleStreamRequest = async function (request, callback) {

const sessionID = request['sessionID']
Expand All @@ -141,20 +177,22 @@ CameraSource.prototype.handleStreamRequest = async function (request, callback)
switch (requestType) {

case "reconfigure":
// to do
console.info(' CameraSource: HomeKit requested Stream to reconfigure');
callback(undefined);
break;

case "stop":
const ffmpegProcess = this.ongoingSessions[sessionIdentifier]
if (ffmpegProcess) {
ffmpegProcess.kill('SIGKILL')
console.info(' CameraSource: HomeKit requested Stream to stop');
const OS = this.ongoingSessions[sessionIdentifier]
if (OS) {
OS.FFMPEG.kill('SIGKILL')
}
delete this.ongoingSessions[sessionIdentifier]
callback(undefined);
break;

case "start":
console.info(' CameraSource: HomeKit requested Stream to start');
const sessionInfo = this.pendingSessions[sessionIdentifier]

if (sessionInfo) {
Expand Down Expand Up @@ -265,69 +303,66 @@ CameraSource.prototype.handleStreamRequest = async function (request, callback)
CMD.push('srtp://' + targetAddress + ':' + targetAudioPort + '?rtcpport=' + targetAudioPort + '&pkt_size=' + MaxPaketSize)
}

const ffmpeg = spawn(this.config.processor, CMD.join(' ').split(' '), {
env: process.env,
stdout: 'ignore'
})

let live = false;
let CBCalled = false;

ffmpeg.stderr.on('data', data => {
if (!live) {
var CB = callback;

if (data.toString().includes('frame=')) {
const ffmpeg = spawn(this.config.processor, CMD.join(' ').split(' '), { env: process.env, stdout:'ignore' })

live = true;
CBCalled = true;
callback(undefined);
// up and running
ffmpeg.stderr.on('data', (data) => {
if(data.toString().includes('frame=')){

this.ongoingSessions[sessionIdentifier] = ffmpeg
if(CB){
console.info(' CameraSource: Stream is now running');
CB(undefined)
CB = undefined;
this.ongoingSessions[sessionIdentifier] = {FFMPEG:ffmpeg,SessionID:sessionID}
delete this.pendingSessions[sessionIdentifier]
}

}
});

ffmpeg.on('error', error => {

if (!live) {

if (!CBCalled) {
})

callback(error);
CBCalled = true;
}
// error before start
ffmpeg.on('error',(error)=>{

console.info(' CameraSource: Stream failed to start');
if (CB) {
CB(new Error('Streaming failed'));
CB = undefined
delete this.pendingSessions[sessionIdentifier]
} else {
this.controller.forceStopStreamingSession(sessionID);
if (this.ongoingSessions.hasOwnProperty(sessionIdentifier)) {
delete this.ongoingSessions[sessionIdentifier]
}
}

})

});

ffmpeg.on('exit', (c, s) => {
if (c !== undefined && c !== 255) {
// process exit (error or expected)
ffmpeg.on('exit', (Code, Signal) =>{

if (!live) {
let _Error = ExitDueToError(Code, Signal);

if (!CBCalled) {
callback(new Error("Return Code: " + c + ", SIGNAL:" + s));
CBCalled = true;
}
if(_Error){

console.info(' CameraSource: Stream process terminated due to error');
if(CB){
CB(new Error("Return Code: " + Code + ", SIGNAL:" + Signal));
CB = undefined
delete this.pendingSessions[sessionIdentifier]
} else {
}else{
this.controller.forceStopStreamingSession(sessionID);
if (this.ongoingSessions.hasOwnProperty(sessionIdentifier)) {
delete this.ongoingSessions[sessionIdentifier]
}
delete this.ongoingSessions[sessionIdentifier]
}
}
});

}else{

if(!ffmpeg.killed){
console.info(' CameraSource: Stream process terminated unexpectedly');
this.controller.forceStopStreamingSession(sessionID);
delete this.ongoingSessions[sessionIdentifier]
}
else{
console.info(' CameraSource: Stream process terminated as per request');
}
}
})
}
break;
}
Expand Down
Loading

0 comments on commit 277e47a

Please sign in to comment.