diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 3f67706..a0b15a1 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -28,6 +28,8 @@ jobs: with: branch: jse - run: swig -version + - run: apt-get update + - run: apt-get install -y SDL2-devel ffmpeg-devel OpenH264 libyuv-devel libv4l2 # build x86_64 version, test, pack, package and native module - run: npm i --ignore-scripts # see https://github.com/bchr02/node-pre-gyp-github/pull/46 @@ -98,6 +100,7 @@ jobs: registry-url: https://npm.pkg.github.com/ - run: apt-get update - run: apt-get install -y libpcre2-dev autotools-dev autoconf libtool-bin g++ make bison uuid-dev + - run: apt-get install -y SDL2-devel ffmpeg-devel OpenH264 libyuv-devel libv4l2 - run: npm i -g node-gyp node-addon-api - uses: mmomtchev/setup-swig@v3 with: diff --git a/binding.gyp b/binding.gyp index 148a73d..c8b11d8 100644 --- a/binding.gyp +++ b/binding.gyp @@ -59,6 +59,13 @@ "-lg7221codec", "-lyuv", "-lwebrtc", + "-lavdevice", + "-lavformat", + "-lavcodec", + "-lswscale", + "-lavutil", + "-lopenh264", + "-lSDL2", ], 'conditions': [ [ 'arch=="aarch64" or arch=="arm64"', @@ -92,7 +99,10 @@ "-framework CoreMedia", "-framework Metal", "-framework MetalKit", - "-framework VideoToolbox" + "-framework VideoToolbox", + "-L/opt/homebrew/Cellar/ffmpeg/> pjlib/include/pj/config_site.h -./configure --prefix=$PREFIX CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" +echo "#define PJMEDIA_HAS_VIDEO 1" >> pjlib/include/pj/config_site.h +./configure --prefix=$PREFIX CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" $CONFIGUREFLAGS make -j 4 make install diff --git a/src/callback.hpp b/src/callback.hpp index a6d4095..a963ab9 100644 --- a/src/callback.hpp +++ b/src/callback.hpp @@ -1,6 +1,7 @@ #ifndef __CALLBACK_HPP__ #define __CALLBACK_HPP__ +#include "videomediaport.hpp" #include "pjsua2.hpp" using namespace pj; @@ -167,6 +168,35 @@ class AudioMediaPortCB: public AudioMediaPort { } CB_IMPLTYPESPECRET(FrameRequested, MediaFrame) CB_IMPLTYPESPEC(FrameReceived, MediaFrame) + + // std::function onFrameReceivedCBFn; + // void setFrameReceivedCB(std::function fn) { onFrameReceivedArrayBufferCBFn = fn; } \ + // virtual void onFrameReceived(MediaFrame &prm) { + // if (onFrameReceivedCBFn) { + // onFrameReceivedCBFn(prm); + // } + // } +}; + +class VideoMediaPortCB: public VideoMediaPort { + public: + void destroy() { + DESTRUCT(FrameRequested) + DESTRUCT(FrameReceived) + } + ~VideoMediaPortCB() { + destroy(); + } + CB_IMPLTYPESPECRET(FrameRequested, MediaFrame) + CB_IMPLTYPESPEC(FrameReceived, MediaFrame) + + // std::function onFrameReceivedCBFn; + // void setFrameReceivedCB(std::function fn) { onFrameReceivedArrayBufferCBFn = fn; } \ + // virtual void onFrameReceived(MediaFrame &prm) { + // if (onFrameReceivedCBFn) { + // onFrameReceivedCBFn(prm); + // } + // } }; class AudioMediaPlayerCB: public AudioMediaPlayer { @@ -193,6 +223,4 @@ class BuddyCB: public Buddy { CB_IMPL(BuddyEvSubState) // OnBuddyEvSubStateParam }; - - #endif \ No newline at end of file diff --git a/src/callback.i b/src/callback.i index a5c71d9..a729ab3 100644 --- a/src/callback.i +++ b/src/callback.i @@ -153,6 +153,7 @@ CB_IGNORE_PARENT(Buddy, BuddyEvSubState) %include "../build/pjproject/pjsip-apps/src/swig/pjsua2.i" %{ +#include "videomediaport.hpp" #include "callback.hpp" #define ASYNC_CALLBACK_SUPPORT @@ -351,6 +352,13 @@ CB_TYPEMAP_RET(MediaFrame, MediaFrame) CB_MANAGE_INNER(AudioMediaPort, FrameRequested, MediaFrame, MediaFrame&, prm: MediaFrame) CB_MANAGE_INNER(AudioMediaPort, FrameReceived, void, MediaFrame&, prm: MediaFrame) +// VideoMediaPortCB + +// CB_TYPEMAP(MediaFrame) +// CB_TYPEMAP_RET(MediaFrame, MediaFrame) +CB_MANAGE_INNER(VideoMediaPort, FrameRequested, MediaFrame, MediaFrame&, prm: MediaFrame) +CB_MANAGE_INNER(VideoMediaPort, FrameReceived, void, MediaFrame&, prm: MediaFrame) + // AudioMediaPlayerCB CB_TYPEMAP_VOID @@ -364,5 +372,6 @@ CB_TYPEMAP(CB_PTYPE(BuddyEvSubState)) CB_MANAGE_VOID(Buddy, BuddyState) CB_MANAGE(Buddy, BuddyEvSubState) +%include "videomediaport.hpp" %include "callback.hpp" diff --git a/src/videomediaport.hpp b/src/videomediaport.hpp new file mode 100644 index 0000000..0c49a59 --- /dev/null +++ b/src/videomediaport.hpp @@ -0,0 +1,136 @@ +#ifndef __VIDEOMEDIAPORT_HPP__ +#define __VIDEOMEDIAPORT_HPP__ + +// missing VideoMediaPort implementation + +#include +#include +#include + +using namespace pj; +using namespace std; + +#include + +#define THIS_FILE "videomediaport.hpp" + + +namespace pj +{ + class VideoMediaPort: public VideoMedia { + + public: + + VideoMediaPort() + : pool(NULL) + { + pj_bzero(&port, sizeof(port)); + } + + virtual ~VideoMediaPort() + { + if (pool) { + PJSUA2_CATCH_IGNORE( unregisterMediaPort() ); + pj_pool_release(pool); + pool = NULL; + } + } + + static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) + { + VideoMediaPort *mport = (VideoMediaPort *) port->port_data.pdata; + MediaFrame frame_; + + frame_.size = frame->size; + mport->onFrameRequested(frame_); + frame->type = frame_.type; + frame->size = PJ_MIN(frame_.buf.size(), frame_.size); + + #if ((defined(_MSVC_LANG) && _MSVC_LANG <= 199711L) || __cplusplus <= 199711L) + /* C++98 does not have Vector::data() */ + if (frame->size > 0) + pj_memcpy(frame->buf, &frame_.buf[0], frame->size); + #else + /* Newer than C++98 */ + pj_memcpy(frame->buf, frame_.buf.data(), frame->size); + #endif + + + return PJ_SUCCESS; + } + + static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) + { + VideoMediaPort *mport = (VideoMediaPort *) port->port_data.pdata; + MediaFrame frame_; + + frame_.type = frame->type; + frame_.buf.assign((char *)frame->buf, ((char *)frame->buf) + frame->size); + frame_.size = frame->size; + mport->onFrameReceived(frame_); + + return PJ_SUCCESS; + } + + void createPort(const string &name, MediaFormatVideo &fmt) + PJSUA2_THROW(Error) + { + pj_str_t name_; + pjmedia_format fmt_; + + if (pool) { + PJSUA2_RAISE_ERROR(PJ_EEXISTS); + } + + pool = pjsua_pool_create( "vmport%p", 512, 512); + if (!pool) { + PJSUA2_RAISE_ERROR(PJ_ENOMEM); + } + + /* Init port. */ + pj_bzero(&port, sizeof(port)); + pj_strdup2_with_null(pool, &name_, name.c_str()); + fmt_ = fmt.toPj(); + pjmedia_port_info_init2(&port.info, &name_, + PJMEDIA_SIG_CLASS_APP ('V', 'M', 'P'), + PJMEDIA_DIR_ENCODING_DECODING, &fmt_); + + port.port_data.pdata = this; + port.put_frame = &put_frame; + port.get_frame = &get_frame; + + registerMediaPort(&port, pool); + } + + /* + * Callbacks + */ + /** + * This callback is called to request a frame from this port. On input, + * frame.size indicates the capacity of the frame buffer and frame.buf + * will initially be an empty vector. Application can then set the frame + * type and fill the vector. + * + * @param frame The frame. + */ + virtual void onFrameRequested(MediaFrame &frame) + { PJ_UNUSED_ARG(frame); } + + /** + * This callback is called when this port receives a frame. The frame + * content will be provided in frame.buf vector, and the frame size + * can be found in either frame.size or the vector's size (both + * have the same value). + * + * @param frame The frame. + */ + virtual void onFrameReceived(MediaFrame &frame) + { PJ_UNUSED_ARG(frame); } + + private: + pj_pool_t *pool; + pjmedia_port port; + }; +} + +#endif \ No newline at end of file