<ClInclude Include="io\ProtocolStrategy.h" />\r
<ClInclude Include="io\SocketInfo.h" />\r
<ClInclude Include="log\log.h" />\r
+ <ClInclude Include="memory\page_locked_allocator.h" />\r
<ClInclude Include="stdafx.h" />\r
<ClInclude Include="utility\safe_ptr.h" />\r
<ClInclude Include="utility\string_convert.h" />\r
<Filter Include="Afx">\r
<UniqueIdentifier>{9250809a-3410-418d-95ca-ff2dcb6f0578}</UniqueIdentifier>\r
</Filter>\r
+ <Filter Include="Source\memory">\r
+ <UniqueIdentifier>{9259676d-c225-4422-a50e-30d152d78dc2}</UniqueIdentifier>\r
+ </Filter>\r
</ItemGroup>\r
<ItemGroup>\r
<ClCompile Include="io\SocketInfo.cpp">\r
<ClInclude Include="gl\gl_check.h">\r
<Filter>Source\gl</Filter>\r
</ClInclude>\r
+ <ClInclude Include="memory\page_locked_allocator.h">\r
+ <Filter>Source\memory</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
</Project>
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <unordered_map>\r
+#include <tbb/mutex.h>\r
+\r
+namespace caspar\r
+{\r
+ \r
+template <class T>\r
+class page_locked_allocator\r
+{\r
+public:\r
+ typedef size_t size_type;\r
+ typedef ptrdiff_t difference_type;\r
+ typedef T* pointer;\r
+ typedef const T* const_pointer;\r
+ typedef T& reference;\r
+ typedef const T& const_reference;\r
+ typedef T value_type;\r
+\r
+ page_locked_allocator() {}\r
+ page_locked_allocator(const page_locked_allocator&) {}\r
+ \r
+ pointer allocate(size_type n, const void * = 0) \r
+ {\r
+ tbb::mutex::scoped_lock lock(get().mutex);\r
+\r
+ size_type size = n * sizeof(T); \r
+ if(get().free < size)\r
+ allocate_store(size);\r
+\r
+ auto p = ::VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);\r
+ if(!p)\r
+ throw std::bad_alloc();\r
+\r
+ if(::VirtualLock(p, size) == 0) \r
+ {\r
+ ::VirtualFree(p, 0, MEM_RELEASE);\r
+ throw std::bad_alloc();\r
+ }\r
+ \r
+ get().free -= size;\r
+ get().map[p] = size;\r
+ return reinterpret_cast<T*>(p);\r
+ }\r
+ \r
+ void deallocate(void* p, size_type) \r
+ {\r
+ tbb::mutex::scoped_lock lock(get().mutex);\r
+\r
+ if(!p || get().map.find(p) == get().map.end())\r
+ return;\r
+\r
+ try\r
+ {\r
+ ::VirtualFree(p, 0, MEM_RELEASE);\r
+ get().free += get().map[p];\r
+ get().map.erase(p);\r
+ }\r
+ catch(...){} \r
+ }\r
+\r
+ pointer address(reference x) const { return &x; }\r
+ const_pointer address(const_reference x) const { return &x; }\r
+ page_locked_allocator<T>& operator=(const page_locked_allocator&) { return *this; }\r
+ void construct(pointer p, const T& val) { new ((T*) p) T(val); }\r
+ void destroy(pointer p) { p->~T(); }\r
+\r
+ size_type max_size() const { return size_t(-1); }\r
+\r
+ template <class U>\r
+ struct rebind { typedef page_locked_allocator<U> other; };\r
+\r
+ template <class U>\r
+ page_locked_allocator(const page_locked_allocator<U>&) {}\r
+\r
+ template <class U>\r
+ page_locked_allocator& operator=(const page_locked_allocator<U>&) { return *this; }\r
+\r
+private:\r
+\r
+ void allocate_store(size_type size)\r
+ { \r
+ SIZE_T workingSetMinSize = 0, workingSetMaxSize = 0;\r
+ if(::GetProcessWorkingSetSize(::GetCurrentProcess(), &workingSetMinSize, &workingSetMaxSize))\r
+ { \r
+ workingSetMinSize += size;\r
+ workingSetMaxSize += size;\r
+\r
+ if(!::SetProcessWorkingSetSize(::GetCurrentProcess(), workingSetMinSize, workingSetMaxSize)) \r
+ throw std::bad_alloc(); \r
+\r
+ get().free += size;\r
+ }\r
+ }\r
+\r
+ struct impl\r
+ {\r
+ impl() : free(0){}\r
+ std::unordered_map<void*, size_type> map;\r
+ size_type free;\r
+ tbb::mutex mutex;\r
+ };\r
+\r
+ static impl& get()\r
+ {\r
+ static impl i;\r
+ return i;\r
+ }\r
+};\r
+}
\ No newline at end of file
EVideoMode vid_fmt_; \r
unsigned long res_fmt_; \r
unsigned long engine_mode_;\r
+ \r
+ std::array<blue_dma_buffer_ptr, 3> reserved_frames_; \r
\r
- std::shared_ptr<const read_frame> transferring_frame_;\r
-\r
- std::array<page_locked_buffer_ptr, 3> hanc_buffers_;\r
- int current_id_;\r
bool embed_audio_;\r
\r
public:\r
, vid_fmt_(VID_FMT_INVALID) \r
, res_fmt_(RES_FMT_NORMAL) \r
, engine_mode_(VIDEO_ENGINE_FRAMESTORE) \r
- , current_id_(0)\r
, embed_audio_(embed_audio)\r
{\r
\r
\r
enable_video_output();\r
\r
- page_locked_buffer::reserve_working_size(MAX_HANC_BUFFER_SIZE * hanc_buffers_.size()); \r
- for(size_t n = 0; n < hanc_buffers_.size(); ++n)\r
- hanc_buffers_[n] = std::make_shared<page_locked_buffer>(MAX_HANC_BUFFER_SIZE);\r
+ for(size_t n = 0; n < reserved_frames_.size(); ++n)\r
+ reserved_frames_[n] = std::make_shared<blue_dma_buffer>(format_desc_.size, n); \r
\r
executor_.start();\r
\r
\r
~implementation()\r
{\r
- page_locked_buffer::unreserve_working_size(MAX_HANC_BUFFER_SIZE * hanc_buffers_.size()); \r
disable_video_output();\r
\r
if(sdk_)\r
\r
void send(const safe_ptr<const read_frame>& frame)\r
{ \r
- static size_t audio_samples = static_cast<size_t>(48000.0 / format_desc_.fps);\r
- static size_t audio_nchannels = 2;\r
- static std::vector<short> silence(audio_samples*audio_nchannels*2, 0);\r
+ static std::vector<short> silence(MAX_HANC_BUFFER_SIZE, 0);\r
+ \r
+ size_t audio_samples = static_cast<size_t>(48000.0 / format_desc_.fps);\r
+ size_t audio_nchannels = 2;\r
\r
active_.get();\r
active_ = executor_.begin_invoke([=]\r
{\r
try\r
{\r
- auto hanc = hanc_buffers_.front(); \r
- std::rotate(hanc_buffers_.begin(), hanc_buffers_.begin() + 1, hanc_buffers_.end());\r
+ std::copy_n(frame->image_data().begin(), frame->image_data().size(), reserved_frames_.front()->image_data());\r
\r
- unsigned long fieldCount = 0;\r
- sdk_->wait_output_video_synch(UPD_FMT_FRAME, fieldCount);\r
+ unsigned long n_field = 0;\r
+ sdk_->wait_output_video_synch(UPD_FMT_FRAME, n_field);\r
\r
if(embed_audio_)\r
{ \r
auto frame_audio_data = frame->audio_data().empty() ? silence.data() : const_cast<short*>(frame->audio_data().begin());\r
\r
- encode_hanc(reinterpret_cast<BLUE_UINT32*>(hanc->data()), frame_audio_data, audio_samples, audio_nchannels);\r
+ encode_hanc(reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()), frame_audio_data, audio_samples, audio_nchannels);\r
\r
- sdk_->system_buffer_write_async(const_cast<unsigned char*>(frame->image_data().begin()), \r
- frame->image_data().size(), \r
+ sdk_->system_buffer_write_async(const_cast<unsigned char*>(reserved_frames_.front()->image_data()), \r
+ reserved_frames_.front()->image_size(), \r
nullptr, \r
- BlueImage_HANC_DMABuffer(current_id_, BLUE_DATA_IMAGE));\r
+ BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));\r
\r
- sdk_->system_buffer_write_async(hanc->data(),\r
- hanc->size(), \r
+ sdk_->system_buffer_write_async(reserved_frames_.front()->hanc_data(),\r
+ reserved_frames_.front()->hanc_size(), \r
nullptr, \r
- BlueImage_HANC_DMABuffer(current_id_, BLUE_DATA_HANC));\r
+ BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_HANC));\r
\r
- if(BLUE_FAIL(sdk_->render_buffer_update(BlueBuffer_Image_HANC(current_id_))))\r
+ if(BLUE_FAIL(sdk_->render_buffer_update(BlueBuffer_Image_HANC(reserved_frames_.front()->id()))))\r
CASPAR_LOG(trace) << TEXT("BLUEFISH: render_buffer_update failed");\r
}\r
else\r
{\r
- sdk_->system_buffer_write_async(const_cast<unsigned char*>(frame->image_data().begin()),\r
- frame->image_data().size(), \r
+ sdk_->system_buffer_write_async(const_cast<unsigned char*>(reserved_frames_.front()->image_data()),\r
+ reserved_frames_.front()->image_size(), \r
nullptr, \r
- BlueImage_DMABuffer(current_id_, BLUE_DATA_IMAGE));\r
+ BlueImage_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));\r
\r
- if(BLUE_FAIL(sdk_->render_buffer_update(BlueBuffer_Image(current_id_))))\r
+ if(BLUE_FAIL(sdk_->render_buffer_update(BlueBuffer_Image(reserved_frames_.front()->id()))))\r
CASPAR_LOG(trace) << TEXT("BLUEFISH: render_buffer_update failed");\r
}\r
\r
- transferring_frame_ = frame;\r
+ std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end());\r
}\r
catch(...)\r
{\r
\r
hanc_stream_info_struct hanc_stream_info;\r
memset(&hanc_stream_info, 0, sizeof(hanc_stream_info));\r
-\r
- std::fill_n(hanc_stream_info.AudioDBNArray, sizeof(hanc_stream_info.AudioDBNArray)/sizeof(hanc_stream_info.AudioDBNArray[0]), -1);\r
+ \r
+ hanc_stream_info.AudioDBNArray[0] = -1;\r
+ hanc_stream_info.AudioDBNArray[1] = -1;\r
+ hanc_stream_info.AudioDBNArray[2] = -1;\r
+ hanc_stream_info.AudioDBNArray[3] = -1;\r
hanc_stream_info.hanc_data_ptr = hanc_data;\r
hanc_stream_info.video_mode = vid_fmt_;\r
\r
#include <BlueVelvet4.h>\r
#include "../../format/video_format.h"\r
\r
+#include <common/memory/page_locked_allocator.h>\r
+\r
#include <tbb/mutex.h>\r
+#include <tbb/recursive_mutex.h>\r
+\r
+#include <unordered_map>\r
\r
namespace caspar { namespace core { namespace bluefish {\r
\r
static const size_t MAX_HANC_BUFFER_SIZE = 256*1024;\r
static const size_t MAX_VBI_BUFFER_SIZE = 36*1920*4;\r
\r
-struct page_locked_buffer\r
+struct blue_dma_buffer\r
{\r
public:\r
- page_locked_buffer(size_t size) \r
- : size_(size)\r
- , data_(static_cast<unsigned char*>(::VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)))\r
- {\r
- if(!data_) \r
- BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("Failed to allocate memory for paged locked buffer.")); \r
- if(::VirtualLock(data_.get(), size_) == 0) \r
- BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("Failed to lock memory for paged locked buffer."));\r
- }\r
- \r
- static void reserve_working_size(size_t size)\r
- {\r
- auto lock = get_lock();\r
- SIZE_T workingSetMinSize = 0, workingSetMaxSize = 0;\r
- if(::GetProcessWorkingSetSize(::GetCurrentProcess(), &workingSetMinSize, &workingSetMaxSize))\r
- {\r
- CASPAR_LOG(debug) << TEXT("WorkingSet size: min = ") << workingSetMinSize << TEXT(", max = ") << workingSetMaxSize;\r
- \r
- workingSetMinSize += size;\r
- workingSetMaxSize += size;\r
-\r
- if(!::SetProcessWorkingSetSize(::GetCurrentProcess(), workingSetMinSize, workingSetMaxSize)) \r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed set workingset.")); \r
- }\r
- }\r
-\r
- static void unreserve_working_size(size_t size)\r
- {\r
- auto lock = get_lock();\r
- SIZE_T workingSetMinSize = 0, workingSetMaxSize = 0;\r
- if(::GetProcessWorkingSetSize(::GetCurrentProcess(), &workingSetMinSize, &workingSetMaxSize))\r
- {\r
- CASPAR_LOG(debug) << TEXT("WorkingSet size: min = ") << workingSetMinSize << TEXT(", max = ") << workingSetMaxSize;\r
+ blue_dma_buffer(int image_size, int id) \r
+ : id_(id)\r
+ , image_size_(image_size)\r
+ , hanc_size_(256*1024)\r
+ , image_buffer_(image_size_)\r
+ , hanc_buffer_(hanc_size_){}\r
\r
- workingSetMinSize += static_cast<SIZE_T>(static_cast<int>(workingSetMinSize) - static_cast<int>(size));\r
- workingSetMaxSize += static_cast<SIZE_T>(static_cast<int>(workingSetMaxSize) - static_cast<int>(size));\r
+ int id() const {return id_;}\r
\r
- if(!::SetProcessWorkingSetSize(::GetCurrentProcess(), workingSetMinSize, workingSetMaxSize)) \r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Failed set workingset.")); \r
- }\r
- }\r
+ PBYTE image_data() { return image_buffer_.data(); }\r
+ PBYTE hanc_data() { return hanc_buffer_.data(); }\r
\r
- PBYTE data() const { return data_.get(); }\r
- size_t size() const { return size_; }\r
-private:\r
-\r
- static std::unique_ptr<tbb::mutex::scoped_lock> get_lock()\r
- {\r
- static tbb::mutex mutex;\r
- return std::move(std::unique_ptr<tbb::mutex::scoped_lock>(new tbb::mutex::scoped_lock(mutex)));\r
- }\r
- \r
- struct virtual_free\r
- {\r
- void operator()(LPVOID lpAddress)\r
- {\r
- if(lpAddress != nullptr) \r
- try{::VirtualFree(lpAddress, 0, MEM_RELEASE);}catch(...){} \r
- }\r
- };\r
+ size_t image_size() const { return image_size_; }\r
+ size_t hanc_size() const { return hanc_size_; }\r
\r
- size_t size_;\r
- std::unique_ptr<BYTE, virtual_free> data_;\r
+private: \r
+ int id_;\r
+ size_t image_size_;\r
+ size_t hanc_size_;\r
+ std::vector<BYTE, page_locked_allocator<BYTE>> image_buffer_; \r
+ std::vector<BYTE, page_locked_allocator<BYTE>> hanc_buffer_;\r
};\r
-typedef std::shared_ptr<page_locked_buffer> page_locked_buffer_ptr;\r
+typedef std::shared_ptr<blue_dma_buffer> blue_dma_buffer_ptr;\r
\r
}}}
\ No newline at end of file
});\r
});\r
\r
- if(frame_processor_->get_video_format_desc().mode == video_mode::upper && codec_context_->codec_id == CODEC_ID_DVVIDEO) // Move up one field \r
+ if(codec_context_->codec_id == CODEC_ID_DVVIDEO) // Move up one field frame_processor_->get_video_format_desc().mode == video_mode::upper && \r
write->translate(0.0f, 1.0/static_cast<double>(height_));\r
\r
return write;\r
avpicture_fill(reinterpret_cast<AVPicture*>(&av_frame), write->image_data().begin(), PIX_FMT_BGRA, width_, height_);\r
\r
sws_scale(sws_context_.get(), decoded_frame->data, decoded_frame->linesize, 0, height_, av_frame.data, av_frame.linesize); \r
- \r
- if(frame_processor_->get_video_format_desc().mode == video_mode::upper && codec_context_->codec_id == CODEC_ID_DVVIDEO) // Move up one field \r
- write->translate(0.0f, 1.0/static_cast<double>(height_));\r
\r
return write;\r
} \r