849a42c1daa7495610b851849e03adad38cd8943
[platform/upstream/iotivity.git] / resource / examples / mediaserver.cpp
1 /* ****************************************************************
2  *
3  * Copyright 2016 Intel Corporation All Rights Reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  ******************************************************************/
18
19 ///
20 /// This sample provides steps to define an interface for a resource
21 /// (properties and methods) and host this resource on the server.
22 ///
23
24 #include <functional>
25 #include <mutex>
26 #include "platform_features.h"
27 #include <condition_variable>
28
29 #include "OCPlatform.h"
30 #include "OCApi.h"
31 #include "ocpayload.h"
32 #include <windows.h>
33 #include <objbase.h>
34
35 #include <commctrl.h>
36 #include <mmdeviceapi.h>
37 #include <endpointvolume.h>
38 #include <stdio.h>
39 #include <math.h>       /* log */
40
41 #define SAFE_RELEASE(x)  \
42               if ((x) != NULL)  \
43                 { (x)->Release(); (x) = NULL; }
44
45 using namespace OC;
46 using namespace std;
47 namespace PH = std::placeholders;
48
49 int gObservation = 0;
50 void * ChangeMediaRepresentation (void *param);
51 void * handleSlowResponse (void *param, std::shared_ptr<OCResourceRequest> pRequest);
52 void playPause(void);
53 void setVolume(int vol);
54 int getVolume(void);
55
56 // Specifies where to notify all observers or list of observers
57 // false: notifies all observers
58 // true: notifies list of observers
59 bool isListOfObservers = false;
60
61 // Specifies secure or non-secure
62 // false: non-secure resource
63 // true: secure resource
64 bool isSecure = false;
65
66 /// Specifies whether Entity handler is going to do slow response or not
67 bool isSlowResponse = false;
68
69
70 // Forward declaring the entityHandler
71
72 /// This class represents a single resource named 'MediaResource'. This resource has
73 /// two simple properties named 'state' and 'volume'
74
75 class MediaResource
76 {
77
78 public:
79     /// Access this property from a TB client
80     std::string m_name;
81     bool m_state;
82     int m_volume;
83     std::string m_mediaUri;
84     OCResourceHandle m_resourceHandle;
85     OCRepresentation m_mediaRep;
86     ObservationIds m_interestedObservers;
87
88 public:
89     /// Constructor
90     MediaResource()
91         :m_name("Media Player"), m_state(false), m_volume(0), m_mediaUri("/a/media"),
92                 m_resourceHandle(nullptr)
93     {
94         // Initialize representation
95         m_mediaRep.setUri(m_mediaUri);
96
97         m_mediaRep.setValue("state", m_state);
98         m_mediaRep.setValue("volume", m_volume);
99         m_mediaRep.setValue("name", m_name);
100     }
101
102     /* Note that this does not need to be a member function: for classes you do not have
103     access to, you can accomplish this with a free function: */
104
105     /// This function internally calls registerResource API.
106     void createResource()
107     {
108         OCStackResult result = OC_STACK_OK;
109
110         /* Resource Information */
111         std::string resourceURI = m_mediaUri;
112         std::string resourceTypeName = "core.media";
113         std::string resourceInterface = DEFAULT_INTERFACE;
114
115         /* Device Information */
116         char* deviceName = "IoTivity Media Server";
117         char* specVersion = "core.1.1.0";
118         OCStringLL types{ nullptr, const_cast<char*>(resourceTypeName.c_str()) };
119         OCDeviceInfo deviceInfo{ deviceName, &types, specVersion, nullptr };
120
121         result = OCPlatform::registerDeviceInfo(deviceInfo);
122         if (OC_STACK_OK != result)
123         {
124             cout << "Device information registration was unsuccessful\n";
125             return;
126         }
127
128         /* Platform Info */
129         char* platformId = "0A3E0D6F-DBF5-404E-8719-D6880042463A";
130         char* manufacturerName = "OCF";
131         char* manufacturerLink = "https://www.iotivity.org";
132         char* modelNumber = "895";
133         char* dateOfManufacture = "2016-01-15";
134         char* platformVersion = "1.0";
135         char* osVersion = "1.0";
136         char* hardwareVersion = "1.0";
137         char* firmwareVersion = "1.0";
138         char* supportLink = "https://www.iotivity.org";
139         OCPlatformInfo platformInfo = { platformId,
140                                         manufacturerName,
141                                         manufacturerLink,
142                                         modelNumber,
143                                         dateOfManufacture,
144                                         platformVersion,
145                                         osVersion,
146                                         hardwareVersion,
147                                         firmwareVersion,
148                                         supportLink,
149                                         nullptr
150                                       };
151
152         result = OCPlatform::registerPlatformInfo(platformInfo);
153         if (OC_STACK_OK != result)
154         {
155             cout << "Platform information registration was unsuccessful\n";
156             return;
157         }
158
159         // OCResourceProperty is defined ocstack.h
160         uint8_t resourceProperty;
161         if (isSecure)
162         {
163             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE | OC_SECURE;
164         }
165         else
166         {
167             resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE;
168         }
169         EntityHandler cb = std::bind(&MediaResource::entityHandler, this, PH::_1);
170
171         // This will internally create and register the resource.
172         result = OCPlatform::registerResource(m_resourceHandle, resourceURI, resourceTypeName,
173                                               resourceInterface, cb, resourceProperty);
174         if (OC_STACK_OK != result)
175         {
176             cout << "Resource creation was unsuccessful\n";
177         }
178     }
179
180     OCResourceHandle getHandle()
181     {
182         return m_resourceHandle;
183     }
184
185     // Puts representation.
186     // Gets values from the representation and
187     // updates the internal state
188     void put(OCRepresentation& rep)
189     {
190         try
191         {
192             if (rep.getValue("state", m_state))
193             {
194                 cout << "\t\t\t\t" << "state: " << m_state << endl;
195                 if(m_state)
196                 {
197                     playPause();
198                 }
199             }
200             else
201             {
202                 cout << "\t\t\t\t" << "state not found in the representation" << endl;
203             }
204
205             if (rep.getValue("volume", m_volume))
206             {
207                 cout << "\t\t\t\t" << "volume: " << m_volume << endl;
208                 if((0 <= m_volume) && (m_volume <= 100))
209                 {
210                     setVolume(m_volume);
211                 }
212             }
213             else
214             {
215                 cout << "\t\t\t\t" << "volume not found in the representation" << endl;
216             }
217         }
218         catch (exception& e)
219         {
220             cout << e.what() << endl;
221         }
222
223     }
224
225     // Post representation.
226     // Post can create new resource or simply act like put.
227     // Gets values from the representation and
228     // updates the internal state
229     OCRepresentation post(OCRepresentation& rep)
230     {
231         put(rep);
232         return get();
233     }
234
235
236     // gets the updated representation.
237     // Updates the representation with latest internal state before
238     // sending out.
239     OCRepresentation get()
240     {
241         m_mediaRep.setValue("state", m_state);
242         m_mediaRep.setValue("volume", m_volume);
243
244         return m_mediaRep;
245     }
246
247     void addType(const std::string& type) const
248     {
249         OCStackResult result = OCPlatform::bindTypeToResource(m_resourceHandle, type);
250         if (OC_STACK_OK != result)
251         {
252             cout << "Binding TypeName to Resource was unsuccessful\n";
253         }
254     }
255
256     void addInterface(const std::string& intf) const
257     {
258         OCStackResult result = OCPlatform::bindInterfaceToResource(m_resourceHandle, intf);
259         if (OC_STACK_OK != result)
260         {
261             cout << "Binding TypeName to Resource was unsuccessful\n";
262         }
263     }
264
265 private:
266 // This is just a sample implementation of entity handler.
267 // Entity handler can be implemented in several ways by the manufacturer
268 OCEntityHandlerResult entityHandler(std::shared_ptr<OCResourceRequest> request)
269 {
270     cout << "\tIn Server CPP entity handler:\n";
271     OCEntityHandlerResult ehResult = OC_EH_ERROR;
272     if(request)
273     {
274         // Get the request type and request flag
275         std::string requestType = request->getRequestType();
276         int requestFlag = request->getRequestHandlerFlag();
277
278         if(requestFlag & RequestHandlerFlag::RequestFlag)
279         {
280             cout << "\t\trequestFlag : Request\n";
281             auto pResponse = std::make_shared<OC::OCResourceResponse>();
282             pResponse->setRequestHandle(request->getRequestHandle());
283             pResponse->setResourceHandle(request->getResourceHandle());
284
285             // Check for query params (if any)
286             QueryParamsMap queries = request->getQueryParameters();
287
288             if (!queries.empty())
289             {
290                 cout << "\nQuery processing upto entityHandler" << std::endl;
291             }
292             for (auto it : queries)
293             {
294                 cout << "Query key: " << it.first << " value : " << it.second
295                         << std::endl;
296             }
297
298             // If the request type is GET
299             if(requestType == "GET")
300             {
301                 cout << "\t\t\trequestType : GET\n";
302                 if(isSlowResponse) // Slow response case
303                 {
304                     static int startedThread = 0;
305                     if(!startedThread)
306                     {
307                         std::thread t(handleSlowResponse, (void *)this, request);
308                         startedThread = 1;
309                         t.detach();
310                     }
311                     ehResult = OC_EH_SLOW;
312                 }
313                 else // normal response case.
314                 {
315
316                     pResponse->setResponseResult(OC_EH_OK);
317                     pResponse->setResourceRepresentation(get());
318                     if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
319                     {
320                         ehResult = OC_EH_OK;
321                     }
322                 }
323             }
324             else if(requestType == "PUT")
325             {
326                 cout << "\t\t\trequestType : PUT\n";
327                 OCRepresentation rep = request->getResourceRepresentation();
328
329                 // Do related operations related to PUT request
330                 // Update the mediaResource
331                 put(rep);
332
333                 pResponse->setResponseResult(OC_EH_OK);
334                 pResponse->setResourceRepresentation(get());
335                 if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
336                 {
337                     ehResult = OC_EH_OK;
338                 }
339             }
340             else if(requestType == "POST")
341             {
342                 cout << "\t\t\trequestType : POST\n";
343
344                 OCRepresentation rep = request->getResourceRepresentation();
345
346                 // Do related operations related to POST request
347                 OCRepresentation rep_post = post(rep);
348                 pResponse->setResourceRepresentation(rep_post);
349
350                 if(rep_post.hasAttribute("createduri"))
351                 {
352                     pResponse->setResponseResult(OC_EH_RESOURCE_CREATED);
353                     pResponse->setNewResourceUri(rep_post.getValue<std::string>("createduri"));
354                 }
355                 else
356                 {
357                     pResponse->setResponseResult(OC_EH_OK);
358                 }
359
360                 if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
361                 {
362                     ehResult = OC_EH_OK;
363                 }
364             }
365             else if(requestType == "DELETE")
366             {
367                 cout << "Delete request received" << endl;
368             }
369         }
370
371         if(requestFlag & RequestHandlerFlag::ObserverFlag)
372         {
373             ObservationInfo observationInfo = request->getObservationInfo();
374             if(ObserveAction::ObserveRegister == observationInfo.action)
375             {
376                 m_interestedObservers.push_back(observationInfo.obsId);
377             }
378             else if(ObserveAction::ObserveUnregister == observationInfo.action)
379             {
380                 m_interestedObservers.erase(std::remove(
381                                                             m_interestedObservers.begin(),
382                                                             m_interestedObservers.end(),
383                                                             observationInfo.obsId),
384                                                             m_interestedObservers.end());
385             }
386
387             cout << "\t\trequestFlag : Observer\n";
388             gObservation = 1;
389             static int startedThread = 0;
390
391             // Observation happens on a different thread in ChangeMediaRepresentation function.
392             // If we have not created the thread already, we will create one here.
393
394             if(!startedThread)
395             {
396                 std::thread t(ChangeMediaRepresentation, (void *)this);
397                 startedThread = 1;
398                 t.detach();
399             }
400
401             ehResult = OC_EH_OK;
402         }
403     }
404     else
405     {
406         cout << "Request invalid" << std::endl;
407     }
408
409     return ehResult;
410 }
411
412 };
413
414 // ChangeMediaRepresentaion is an observation function,
415 // which notifies any changes to the resource to stack
416 // via notifyObservers
417 void * ChangeMediaRepresentation (void *param)
418 {
419     int prevVolume = 0;
420     MediaResource* mediaPtr = (MediaResource*) param;
421
422     // This function continuously monitors for the changes
423     while (1)
424     {
425         Sleep(100);
426
427         if (gObservation)
428         {
429             prevVolume = mediaPtr->m_volume;
430             mediaPtr->m_volume = getVolume();
431             if (prevVolume == mediaPtr->m_volume)
432                 continue;
433
434             cout << "Volume changed from " << prevVolume << "% to " << mediaPtr->m_volume << "%\n";
435
436             // If under observation if there are any changes to the media resource
437             // we call notifyObservors
438             //
439             // For demostration we are changing the volume value and notifying.
440
441             cout << "\nVolume updated to : " << mediaPtr->m_volume << endl;
442             cout << "Notifying observers with resource handle: " << mediaPtr->getHandle() << endl;
443
444             OCStackResult result = OC_STACK_OK;
445
446             if(isListOfObservers)
447             {
448                 std::shared_ptr<OCResourceResponse> resourceResponse =
449                             {std::make_shared<OCResourceResponse>()};
450
451                 resourceResponse->setResourceRepresentation(mediaPtr->get(), DEFAULT_INTERFACE);
452
453                 result = OCPlatform::notifyListOfObservers(  mediaPtr->getHandle(),
454                                                              mediaPtr->m_interestedObservers,
455                                                              resourceResponse);
456             }
457             else
458             {
459                 result = OCPlatform::notifyAllObservers(mediaPtr->getHandle());
460             }
461
462             if(OC_STACK_NO_OBSERVERS == result)
463             {
464                 cout << "No More observers, stopping notifications" << endl;
465                 gObservation = 0;
466             }
467         }
468     }
469
470     return NULL;
471 }
472
473 void * handleSlowResponse (void *param, std::shared_ptr<OCResourceRequest> pRequest)
474 {
475     // This function handles slow response case
476     MediaResource* mediaPtr = (MediaResource*) param;
477     // Induce a case for slow response by using sleep
478     cout << "SLOW response" << std::endl;
479     sleep (10);
480
481     auto pResponse = std::make_shared<OC::OCResourceResponse>();
482     pResponse->setRequestHandle(pRequest->getRequestHandle());
483     pResponse->setResourceHandle(pRequest->getResourceHandle());
484     pResponse->setResourceRepresentation(mediaPtr->get());
485
486     pResponse->setResponseResult(OC_EH_OK);
487
488     // Set the slow response flag back to false
489     isSlowResponse = false;
490     OCPlatform::sendResponse(pResponse);
491     return NULL;
492 }
493
494 void PrintUsage()
495 {
496     cout << std::endl;
497     cout << "Usage : mediaserver <value>\n";
498     cout << "    Default - Non-secure resource and notify all observers\n";
499     cout << "    1 - Non-secure resource and notify list of observers\n\n";
500     cout << "    2 - Secure resource and notify all observers\n";
501     cout << "    3 - Secure resource and notify list of observers\n\n";
502     cout << "    4 - Non-secure resource, GET slow response, notify all observers\n";
503 }
504
505 static FILE* client_open(const char* /*path*/, const char* mode)
506 {
507     return fopen("./oic_svr_db_server.dat", mode);
508 }
509
510 void playPause()
511 {
512     INPUT ip;
513
514     // Set up a generic keyboard event.
515     ip.type = INPUT_KEYBOARD;
516     ip.ki.wScan = 0; // hardware scan code for key
517     ip.ki.time = 0;
518     ip.ki.dwExtraInfo = 0;
519     ip.ki.wVk = VK_MEDIA_PLAY_PAUSE; // virtual-key code for the "a" key
520     ip.ki.dwFlags = 0; // 0 for key press
521
522     SendInput(1, &ip, sizeof(INPUT));
523     // Release the "Play/Pause" key
524     ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
525     SendInput(1, &ip, sizeof(INPUT));
526 }
527
528 int getVolume()
529 {
530     IAudioEndpointVolume *g_pEndptVol = NULL;
531     HRESULT hr = S_OK;
532     IMMDeviceEnumerator *pEnumerator = NULL;
533     IMMDevice *pDevice = NULL;
534     OSVERSIONINFO VersionInfo;
535
536     ZeroMemory(&VersionInfo, sizeof(OSVERSIONINFO));
537     VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
538     GetVersionEx(&VersionInfo);
539     CoInitialize(NULL);
540
541     // Get enumerator for audio endpoint devices.
542     hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
543                           NULL, CLSCTX_INPROC_SERVER,
544                           __uuidof(IMMDeviceEnumerator),
545                           (void**)&pEnumerator);
546
547     // Get default audio-rendering device.
548     hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
549
550     hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),
551                            CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
552     float currentVal;
553     hr = g_pEndptVol->GetMasterVolumeLevelScalar(&currentVal);
554     fflush(stdout); // just in case
555
556     SAFE_RELEASE(pEnumerator)
557     SAFE_RELEASE(pDevice)
558     SAFE_RELEASE(g_pEndptVol)
559     CoUninitialize();
560     return ((int) round(100 * currentVal));
561
562 }
563
564 void setVolume(int vol)
565 {
566     IAudioEndpointVolume *g_pEndptVol = NULL;
567     HRESULT hr = S_OK;
568     IMMDeviceEnumerator *pEnumerator = NULL;
569     IMMDevice *pDevice = NULL;
570     OSVERSIONINFO VersionInfo;
571
572     ZeroMemory(&VersionInfo, sizeof(OSVERSIONINFO));
573     VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
574     GetVersionEx(&VersionInfo);
575     CoInitialize(NULL);
576
577     // Get enumerator for audio endpoint devices.
578     hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
579                           NULL, CLSCTX_INPROC_SERVER,
580                           __uuidof(IMMDeviceEnumerator),
581                           (void**)&pEnumerator);
582
583     // Get default audio-rendering device.
584     hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
585
586     hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),
587                            CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
588     float got = (float)vol/100.0; // needs to be within 1.0 to 0.0
589     hr = g_pEndptVol->SetMasterVolumeLevelScalar(got, NULL);
590     fflush(stdout); // just in case
591
592     SAFE_RELEASE(pEnumerator)
593     SAFE_RELEASE(pDevice)
594     SAFE_RELEASE(g_pEndptVol)
595     CoUninitialize();
596 }
597
598 int main(int argc, char* argv[])
599 {
600     OCPersistentStorage ps {client_open, fread, fwrite, fclose, unlink };
601
602     if (argc == 1)
603     {
604         isListOfObservers = false;
605         isSecure = false;
606     }
607     else if (argc == 2)
608     {
609         int value = atoi(argv[1]);
610         switch (value)
611         {
612             case 1:
613                 isListOfObservers = true;
614                 isSecure = false;
615                 break;
616             case 2:
617                 isListOfObservers = false;
618                 isSecure = true;
619                 break;
620             case 3:
621                 isListOfObservers = true;
622                 isSecure = true;
623                 break;
624             case 4:
625                 isSlowResponse = true;
626                 break;
627             default:
628                 PrintUsage();
629                 break;
630        }
631      }
632     else
633     {
634         PrintUsage();
635         return -1;
636     }
637
638     // Create PlatformConfig object
639     PlatformConfig cfg {
640         OC::ServiceType::InProc,
641         OC::ModeType::Server,
642         "0.0.0.0", // By setting to "0.0.0.0", it binds to all available interfaces
643         0,         // Uses randomly available port
644         OC::QualityOfService::LowQos,
645         &ps
646     };
647
648     OCPlatform::Configure(cfg);
649     try
650     {
651         // Create the instance of the resource class
652         // (in this case instance of class 'MediaResource').
653         MediaResource myMedia;
654
655         // Invoke createResource function of class media.
656         myMedia.createResource();
657         cout << "Created resource." << std::endl;
658
659         // A condition variable will free the mutex it is given, then do a non-
660         // intensive block until 'notify' is called on it.  In this case, since we
661         // don't ever call cv.notify, this should be a non-processor intensive version
662         // of while(true);
663         std::mutex blocker;
664         std::condition_variable cv;
665         std::unique_lock<std::mutex> lock(blocker);
666         cout <<"Waiting" << std::endl;
667         cv.wait(lock, []{return false;});
668     }
669     catch(OCException &e)
670     {
671         cout << "OCException in main : " << e.what() << endl;
672     }
673
674     // No explicit call to stop the platform.
675     // When OCPlatform::destructor is invoked, internally we do platform cleanup
676
677     return 0;
678 }
679