diff --git a/binding.gyp b/binding.gyp index d0e3d17..a98b11f 100755 --- a/binding.gyp +++ b/binding.gyp @@ -11,6 +11,7 @@ "src/Contours.cc", "src/Point.cc", "src/VideoCaptureWrap.cc", + "src/VideoWriterWrap.cc", "src/CamShift.cc", "src/HighGUI.cc", "src/FaceRecognizer.cc", diff --git a/examples/write-video.js b/examples/write-video.js new file mode 100644 index 0000000..f2a95f3 --- /dev/null +++ b/examples/write-video.js @@ -0,0 +1,29 @@ +var cv = require('../lib/opencv'); +var path = require('path'); + +var vid = new cv.VideoCapture(path.join(__dirname, 'files', 'motion.mov')); + +vid.read(function(err, mat) { + if (err) throw err; + + var filename = './tmp/output-'+new Date().getTime()+'.avi'; + var writer = new cv.VideoWriter(filename, 'DIVX', vid.getFPS(), mat.size(), true); + writer.writeSync(mat); + + var x = 0; + var iter = function () { + vid.read(function (err, m2) { + x++; + writer.writeSync(m2); + if (x < 100) { + iter(); + } else { + vid.release(); + writer.release(); + } + }) + }; + iter(); +}); + + diff --git a/lib/opencv.js b/lib/opencv.js index 6184fbe..a594700 100755 --- a/lib/opencv.js +++ b/lib/opencv.js @@ -8,6 +8,7 @@ var cv = module.exports = require('./bindings'); var Matrix = cv.Matrix , VideoCapture = cv.VideoCapture + , VideoWriter = cv.VideoWriter , ImageStream , ImageDataStream , ObjectDetectionStream diff --git a/src/VideoCaptureWrap.cc b/src/VideoCaptureWrap.cc index b4e4634..6b24d65 100755 --- a/src/VideoCaptureWrap.cc +++ b/src/VideoCaptureWrap.cc @@ -35,6 +35,7 @@ void VideoCaptureWrap::Init(Local target) { Nan::SetPrototypeMethod(ctor, "setPosition", SetPosition); Nan::SetPrototypeMethod(ctor, "getFrameAt", GetFrameAt); Nan::SetPrototypeMethod(ctor, "getFrameCount", GetFrameCount); + Nan::SetPrototypeMethod(ctor, "getFPS", GetFPS); Nan::SetPrototypeMethod(ctor, "release", Release); Nan::SetPrototypeMethod(ctor, "ReadSync", ReadSync); Nan::SetPrototypeMethod(ctor, "grab", Grab); @@ -114,6 +115,17 @@ NAN_METHOD(VideoCaptureWrap::GetFrameCount) { info.GetReturnValue().Set(Nan::New(cnt)); } + +NAN_METHOD(VideoCaptureWrap::GetFPS) { + Nan::HandleScope scope; + VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + + int fps = int(v->cap.get(CV_CAP_PROP_FPS)); + + info.GetReturnValue().Set(Nan::New(fps)); +} + + NAN_METHOD(VideoCaptureWrap::GetHeight) { Nan::HandleScope scope; VideoCaptureWrap *v = Nan::ObjectWrap::Unwrap(info.This()); diff --git a/src/VideoCaptureWrap.h b/src/VideoCaptureWrap.h index 55a73f6..a8a2dc2 100755 --- a/src/VideoCaptureWrap.h +++ b/src/VideoCaptureWrap.h @@ -29,6 +29,8 @@ public: static NAN_METHOD(SetPosition); static NAN_METHOD(GetFrameCount); + static NAN_METHOD(GetFPS); + static NAN_METHOD(GetFrameAt); // release the stream diff --git a/src/VideoWriterWrap.cc b/src/VideoWriterWrap.cc new file mode 100755 index 0000000..dcc0e1d --- /dev/null +++ b/src/VideoWriterWrap.cc @@ -0,0 +1,152 @@ +#include "VideoWriterWrap.h" +#include "Matrix.h" +#include "OpenCV.h" + +#include + +Nan::Persistent VideoWriterWrap::constructor; + +struct videowriter_baton { + + Nan::Persistent cb; + VideoWriterWrap *vc; + Matrix *im; + + uv_work_t request; +}; + +void VideoWriterWrap::Init(Local target) { + Nan::HandleScope scope; + + //Class + Local ctor = Nan::New(VideoWriterWrap::New); + constructor.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(1); + ctor->SetClassName(Nan::New("VideoWriter").ToLocalChecked()); + + Nan::SetPrototypeMethod(ctor, "write", Write); + Nan::SetPrototypeMethod(ctor, "writeSync", WriteSync); + Nan::SetPrototypeMethod(ctor, "release", Release); + + target->Set(Nan::New("VideoWriter").ToLocalChecked(), ctor->GetFunction()); +} + +NAN_METHOD(VideoWriterWrap::New) { + Nan::HandleScope scope; + + if (info.This()->InternalFieldCount() == 0) + return Nan::ThrowTypeError("Cannot Instantiate without new"); + + VideoWriterWrap *v; + + + // Arg 0 is the output filename + std::string filename = std::string(*Nan::Utf8String(info[0]->ToString())); + + // Arg 1 is the fourcc code + const char *fourccStr = std::string(*Nan::Utf8String(info[1]->ToString())).c_str(); + int fourcc = CV_FOURCC(fourccStr[0],fourccStr[1],fourccStr[2],fourccStr[3]); + + // Arg 2 is the output fps + double fps = Nan::To(info[2]).FromJust(); + + // Arg 3 is the image size + cv::Size imageSize; + if (info[3]->IsArray()) { + Local v8sz = info[3]->ToObject(); + imageSize = cv::Size(v8sz->Get(1)->IntegerValue(), v8sz->Get(0)->IntegerValue()); + } else { + JSTHROW_TYPE("Must pass image size"); + } + + // Arg 4 is the color flag + bool isColor = info[4]->BooleanValue(); + v = new VideoWriterWrap(filename, fourcc, fps, imageSize, isColor); + + v->Wrap(info.This()); + + info.GetReturnValue().Set(info.This()); +} + +VideoWriterWrap::VideoWriterWrap(const std::string& filename, int fourcc, double fps, cv::Size frameSize, bool isColor) { + Nan::HandleScope scope; + writer.open(filename, fourcc, fps, frameSize, isColor); + + if(!writer.isOpened()) { + Nan::ThrowError("Writer could not be opened"); + } +} + +NAN_METHOD(VideoWriterWrap::Release) { + Nan::HandleScope scope; + VideoWriterWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + + v->writer.release(); + + return; +} + +class AsyncVWWorker: public Nan::AsyncWorker { +public: + AsyncVWWorker(Nan::Callback *callback, VideoWriterWrap *vw, Matrix *im) : + Nan::AsyncWorker(callback), + vw(vw), + im(im) { + } + + ~AsyncVWWorker() { + } + + // Executed inside the worker-thread. + // It is not safe to access V8, or V8 data structures + // here, so everything we need for input and output + // should go on `this`. + void Execute() { + this->vw->writer.write(im->mat); + } + + // Executed when the async work is complete + // this function will be run inside the main event loop + // so it is safe to use V8 again + void HandleOKCallback() { + Nan::HandleScope scope; + + Local argv[] = { + Nan::Null() + }; + + Nan::TryCatch try_catch; + callback->Call(1, argv); + if (try_catch.HasCaught()) { + Nan::FatalException(try_catch); + } + } + +private: + VideoWriterWrap *vw; + Matrix* im; +}; + +NAN_METHOD(VideoWriterWrap::Write) { + Nan::HandleScope scope; + VideoWriterWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + + Matrix *im = Nan::ObjectWrap::Unwrap < Matrix > (info[0]->ToObject()); + REQ_FUN_ARG(1, cb); + + Nan::Callback *callback = new Nan::Callback(cb.As()); + Nan::AsyncQueueWorker(new AsyncVWWorker(callback, v, im)); + + return; +} + +NAN_METHOD(VideoWriterWrap::WriteSync) { + Nan::HandleScope scope; + VideoWriterWrap *v = Nan::ObjectWrap::Unwrap(info.This()); + Matrix *im = Nan::ObjectWrap::Unwrap < Matrix > (info[0]->ToObject()); + + v->writer.write(im->mat); + + info.GetReturnValue().Set(Nan::Null()); +} + diff --git a/src/VideoWriterWrap.h b/src/VideoWriterWrap.h new file mode 100755 index 0000000..1023854 --- /dev/null +++ b/src/VideoWriterWrap.h @@ -0,0 +1,18 @@ +#include "OpenCV.h" + +class VideoWriterWrap: public Nan::ObjectWrap { +public: + cv::VideoWriter writer; + + static Nan::Persistent constructor; + static void Init(Local target); + static NAN_METHOD(New); + + VideoWriterWrap(const std::string& filename, int fourcc, double fps, cv::Size frameSize, bool isColor=true); + + static NAN_METHOD(Write); + static NAN_METHOD(WriteSync); + + // release the stream + static NAN_METHOD(Release); +}; diff --git a/src/init.cc b/src/init.cc index 10f4d76..ffbc286 100755 --- a/src/init.cc +++ b/src/init.cc @@ -4,6 +4,7 @@ #include "Matrix.h" #include "CascadeClassifierWrap.h" #include "VideoCaptureWrap.h" +#include "VideoWriterWrap.h" #include "Contours.h" #include "CamShift.h" #include "HighGUI.h" @@ -25,6 +26,7 @@ extern "C" void init(Local target) { Matrix::Init(target); CascadeClassifierWrap::Init(target); VideoCaptureWrap::Init(target); + VideoWriterWrap::Init(target); Contour::Init(target); TrackedObject::Init(target); NamedWindow::Init(target);