sample code - “get Live view and OSD image with http”

usage:
   p <1(Main),2(httpLV)>  - set live view protocol
   l                      - get live view
   t                      - turn On OSD image
   o                      - get OSD image
   s <0(liveview),1(osd)> - streaming liveview/osd
   k <1(U),2(D),3(L),4(R),5(Ent),6(Menu) - send key
This sample code is included in SimpleCli.
For Jetson environment, please add ‘target_link_libraries(${remotecli} PRIVATE “-pthread”)’ to CMakeLists.txt.
  1// "get live view and OSD with http" sample
  2#include "httplib.h"  // https://github.com/yhirose/cpp-httplib
  3
  4#include <chrono>
  5#include <cstdint>
  6#include <cstdlib>
  7#include <fstream>
  8#include <future>
  9#include <iostream>
 10#include <mutex>
 11#include <sstream>
 12#include <stdexcept>
 13#include <string>
 14#include <thread>
 15#include <vector>
 16
 17#if !defined(__APPLE__)
 18  #if defined(USE_EXPERIMENTAL_FS) // for jetson
 19    #include <experimental/filesystem>
 20    namespace fs = std::experimental::filesystem;
 21  #else
 22    #include <filesystem>
 23    namespace fs = std::filesystem;
 24  #endif
 25#endif
 26
 27#if defined(__APPLE__) || defined(__linux__)
 28  #include <unistd.h>
 29#endif
 30
 31// macro for multibyte character
 32#if defined(_WIN32) || defined(_WIN64)
 33  using CrString = std::wstring;
 34  #define CRSTR(s) L ## s
 35  #define CrCout std::wcout
 36  #define DELIMITER CRSTR("\\")
 37#else
 38  using CrString = std::string;
 39  #define CRSTR(s) s
 40  #define CrCout std::cout
 41  #define DELIMITER CRSTR("/")
 42#endif
 43
 44
 45#include "CrDeviceProperty.h"
 46#include "CameraRemote_SDK.h"
 47#include "IDeviceCallback.h"
 48#include "CrDebugString.h"   // use CrDebugString.cpp
 49
 50#define PrintError(msg, err) { fprintf(stderr, "Error in %s(%d):" msg ",%s\n", __FUNCTION__, __LINE__, (err ? CrErrorString(err).c_str():"")); }
 51#define GotoError(msg, err) { PrintError(msg, err); goto Error; }
 52
 53bool  m_connected = false;
 54CrString m_modelId;
 55int64_t  m_device_handle = 0;
 56
 57std::mutex m_eventPromiseMutex;
 58uint32_t m_setDPCode = 0;
 59std::promise<void>* m_eventPromise = nullptr;
 60void setEventPromise(std::promise<void>* dp)
 61{
 62    std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
 63    m_eventPromise = dp;
 64}
 65
 66std::promise<void>* m_lvPromise = nullptr;
 67std::mutex m_lvPromiseMutex;
 68int lvType = 0;
 69void setLvPromise(std::promise<void>* dp)
 70{
 71    std::lock_guard<std::mutex> lock(m_lvPromiseMutex);
 72    m_lvPromise = dp;
 73}
 74
 75SCRSDK::CrError _getDeviceProperty(int64_t device_handle, uint32_t code, SCRSDK::CrDeviceProperty* devProp)
 76{
 77    std::int32_t nprop = 0;
 78    SCRSDK::CrDeviceProperty* prop_list = nullptr;
 79    SCRSDK::CrError err = SCRSDK::GetSelectDeviceProperties(device_handle, 1, &code, &prop_list, &nprop);
 80    if(err) GotoError("", err);
 81    if(prop_list && nprop >= 1) {
 82        *devProp = prop_list[0];
 83    }
 84Error:
 85    if(prop_list) SCRSDK::ReleaseDeviceProperties(device_handle, prop_list);
 86    return err;
 87}
 88
 89SCRSDK::CrError _setDeviceProperty(int64_t device_handle, uint32_t code, uint64_t data, bool blocking=true)
 90{
 91    int result = SCRSDK::CrError_Generic_Unknown;
 92    SCRSDK::CrError err = 0;
 93    std::promise<void> eventPromise;
 94    std::future<void> eventFuture = eventPromise.get_future();
 95    std::future_status status;
 96
 97    SCRSDK::CrDeviceProperty devProp;
 98
 99    err = _getDeviceProperty(device_handle, code, &devProp);
100    if(err) GotoError("", err);
101    if (blocking && devProp.GetCurrentValue() == data) {
102        std::cout << "skipped\n";
103        return 0;
104    }
105
106    if(blocking) {
107        std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
108        m_setDPCode = code;
109        m_eventPromise = &eventPromise;
110    }
111
112    devProp.SetCurrentValue(data);
113    err = SCRSDK::SetDeviceProperty(device_handle, &devProp);
114    if(err) GotoError("", err);
115
116    if(!blocking) return 0;
117
118    status = eventFuture.wait_for(std::chrono::milliseconds(3000));
119    if(status != std::future_status::ready) GotoError("timeout", 0);
120
121    try{
122        eventFuture.get();
123    } catch(const std::exception&) GotoError("", 0);
124    std::cout << "OK\n";
125
126    result = 0;
127Error:
128    setEventPromise(nullptr);
129    return result;
130}
131
132class DeviceCallback : public SCRSDK::IDeviceCallback
133{
134public:
135    DeviceCallback() {};
136    ~DeviceCallback() {};
137
138    void OnConnected(SCRSDK::DeviceConnectionVersioin version)
139    {
140        CrCout << "Connected to " << m_modelId << "\n";
141        m_connected = true;
142        std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
143        if(m_eventPromise) {
144            m_eventPromise->set_value();
145            m_eventPromise = nullptr;
146        }
147    }
148
149    void OnError(CrInt32u error)
150    {
151        printf("Connection error:%s\n", CrErrorString(error).c_str());
152        std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
153        if(m_eventPromise) {
154            m_eventPromise->set_exception(std::make_exception_ptr(std::runtime_error("error")));
155            m_eventPromise = nullptr;
156        }
157    }
158
159    void OnDisconnected(CrInt32u error)
160    {
161        CrCout << "Disconnected from " << m_modelId << "\n";
162        m_connected = false;
163        std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
164        if(m_eventPromise) {
165            m_eventPromise->set_value();
166            m_eventPromise = nullptr;
167        }
168    }
169
170    void OnCompleteDownload(CrChar* filename, CrInt32u type )
171    {
172        CrCout << "OnCompleteDownload:" << filename << "\n";
173    }
174
175    void OnNotifyContentsTransfer(CrInt32u notify, SCRSDK::CrContentHandle contentHandle, CrChar* filename)
176    {
177        std::cout << "OnNotifyContentsTransfer.\n";
178    }
179
180    void OnWarning(CrInt32u warning)
181    {
182        if (warning == SCRSDK::CrWarning_Connect_Reconnecting) {
183            CrCout << "Reconnecting to " << m_modelId << "\n";
184            return;
185        }
186    }
187
188    void OnWarningExt(CrInt32u warning, CrInt32 param1, CrInt32 param2, CrInt32 param3) {}
189    void OnLvPropertyChanged() {}
190    void OnLvPropertyChangedCodes(CrInt32u num, CrInt32u* codes) {}
191    void OnPropertyChanged() {}
192    void OnPropertyChangedCodes(CrInt32u num, CrInt32u* codes)
193    {
194        std::cout << "OnPropertyChangedCodes:\n";
195        for(uint32_t i = 0; i < num; ++i) {
196            std::lock_guard<std::mutex> lock(m_eventPromiseMutex);
197            if(m_setDPCode && m_setDPCode == codes[i]) {
198                m_setDPCode = 0;
199                if(m_eventPromise) {
200                    m_eventPromise->set_value();
201                    m_eventPromise = nullptr;
202                }
203            }
204        }
205    }
206    void OnNotifyMonitorUpdated(CrInt32u type, CrInt32u frameNo)
207    {
208        if(type == lvType) {
209    //  printf("%x", frameNo & 0xF);
210            std::lock_guard<std::mutex> lock(m_lvPromiseMutex);
211            if(m_lvPromise) {
212                m_lvPromise->set_value();
213                m_lvPromise = nullptr;
214            }
215        }
216    }
217
218};
219
220CrString _getModelId(const SCRSDK::ICrCameraObjectInfo* objInfo)
221{
222    CrString id;
223    if (CrString(objInfo->GetConnectionTypeName()) == CRSTR("IP")) {
224        id = CrString(objInfo->GetMACAddressChar());
225    } else {
226        id = CrString((CrChar*)objInfo->GetId());
227    }
228    return CrString(objInfo->GetModel()).append(CRSTR(" (")).append(id).append(CRSTR(")"));
229}
230
231SCRSDK::CrError _getIdPassword(SCRSDK::ICrCameraObjectInfo* objInfo, std::string& fingerprint, std::string& userId, std::string& userPassword)
232{
233    char fpBuff[128] = {0};
234    CrInt32u fpLen = 0;
235    SCRSDK::CrError err = SCRSDK::GetFingerprint(objInfo, fpBuff, &fpLen);
236    if(err) GotoError("", err);
237    fingerprint = std::string(fpBuff, fpLen);
238
239    std::cout << "fingerprint: " << fingerprint.c_str() << "\n";
240    std::cout << "id:";       std::getline(std::cin, userId);
241    std::cout << "password:"; std::getline(std::cin, userPassword);
242    return 0;
243Error:
244    return err;
245}
246
247SCRSDK::CrError _getLiveView(int64_t device_handle, CrString path)
248{
249    int result = SCRSDK::CrError_Generic_Unknown;
250    SCRSDK::CrError err = 0;
251    CrInt32 num = 0;
252    SCRSDK::CrLiveViewProperty* property = nullptr;
253    SCRSDK::CrImageInfo imageInfo;
254    SCRSDK::CrImageDataBlock image_data;
255    CrInt32u bufSize = 0;
256    CrInt8u* image_buff = nullptr;
257
258    err = SCRSDK::GetLiveViewProperties(device_handle, &property, &num);  if(err) GotoError("", err);
259    SCRSDK::ReleaseLiveViewProperties(device_handle, property);
260
261    err = SCRSDK::GetLiveViewImageInfo(device_handle, &imageInfo);  if(err) GotoError("", err);
262    bufSize = imageInfo.GetBufferSize();
263    if (bufSize <= 0) GotoError("", 0);
264
265    image_buff = new CrInt8u[bufSize];
266    if (!image_buff) GotoError("", 0);
267
268    image_data.SetData(image_buff);
269    image_data.SetSize(bufSize);
270
271    err = SCRSDK::GetLiveViewImage(device_handle, &image_data);  if(err) GotoError("", err);
272    if (image_data.GetSize() <= 0) GotoError("", 0);
273
274    {
275        path.append(DELIMITER CRSTR("LiveView000000.JPG"));
276        std::ofstream file(path, std::ios::out | std::ios::binary);
277        if (file.bad()) GotoError("", 0);
278        file.write((char*)image_data.GetImageData(), image_data.GetImageSize());
279        file.close();
280        CrCout << path.data() << '\n';
281    }
282    result = 0;
283Error:
284    if(image_buff) delete[] image_buff;
285    return result;
286}
287
288SCRSDK::CrError _getOsdImage(int64_t device_handle, CrString path)
289{
290    int result = SCRSDK::CrError_Generic_Unknown;
291    SCRSDK::CrError err = 0;
292    SCRSDK::CrDeviceProperty devProp;
293    SCRSDK::CrOSDImageDataBlock image_data;
294    CrInt8u* image_buff = nullptr;
295
296    err = _getDeviceProperty(device_handle, SCRSDK::CrDeviceProperty_OSDImageMode, &devProp);
297    if(err) GotoError("", err);
298
299    if (devProp.GetCurrentValue() != SCRSDK::CrOSDImageMode_On)
300        GotoError("please turn on OSD image", 0);
301
302    image_buff = new CrInt8u[CR_OSD_IMAGE_MAX_SIZE]; if(!image_buff) GotoError("", 0);
303    image_data.SetData(image_buff);
304
305    err = SCRSDK::GetOSDImage(device_handle, &image_data); if(err) GotoError("", err);
306    if (image_data.GetImageSize() <= 0) GotoError("", 0);
307
308    {
309        path.append(DELIMITER CRSTR("OSDImage000000.PNG"));
310        std::ofstream file(path, std::ios::out | std::ios::binary);
311        if (file.bad()) GotoError("", 0)
312        file.write((char*)image_data.GetImageData(), image_data.GetImageSize());
313        file.close();
314        CrCout << path.data() << '\n';
315    }
316    result = 0;
317Error:
318    if(image_buff) delete[] image_buff;
319    return result;
320}
321
322//-------------------------------
323
324#include <atomic>
325
326std::atomic<bool> running(true);
327
328SCRSDK::CrError _getLiveView2(int64_t device_handle, CrInt8u** lv_image, CrInt32u* lv_size)
329{
330    int result = SCRSDK::CrError_Generic_Unknown;
331    SCRSDK::CrError err = 0;
332    CrInt32 num = 0;
333    SCRSDK::CrLiveViewProperty* property = nullptr;
334    SCRSDK::CrImageInfo imageInfo;
335    SCRSDK::CrImageDataBlock image_data;
336    CrInt32u bufSize = 0;
337    CrInt8u* image_buff = nullptr;
338
339    err = SCRSDK::GetLiveViewProperties(device_handle, &property, &num);  if(err) GotoError("", err);
340    SCRSDK::ReleaseLiveViewProperties(device_handle, property);
341
342    err = SCRSDK::GetLiveViewImageInfo(device_handle, &imageInfo);  if(err) GotoError("", err);
343    bufSize = imageInfo.GetBufferSize();
344    if (bufSize <= 0) GotoError("", 0);
345
346    image_buff = new CrInt8u[bufSize];
347    if (!image_buff) GotoError("", 0);
348
349    image_data.SetData(image_buff);
350    image_data.SetSize(bufSize);
351
352    err = SCRSDK::GetLiveViewImage(device_handle, &image_data);  if(err) GotoError("", err);
353    if (image_data.GetSize() <= 0) GotoError("", 0);
354
355    *lv_size = image_data.GetImageSize();
356    *lv_image = new CrInt8u[*lv_size];
357    if(!*lv_image) GotoError("", 0);
358    memcpy(*lv_image, image_data.GetImageData(), *lv_size);
359    result = 0;
360Error:
361    if(image_buff) delete[] image_buff;
362    return result;
363}
364
365SCRSDK::CrError _getOsdImage2(int64_t device_handle, CrInt8u** lv_image, CrInt32u* lv_size)
366{
367    int result = SCRSDK::CrError_Generic_Unknown;
368    SCRSDK::CrError err = 0;
369    SCRSDK::CrDeviceProperty devProp;
370    SCRSDK::CrOSDImageDataBlock image_data;
371    CrInt8u* image_buff = nullptr;
372
373    err = _getDeviceProperty(device_handle, SCRSDK::CrDeviceProperty_OSDImageMode, &devProp);
374    if(err) GotoError("", err);
375
376    if (devProp.GetCurrentValue() != SCRSDK::CrOSDImageMode_On)
377        GotoError("please turn on OSD image", 0);
378
379    image_buff = new CrInt8u[CR_OSD_IMAGE_MAX_SIZE]; if(!image_buff) GotoError("", 0);
380    image_data.SetData(image_buff);
381
382    err = SCRSDK::GetOSDImage(device_handle, &image_data); if(err) GotoError("", err);
383    if (image_data.GetImageSize() <= 0) GotoError("", 0);
384
385    *lv_size = image_data.GetImageSize();
386    *lv_image = new CrInt8u[*lv_size];
387    if(!*lv_image) GotoError("", 0);
388    memcpy(*lv_image, image_data.GetImageData(), *lv_size);
389    result = 0;
390Error:
391    if(image_buff) delete[] image_buff;
392    return result;
393}
394
395bool streamLiveview(size_t offset, httplib::DataSink &sink)
396{
397    bool result = false;
398    SCRSDK::CrError err = 0;
399    CrInt8u* image_buff = nullptr;
400    CrInt32u bufSize = 0;
401
402    std::promise<void> lvPromise;
403    std::future<void> lvFuture = lvPromise.get_future();
404    std::future_status status;
405
406    setLvPromise(&lvPromise);
407    status = lvFuture.wait_for(std::chrono::milliseconds(3000));
408    if(status != std::future_status::ready) GotoError("timeout", 0);
409    try{
410        lvFuture.get();
411    } catch(const std::exception&) GotoError("", 0);
412
413    {
414        char buf[256] = {0};
415        int len = 0;
416        if(lvType == 0) { // liveview
417		    err = _getLiveView2(m_device_handle, &image_buff, &bufSize);
418		    if(err) return false;
419	        len = snprintf(buf, sizeof(buf), "--frame\r\n"
420	                                            "Content-Type: image/jpeg\r\n"
421	                                            "Content-Length: %d\r\n\r\n", bufSize);
422		} else { // osd
423		    err = _getOsdImage2(m_device_handle, &image_buff, &bufSize);
424		    if(err) return false;
425	        len = snprintf(buf, sizeof(buf), "--frame\r\n"
426	                                            "Content-Type: image/png\r\n"
427	                                            "Content-Length: %d\r\n\r\n", bufSize);
428		}
429        if(len >= sizeof(buf)) GotoError("", 0);
430        sink.write(buf, len);
431    }
432    sink.write((char*)image_buff, bufSize);
433    sink.write("\r\n", 2);
434
435    //std::this_thread::sleep_for(std::chrono::milliseconds(33));
436    result = true;
437Error:
438    setLvPromise(nullptr);
439    delete[] image_buff;
440    image_buff = nullptr;
441    return result;
442}
443
444void handle_request(const httplib::Request& req, httplib::Response& res)
445{
446    res.set_chunked_content_provider(
447        "multipart/x-mixed-replace; boundary=frame", streamLiveview
448    );
449}
450
451void server_thread(httplib::Server& svr)
452{
453    std::cout << "please access to http://localhost:8080\n";
454    svr.Get("/", handle_request);
455    svr.listen("127.0.0.1", 8080);
456    running = false;
457}
458
459std::vector<std::string> _split(std::string inputLine, char delimiter)
460{
461    std::vector<std::string> strArray;
462    if (inputLine.empty()) return strArray;
463
464    std::string tmp;
465    std::stringstream ss{inputLine};
466    while (getline(ss, tmp, delimiter)) {
467        strArray.push_back(tmp);
468    }
469    return strArray;
470}
471
472int main(void)
473{
474    int result = -1;
475    SCRSDK::CrError err = SCRSDK::CrError_None;
476    SCRSDK::ICrEnumCameraObjectInfo* enumCameraObjectInfo = nullptr;
477    SCRSDK::ICrCameraObjectInfo* objInfo = nullptr;
478    DeviceCallback deviceCallback;
479    httplib::Server svr;
480    std::thread* serverThread = nullptr;
481
482  #if defined(__APPLE__)
483    #define MAC_MAX_PATH 255
484    char pathBuf[MAC_MAX_PATH] = {0};
485    if(NULL == getcwd(pathBuf, sizeof(pathBuf) - 1)) return 1;
486    CrString path = pathBuf;
487  #else
488    CrString path = fs::current_path().native();
489  #endif
490
491    bool boolRet = SCRSDK::Init();
492    if(!boolRet) GotoError("", 0);
493
494    // enumeration
495    {
496        uint32_t count = 0;
497        uint32_t index = 1;
498
499        err = SCRSDK::EnumCameraObjects(&enumCameraObjectInfo, 3/*timeInSec*/);
500        if(err || !enumCameraObjectInfo) GotoError("no camera", err);
501
502        count = enumCameraObjectInfo->GetCount();
503        if(count >= 2) {
504            for (uint32_t i = 0; i < count; ++i) {
505                auto* info = enumCameraObjectInfo->GetCameraObjectInfo(i);
506                CrCout << '[' << i + 1 << "] " << _getModelId(info) << "\n";
507            }
508
509            std::string inputLine;
510            std::cout << "select camera:"; std::getline(std::cin, inputLine);
511            try { index = stoi(inputLine); } catch(const std::exception&) { GotoError("", 0); }
512            if(index < 1 || index > count) GotoError("", 0);
513        }
514        objInfo = (SCRSDK::ICrCameraObjectInfo*)enumCameraObjectInfo->GetCameraObjectInfo(index - 1);
515        m_modelId = _getModelId(objInfo);
516    }
517
518    // connect
519    {
520        std::string  fingerprint = "";
521        std::string  userId = "";
522        std::string  userPassword = "";
523        std::promise<void> eventPromise;
524        std::future<void> eventFuture = eventPromise.get_future();
525
526        if (objInfo->GetSSHsupport() == SCRSDK::CrSSHsupport_ON) {
527            err = _getIdPassword(objInfo, fingerprint, userId, userPassword); if(err) goto Error;
528        }
529
530        setEventPromise(&eventPromise);
531        err = SCRSDK::Connect(objInfo, &deviceCallback, &m_device_handle,
532            SCRSDK::CrSdkControlMode_Remote,
533            SCRSDK::CrReconnecting_ON,
534            userId.c_str(), userPassword.c_str(), fingerprint.c_str(), (uint32_t)fingerprint.size());
535        if(err) GotoError("", err);
536
537    //  std::future_status status = eventFuture.wait_for(std::chrono::milliseconds(3000));
538    //  if(status != std::future_status::ready) GotoError("timeout",0);
539        try{
540            eventFuture.get();
541        } catch(const std::exception&) GotoError("", 0);
542    }
543
544    // set work directory
545    {
546        CrCout << "path=" << path.data() << "\n";
547        err = SCRSDK::SetSaveInfo(m_device_handle, const_cast<CrChar*>(path.data()), const_cast<CrChar*>(CRSTR("DSC")), -1/*startNo*/);
548        if(err) GotoError("", err);
549    }
550
551    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
552
553    std::cout << "usage:\n";
554    std::cout << "   p <1(Main),2(httpLV)>  - set live view protocol\n";
555    std::cout << "   l                      - get live view\n";
556    std::cout << "   t                      - turn On OSD image\n";
557    std::cout << "   o                      - get OSD image\n";
558    std::cout << "   s <0(liveview),1(osd)> - streaming liveview/osd\n";
559    std::cout << "   k <1(U),2(D),3(L),4(R),5(Ent),6(Menu) - send key\n";
560    std::cout << "To exit, please enter 'q'.\n";
561
562    while(1) {
563        std::string inputLine;
564        std::getline(std::cin, inputLine);
565        std::vector<std::string> args = _split(inputLine, ' ');
566
567        if(args.size() == 0) {
568
569        } else if(args[0] == "p" && args.size() >=2) {
570            uint32_t val;
571            try { val = stoi(args[1]); } catch(const std::exception&) { GotoError("", 0); }
572            err = _setDeviceProperty(m_device_handle, SCRSDK::CrDeviceProperty_LiveViewProtocol, val);
573            if(err) goto Error;
574
575        } else if(args[0] == "l" || args[0] == "L") {
576            err = _getLiveView(m_device_handle, path);
577            if(err) goto Error;
578
579        } else if(args[0] == "s" || args[0] == "S") {
580        	if(args.size() >= 2) {
581	            try { lvType = stoi(args[1]); } catch(const std::exception&) { GotoError("", 0); }
582	        }
583            if(!serverThread) {
584                serverThread = new std::thread(server_thread, std::ref(svr));
585            }
586
587        } else if(args[0] == "k" && args.size() >=2) {
588            uint32_t val;
589            try { val = stoi(args[1]); } catch(const std::exception&) { GotoError("", 0); }
590            val = val << 16;
591            err = _setDeviceProperty(m_device_handle, SCRSDK::CrDeviceProperty_CameraButtonFunction, (val|SCRSDK::CrCameraButtonFunctionValue_Down), false);
592            err = _setDeviceProperty(m_device_handle, SCRSDK::CrDeviceProperty_CameraButtonFunction, (val|SCRSDK::CrCameraButtonFunctionValue_Up), false);
593            if(err) goto Error;
594
595        } else if(args[0] == "t" || args[0] == "T") {
596            err = _setDeviceProperty(m_device_handle, SCRSDK::CrDeviceProperty_OSDImageMode, SCRSDK::CrOSDImageMode_On);
597            if(err) goto Error;
598
599        } else if(args[0] == "o" || args[0] == "O") {
600            err = _getOsdImage(m_device_handle, path);
601            if(err) goto Error;
602
603        } else if(args[0] == "q" || args[0] == "Q") {
604            break;
605        } else {
606            std::cout << "unknown command\n";
607        }
608    }
609
610    result = 0;
611Error:
612    if(serverThread) {
613        svr.stop();
614        while(running);
615        serverThread->join();
616    }
617    if(enumCameraObjectInfo) enumCameraObjectInfo->Release();
618
619    if(m_connected) {
620        std::promise<void> eventPromise;
621        std::future<void> eventFuture = eventPromise.get_future();
622        setEventPromise(&eventPromise);
623        SCRSDK::Disconnect(m_device_handle);
624        eventFuture.wait_for(std::chrono::milliseconds(3000));
625    }
626    if(m_device_handle) SCRSDK::ReleaseDevice(m_device_handle);
627    SCRSDK::Release();
628
629    return result;
630}