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