diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index 1a9c875a4b7afb6d03753bf3d3f5041a97d7811f..ba74f93068021582b365dd208acde1aaa03160b3 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -88,11 +88,10 @@ getSignalHandlers() exported_callback<DRing::VideoSignal::DecodingStarted>(), exported_callback<DRing::VideoSignal::DecodingStopped>(), #ifdef __ANDROID__ - exported_callback<DRing::VideoSignal::AcquireCamera>(), - exported_callback<DRing::VideoSignal::ReleaseCamera>(), - exported_callback<DRing::VideoSignal::GetCameraFormats>(), - exported_callback<DRing::VideoSignal::GetCameraSizes>(), - exported_callback<DRing::VideoSignal::GetCameraRates>(), + exported_callback<DRing::VideoSignal::GetCameraInfo>(), + exported_callback<DRing::VideoSignal::SetParameters>(), + exported_callback<DRing::VideoSignal::StartCapture>(), + exported_callback<DRing::VideoSignal::StopCapture>(), #endif #endif }; diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp index 009e8130d51e3bfc1e23b545f613e24ce01a3010..10f4b9b48e886807a967a76ff2154b483117482b 100644 --- a/src/client/videomanager.cpp +++ b/src/client/videomanager.cpp @@ -159,6 +159,22 @@ removeVideoDevice(const std::string &node) { videoManager.videoDeviceMonitor.removeDevice(node); } + +void* +obtainFrame(int length) +{ + if (auto input = videoManager.videoInput.lock()) + return (*input).obtainFrame(length); + + return nullptr; +} + +void +releaseFrame(void* frame) +{ + if (auto input = videoManager.videoInput.lock()) + (*input).releaseFrame(frame); +} #endif } // namespace DRing diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h index 13eef20cfd5e58b2025eab47cd2e34d91a7cd68a..1097b38f7605e83811fb1f3a9e4036791d22da24 100644 --- a/src/dring/videomanager_interface.h +++ b/src/dring/videomanager_interface.h @@ -73,6 +73,8 @@ void registerSinkTarget(const std::string& sinkId, const SinkTarget& target); #ifdef __ANDROID__ void addVideoDevice(const std::string &node); void removeVideoDevice(const std::string &node); +void* obtainFrame(int length); +void releaseFrame(void* frame); #endif // Video signal type definitions @@ -90,25 +92,21 @@ struct VideoSignal { using cb_type = void(const std::string& /*id*/, const std::string& /*shm_path*/, bool /*is_mixer*/); }; #ifdef __ANDROID__ - struct AcquireCamera { - constexpr static const char* name = "AcquireCamera"; - using cb_type = void(const std::string& device); - }; - struct ReleaseCamera { - constexpr static const char* name = "ReleaseCamera"; - using cb_type = void(const std::string& device); + struct GetCameraInfo { + constexpr static const char* name = "GetCameraInfo"; + using cb_type = void(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates); }; - struct GetCameraFormats { - constexpr static const char* name = "GetCameraFormats"; - using cb_type = void(const std::string& device, std::vector<int> *formats_); + struct SetParameters { + constexpr static const char* name = "SetParameters"; + using cb_type = void(const std::string& device, const int format, const int width, const int height, const int rate); }; - struct GetCameraSizes { - constexpr static const char* name = "GetCameraSizes"; - using cb_type = void(const std::string& device, int format, std::vector<std::string> *sizes); + struct StartCapture { + constexpr static const char* name = "StartCapture"; + using cb_type = void(const std::string& device); }; - struct GetCameraRates{ - constexpr static const char* name = "GetCameraRates"; - using cb_type = void(const std::string& device, const int format, const std::string& size, std::vector<float> *rates_); + struct StopCapture { + constexpr static const char* name = "StopCapture"; + using cb_type = void(void); }; #endif }; diff --git a/src/media/libav_utils.cpp b/src/media/libav_utils.cpp index 2dfd4efcec0b06fa7a8e93296b32ddb1bd53c252..da4f44a360274ca363905171c2b778ab95f6e829 100644 --- a/src/media/libav_utils.cpp +++ b/src/media/libav_utils.cpp @@ -116,6 +116,8 @@ int libav_pixel_format(int fmt) case video::VIDEO_PIXFMT_BGRA: return PIXEL_FORMAT(BGRA); case video::VIDEO_PIXFMT_RGBA: return PIXEL_FORMAT(RGBA); case video::VIDEO_PIXFMT_YUYV422: return PIXEL_FORMAT(YUYV422); + case video::VIDEO_PIXFMT_YUV420P: return PIXEL_FORMAT(YUV420P); + case video::VIDEO_PIXFMT_NV21: return PIXEL_FORMAT(NV21); } return fmt; } diff --git a/src/media/media_buffer.cpp b/src/media/media_buffer.cpp index c8a6c8cbc032936543b1f6d07ae621f8b8f7718b..0620fe3d8823fb8742230be8bf552d981aa45751 100644 --- a/src/media/media_buffer.cpp +++ b/src/media/media_buffer.cpp @@ -44,6 +44,12 @@ MediaFrame::reset() noexcept #ifdef RING_VIDEO +VideoFrame::~VideoFrame() +{ + if (releaseBufferCb_) + releaseBufferCb_(ptr_); +} + void VideoFrame::reset() noexcept { @@ -51,6 +57,7 @@ VideoFrame::reset() noexcept #if !USE_OLD_AVU allocated_ = false; #endif + releaseBufferCb_ = {}; } size_t @@ -107,10 +114,11 @@ VideoFrame::reserve(int format, int width, int height) if (av_frame_get_buffer(libav_frame, 32)) throw std::bad_alloc(); allocated_ = true; + releaseBufferCb_ = {}; } void -VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height) +VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height) noexcept { reset(); setGeometry(format, width, height); @@ -120,6 +128,17 @@ VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height) (AVPixelFormat)frame_->format, width, height); } +void +VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height, + std::function<void(void*)> cb) noexcept +{ + setFromMemory(ptr, format, width, height); + if (cb) { + releaseBufferCb_ = cb; + ptr_ = ptr; + } +} + void VideoFrame::noise() { diff --git a/src/media/media_buffer.h b/src/media/media_buffer.h index 106597cc00aadccf87c5d1e8942f1daf22bb9057..a59fb616bad33e2207e4bed59c9c876fb958be10 100644 --- a/src/media/media_buffer.h +++ b/src/media/media_buffer.h @@ -57,6 +57,7 @@ class VideoFrame: public MediaFrame { public: // Construct an empty VideoFrame VideoFrame() = default; + ~VideoFrame(); // Reset internal buffers (return to an empty VideoFrame) void reset() noexcept override; @@ -78,7 +79,8 @@ class VideoFrame: public MediaFrame { // Set internal pixel buffers on given memory buffer // This buffer must follow given specifications. - void setFromMemory(uint8_t* data, int format, int width, int height); + void setFromMemory(uint8_t* data, int format, int width, int height) noexcept; + void setFromMemory(uint8_t* data, int format, int width, int height, std::function<void(void*)> cb) noexcept; void noise(); @@ -86,6 +88,8 @@ class VideoFrame: public MediaFrame { VideoFrame& operator =(const VideoFrame& src); private: + std::function<void(void*)> releaseBufferCb_ {}; + void *ptr_ {nullptr}; bool allocated_ {false}; void setGeometry(int format, int width, int height) noexcept; }; diff --git a/src/media/video/androidvideo/video_device_impl.cpp b/src/media/video/androidvideo/video_device_impl.cpp index b41f822385a4531ad20f59b552c3afb0c6bd06a7..789526dcf6a957131f3d5b00000d177408b44514 100644 --- a/src/media/video/androidvideo/video_device_impl.cpp +++ b/src/media/video/androidvideo/video_device_impl.cpp @@ -33,19 +33,19 @@ namespace ring { namespace video { -class VideoAndroidSize { - public: - VideoAndroidSize(std::string size, std::string camera_id, int format); - - std::string str() const; - float getRate(std::string) const; - float getRate(unsigned) const; - std::vector<std::string> getRateList() const; +/* + * Array to match Android formats. List formats in ascending + * preferrence: the format with the lower index will be picked. + */ +struct android_fmt { + int code; + std::string name; + enum VideoPixelFormat ring_format; +}; - unsigned width; - unsigned height; - private: - std::vector<float> rates_; +static const struct android_fmt and_formats[] { + { 17, "NV21", VIDEO_PIXFMT_NV21 }, + { 842094169, "YUV420", VIDEO_PIXFMT_YUV420P }, }; class VideoDeviceImpl { @@ -55,193 +55,175 @@ class VideoDeviceImpl { */ VideoDeviceImpl(const std::string& path); - std::string camera_id; std::string name; - const VideoAndroidSize& getSize(const std::string size) const; - - VideoSettings getSettings() const; - void applySettings(VideoSettings settings); - DeviceParams getDeviceParams() const; - std::vector<std::string> getSizeList() const; - private: - int format_; - std::string size_; - unsigned rate_; - std::vector<VideoAndroidSize> sizes_; -}; + void setDeviceParams(const DeviceParams&); -/* VideoAndroidSize */ -VideoAndroidSize::VideoAndroidSize(std::string size, std::string camera_id, int format) -{ - sscanf(size.c_str(), "%ux%u", &width, &height); + std::vector<VideoSize> getSizeList() const; + std::vector<FrameRate> getRateList() const; + private: + void selectFormat(); + VideoSize getSize(VideoSize size) const; + FrameRate getRate(FrameRate rate) const; - emitSignal<DRing::VideoSignal::GetCameraRates>(camera_id, format, size, &rates_); -} + std::vector<int> formats_ {}; + std::vector<VideoSize> sizes_ {}; + std::vector<FrameRate> rates_ {}; -std::string -VideoAndroidSize::str() const -{ - std::stringstream ss; - ss << width << "x" << height; - return ss.str(); -} + const struct android_fmt *fmt_ {nullptr}; + VideoSize size_ {}; + FrameRate rate_ {}; +}; -float -VideoAndroidSize::getRate(unsigned rate) const -{ - for(const auto &r : rates_) { - if (r == rate) - return r; +void +VideoDeviceImpl::selectFormat() +{ + /* + * formats_ contains camera parameters as returned by the GetCameraInfo + * signal, find the matching V4L2 formats + */ + unsigned int current, best = UINT_MAX; + for(const auto &fmt : formats_) { + const struct android_fmt *and_fmt; + for(and_fmt = std::begin(and_formats); + and_formats < std::end(and_formats); + and_fmt++) { + if (fmt == and_fmt->code) { + current = and_fmt - std::begin(and_formats); + break; + } + } + + /* No match found, we should add it */ + if (and_fmt == std::end(and_formats)) { + RING_WARN("AndroidVideo: No format matching %d", fmt); + continue; + } + + if (current < best) + best = current; } - assert(not rates_.empty()); - return rates_.back(); -} - -float -VideoAndroidSize::getRate(std::string rate) const -{ - unsigned r; - std::stringstream ss; - ss << rate; - ss >> r; - - return getRate(r); -} - -std::vector<std::string> -VideoAndroidSize::getRateList() const -{ - std::vector<std::string> v; - - for(const auto &rate : rates_) { - std::stringstream ss; - ss << rate; - v.push_back(ss.str()); + if (best != UINT_MAX) { + fmt_ = &and_formats[best]; + RING_DBG("AndroidVideo: picked format %s", fmt_->name.c_str()); + } + else { + fmt_ = &and_formats[0]; + RING_ERR("AndroidVideo: Could not find a known format to use"); } - - return v; } -/* VideoDeviceImpl */ -VideoDeviceImpl::VideoDeviceImpl(const std::string& path) : - camera_id(path), name(path), format_(), rate_() +VideoDeviceImpl::VideoDeviceImpl(const std::string& path) : name(path) { - emitSignal<DRing::VideoSignal::AcquireCamera>(camera_id); - RING_DBG("### Acquired Camera %s", camera_id.c_str()); - - std::vector<int> formats; - emitSignal<DRing::VideoSignal::GetCameraFormats>(camera_id, &formats); - - assert(not formats.empty()); - format_ = formats[0]; /* FIXME: select a real value */ + std::vector<unsigned> sizes; + std::vector<unsigned> rates; + emitSignal<DRing::VideoSignal::GetCameraInfo>(name, &formats_, &sizes, &rates); + for (size_t i=0, n=sizes.size(); i<n; i+=2) + sizes_.emplace_back(sizes[i], sizes[i+1]); + for (const auto& r : rates) + rates_.emplace_back(r, 1000); - std::vector<std::string> sizes; - emitSignal<DRing::VideoSignal::GetCameraSizes>(camera_id, format_, &sizes); - for(const auto &iter : sizes) { - sizes_.push_back(VideoAndroidSize(iter, camera_id, format_)); - } - - emitSignal<DRing::VideoSignal::ReleaseCamera>(camera_id); - - // Set default settings - applySettings(VideoSettings()); + selectFormat(); } -const VideoAndroidSize& -VideoDeviceImpl::getSize(const std::string size) const +VideoSize +VideoDeviceImpl::getSize(VideoSize size) const { for (const auto &iter : sizes_) { - if (iter.str() == size) + if (iter == size) return iter; } - assert(not sizes_.empty()); - return sizes_.back(); + return sizes_.empty() ? VideoSize{0, 0} : sizes_.back(); } -std::vector<std::string> -VideoDeviceImpl::getSizeList() const +FrameRate +VideoDeviceImpl::getRate(FrameRate rate) const { - std::vector<std::string> v; - - for(const auto &iter : sizes_) - v.push_back(iter.str()); + for (const auto &iter : rates_) { + if (iter == rate) + return iter; + } - return v; + return rates_.empty() ? FrameRate{0, 0} : rates_.back(); } -void -VideoDeviceImpl::applySettings(VideoSettings settings) +std::vector<VideoSize> +VideoDeviceImpl::getSizeList() const { - const VideoAndroidSize &s = getSize(settings.video_size); - size_ = s.str(); - rate_ = s.getRate(settings.framerate); + return sizes_; } -VideoSettings -VideoDeviceImpl::getSettings() const +std::vector<FrameRate> +VideoDeviceImpl::getRateList() const { - VideoSettings settings; - settings.name = name; - settings.channel = "default"; - settings.video_size = size_; - settings.framerate = rate_; - return settings; + return rates_; } DeviceParams VideoDeviceImpl::getDeviceParams() const { - const VideoAndroidSize &s = getSize(size_); - DeviceParams params; - params.input = camera_id; - params.format = "android"; + std::stringstream ss1, ss2; + char sep; + + ss1 << fmt_->ring_format; + ss1 >> params.format; + + params.name = name; + params.input = name; params.channel = 0; - params.width = s.width; - params.height = s.height; + params.width = size_.first; + params.height = size_.second; params.framerate = rate_; + return params; } +void +VideoDeviceImpl::setDeviceParams(const DeviceParams& params) +{ + size_ = getSize({params.width, params.height}); + rate_ = getRate(params.framerate); + emitSignal<DRing::VideoSignal::SetParameters>(name, fmt_->code, size_.first, size_.second, rate_.real()); +} + VideoDevice::VideoDevice(const std::string& path) : deviceImpl_(new VideoDeviceImpl(path)) { name = deviceImpl_->name; } -void -VideoDevice::applySettings(VideoSettings settings) +DeviceParams +VideoDevice::getDeviceParams() const { - deviceImpl_->applySettings(settings); + return deviceImpl_->getDeviceParams(); } -VideoSettings -VideoDevice::getSettings() const +void +VideoDevice::setDeviceParams(const DeviceParams& params) { - return deviceImpl_->getSettings(); + return deviceImpl_->setDeviceParams(params); } -DeviceParams -VideoDevice::getDeviceParams() const +std::vector<std::string> +VideoDevice::getChannelList() const { - return deviceImpl_->getDeviceParams(); + return {"default"}; } -DRing::VideoCapabilities -VideoDevice::getCapabilities() const +std::vector<VideoSize> +VideoDevice::getSizeList(const std::string& channel) const { - DRing::VideoCapabilities cap; - - for (const auto &iter : deviceImpl_->getSizeList()) { - const auto &s = deviceImpl_->getSize(iter); - cap["default"][s.str()] = s.getRateList(); - } + return deviceImpl_->getSizeList(); +} - return cap; +std::vector<FrameRate> +VideoDevice::getRateList(const std::string& channel, VideoSize size) const +{ + return deviceImpl_->getRateList(); } VideoDevice::~VideoDevice() diff --git a/src/media/video/video_base.h b/src/media/video/video_base.h index ff1a776e5a02914af596858b112e15633f654284..6b39000e543aac318976002bffc4164ec10240d3 100644 --- a/src/media/video/video_base.h +++ b/src/media/video/video_base.h @@ -50,8 +50,10 @@ namespace ring { namespace video { enum VideoPixelFormat { VIDEO_PIXFMT_BGRA = -1, - VIDEO_PIXFMT_YUYV422 = -2, - VIDEO_PIXFMT_RGBA = -3, + VIDEO_PIXFMT_YUV420P = -2, + VIDEO_PIXFMT_YUYV422 = -3, + VIDEO_PIXFMT_RGBA = -4, + VIDEO_PIXFMT_NV21 = -5, }; template <typename T> class Observer; diff --git a/src/media/video/video_device.h b/src/media/video/video_device.h index 2216112133015c17f897c1d1f19e996d9afef491..125538adfd42f5046218b14097f13e51e17afe0f 100644 --- a/src/media/video/video_device.h +++ b/src/media/video/video_device.h @@ -190,7 +190,7 @@ private: FrameRate closest {0}; double rate_val = 0; try { - rate_val = rate.empty() ? 0 : std::stod(rate); + rate_val = rate.empty() ? 0 : ring::stod(rate); } catch (...) { RING_WARN("Can't read framerate \"%s\"", rate.c_str()); } diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index 69f3629a54463224fe632862d9426d238ee11423..ed702846448d88bf43093ec8b5ff5d9bdf957ca7 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -29,8 +29,12 @@ #include "media_const.h" #include "manager.h" #include "client/videomanager.h" +#include "client/ring_signal.h" #include "sinkclient.h" #include "logger.h" +#include "media/media_buffer.h" + +#include <libavformat/avio.h> #include <string> #include <sstream> @@ -45,16 +49,94 @@ static constexpr unsigned default_grab_height = 480; VideoInput::VideoInput() : VideoGenerator::VideoGenerator() , sink_ {Manager::instance().createSinkClient("local")} +#ifndef __ANDROID__ , loop_(std::bind(&VideoInput::setup, this), std::bind(&VideoInput::process, this), std::bind(&VideoInput::cleanup, this)) +#else + , loop_(std::bind(&VideoInput::setup, this), + std::bind(&VideoInput::processAndroid, this), + std::bind(&VideoInput::cleanupAndroid, this)) + , mutex_(), frame_cv_(), buffers_(8) +#endif {} VideoInput::~VideoInput() { +#ifdef __ANDROID__ + /* we need to stop the loop and notify the condition variable + * to unblock the process loop */ + loop_.stop(); + frame_cv_.notify_one(); +#endif loop_.join(); } +#ifdef __ANDROID__ +bool VideoInput::waitForBufferFull() +{ + for(auto& buffer : buffers_) { + if (buffer.status == BUFFER_FULL) + return true; + } + + /* If the loop is stopped, returned true so we can quit the process loop */ + return !isCapturing(); +} + +void VideoInput::processAndroid() +{ + foundDecOpts(decOpts_); + + if (switchPending_.exchange(false)) { + RING_DBG("Switching input to '%s'", decOpts_.input.c_str()); + if (decOpts_.input.empty()) { + loop_.stop(); + return; + } + + emitSignal<DRing::VideoSignal::StopCapture>(); + emitSignal<DRing::VideoSignal::StartCapture>(decOpts_.input); + } + + std::unique_lock<std::mutex> lck(mutex_); + + frame_cv_.wait(lck, [this] { return waitForBufferFull(); }); + for (auto& buffer : buffers_) { + if (buffer.status == BUFFER_FULL && buffer.index == publish_index_) { + auto& frame = getNewFrame(); + int format = getPixelFormat(); + + buffer.status = BUFFER_PUBLISHED; + frame.setFromMemory((uint8_t*)buffer.data, format, decOpts_.width, decOpts_.height, + std::bind(&VideoInput::releaseBufferCb, this)); + publish_index_++; + lck.unlock(); + publishFrame(); + break; + } + } +} + +void VideoInput::cleanupAndroid() +{ + emitSignal<DRing::VideoSignal::StopCapture>(); + + if (detach(sink_.get())) + sink_->stop(); + + std::lock_guard<std::mutex> lck(mutex_); + for (auto& buffer : buffers_) { + if (buffer.status == BUFFER_AVAILABLE || + buffer.status == BUFFER_FULL) { + freeOneBuffer(buffer); + } else if (buffer.status != BUFFER_NOT_ALLOCATED) { + RING_ERR("Failed to free buffer [%p]", buffer.data); + } + } +} +#endif + bool VideoInput::setup() { if (not attach(sink_.get())) { @@ -134,6 +216,92 @@ bool VideoInput::captureFrame() } } +#ifdef __ANDROID__ +int VideoInput::allocateOneBuffer(struct VideoFrameBuffer& b, int length) +{ + b.data = std::malloc(length); + if (b.data) { + b.status = BUFFER_AVAILABLE; + b.length = length; + RING_DBG("Allocated buffer [%p]", b.data); + return 0; + } + + RING_DBG("Failed to allocate memory for one buffer"); + return -ENOMEM; +} + +void VideoInput::freeOneBuffer(struct VideoFrameBuffer& b) +{ + RING_DBG("Free buffer [%p]", b.data); + std::free(b.data); + b.data = nullptr; + b.length = 0; + b.status = BUFFER_NOT_ALLOCATED; +} + +void VideoInput::releaseBufferCb(void *ptr) +{ + std::lock_guard<std::mutex> lck(mutex_); + + for(auto &buffer : buffers_) { + if (buffer.data == ptr) { + buffer.status = BUFFER_AVAILABLE; + if (!isCapturing()) + freeOneBuffer(buffer); + break; + } + } +} + +void* +VideoInput::obtainFrame(int length) +{ + std::lock_guard<std::mutex> lck(mutex_); + + /* allocate buffers. This is done there because it's only when the Android + * application requests a buffer that we know its size + */ + for(auto& buffer : buffers_) { + if (buffer.status == BUFFER_NOT_ALLOCATED) { + allocateOneBuffer(buffer, length); + } + } + + /* search for an available frame */ + for(auto& buffer : buffers_) { + if (buffer.length == length && buffer.status == BUFFER_AVAILABLE) { + buffer.status = BUFFER_CAPTURING; + return buffer.data; + } + } + + RING_WARN("No buffer found"); + return nullptr; +} + +void +VideoInput::releaseFrame(void *ptr) +{ + std::lock_guard<std::mutex> lck(mutex_); + for(auto& buffer : buffers_) { + if (buffer.data == ptr) { + if (buffer.status != BUFFER_CAPTURING) + RING_ERR("Released a buffer with status %d, expected %d", + buffer.status, BUFFER_CAPTURING); + if (isCapturing()) { + buffer.status = BUFFER_FULL; + buffer.index = capture_index_++; + frame_cv_.notify_one(); + } else { + freeOneBuffer(buffer); + } + break; + } + } +} +#endif + void VideoInput::createDecoder() { @@ -360,6 +528,23 @@ VideoInput::switchInput(const std::string& resource) return futureDecOpts_; } +#ifdef __ANDROID__ +int VideoInput::getWidth() const +{ return decOpts_.width; } + +int VideoInput::getHeight() const +{ return decOpts_.height; } + +int VideoInput::getPixelFormat() const +{ + int format; + std::stringstream ss; + ss << decOpts_.format; + ss >> format; + + return format; +} +#else int VideoInput::getWidth() const { return decoder_->getWidth(); } @@ -368,6 +553,7 @@ int VideoInput::getHeight() const int VideoInput::getPixelFormat() const { return decoder_->getPixelFormat(); } +#endif DeviceParams VideoInput::getParams() const { return decOpts_; } diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index 740b5c7f8864f04ddd4d67f4b3340fbfc322afd9..84692b9fe90c09e7690346ed48e1dd46a6810997 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -32,6 +32,8 @@ #include <atomic> #include <future> #include <string> +#include <mutex> +#include <condition_variable> namespace ring { class MediaDecoder; @@ -41,6 +43,24 @@ namespace ring { namespace video { class SinkClient; +enum VideoFrameStatus { + BUFFER_NOT_ALLOCATED, + BUFFER_AVAILABLE, /* owned by us */ + BUFFER_CAPTURING, /* owned by Android Java Application */ + BUFFER_FULL, /* owned by us again */ + BUFFER_PUBLISHED, /* owned by libav */ +}; + +struct VideoFrameBuffer { + void *data; + size_t length; + enum VideoFrameStatus status; + int index; + + VideoFrameBuffer() : data(nullptr), length(0), + status(BUFFER_NOT_ALLOCATED), index(0) {} +}; + class VideoInput : public VideoGenerator { public: @@ -54,6 +74,14 @@ public: DeviceParams getParams() const; std::shared_future<DeviceParams> switchInput(const std::string& resource); +#ifdef __ANDROID__ + /* + * these fonctions are used to pass buffer from/to the daemon + * to the Java application + */ + void* obtainFrame(int length); + void releaseFrame(void *frame); +#endif private: NON_COPYABLE(VideoInput); @@ -91,6 +119,23 @@ private: bool captureFrame(); bool isCapturing() const noexcept; + +#ifdef __ANDROID__ + void processAndroid(); + void cleanupAndroid(); + int allocateOneBuffer(struct VideoFrameBuffer& b, int length); + void freeOneBuffer(struct VideoFrameBuffer& b); + bool waitForBufferFull(); + + std::mutex mutex_; + std::condition_variable frame_cv_; + int capture_index_ = 0; + int publish_index_ = 0; + + /* Get notified when libav is done with this buffer */ + static void releaseBufferCb(void *opaque, void *ptr); + std::vector<struct VideoFrameBuffer> buffers_; +#endif }; }} // namespace ring::video diff --git a/src/string_utils.h b/src/string_utils.h index 4072bb10fdf306ee850f2baf0efd83786f7398de..fbd677816bd80783ee226a694e950b17d156ccba 100644 --- a/src/string_utils.h +++ b/src/string_utils.h @@ -67,6 +67,15 @@ stoi(const std::string& str) return v; } +static inline double +stod(const std::string& str) +{ + double v; + std::istringstream os(str); + os >> v; + return v; +} + #else template <typename T> @@ -82,6 +91,12 @@ stoi(const std::string& str) return std::stoi(str); } +static inline double +stod(const std::string& str) +{ + return std::stod(str); +} + #endif std::string trim(const std::string &s);