aboutsummaryrefslogtreecommitdiffstats
path: root/android/camera/camera-service.c
blob: 173b6f55cd7bea5e5f0726587c5113577f686422 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Contains emulated camera service implementation.
 */

#include "qemu-common.h"
#include "android/globals.h"  /* for android_hw */
#include "android/hw-qemud.h"
#include "android/utils/misc.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
#include "android/camera/camera-capture.h"
#include "android/camera/camera-format-converters.h"
#include "android/camera/camera-service.h"

#define  E(...)    derror(__VA_ARGS__)
#define  W(...)    dwarning(__VA_ARGS__)
#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
#define  D_ACTIVE  VERBOSE_CHECK(camera)

/* the T(...) macro is used to dump traffic */
#define  T_ACTIVE   0

#if T_ACTIVE
#define  T(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
#else
#define  T(...)    ((void)0)
#endif

/* Defines name of the camera service. */
#define SERVICE_NAME    "camera"

/* Maximum number of supported emulated cameras. */
#define MAX_CAMERA      8

/* Camera sevice descriptor. */
typedef struct CameraServiceDesc CameraServiceDesc;
struct CameraServiceDesc {
    /* Information about camera devices connected to the host.
     * Note that once initialized, entries in this array are considered to be
     * constant. */
    CameraInfo  camera_info[MAX_CAMERA];
    /* Number of camera devices connected to the host. */
    int         camera_count;
};

/* One and only one camera service. */
static CameraServiceDesc    _camera_service_desc;

/********************************************************************************
 * Helper routines
 *******************************************************************************/

/* Extracts query name, and (optionally) query parameters from the query string.
 * Param:
 *  query - Query string. Query string in the camera service are formatted as such:
 *          "<query name>[ <parameters>]",
 *      where parameters are optional, and if present, must be separated from the
 *      query name with a single ' '. See comments to get_token_value routine
 *      for the format of the parameters string.
 *  query_name - Upon success contains query name extracted from the query
 *      string.
 *  query_name_size - Buffer size for 'query_name' string.
 *  query_param - Upon success contains a pointer to the beginning of the query
 *      parameters. If query has no parameters, NULL will be passed back with
 *      this parameter. This parameter is optional and can be NULL.
 * Return:
 *  0 on success, or number of bytes required for query name if 'query_name'
 *  string buffer was too small to contain it.
 */
static int
_parse_query(const char* query,
             char* query_name,
             int query_name_size,
             const char** query_param)
{
    /* Extract query name. */
    const char* qend = strchr(query, ' ');
    if (qend == NULL) {
        qend = query + strlen(query);
    }
    if ((qend - query) >= query_name_size) {
        return qend - query + 1;
    }
    memcpy(query_name, query, qend - query);
    query_name[qend - query] = '\0';

    /* Calculate query parameters pointer (if needed) */
    if (query_param != NULL) {
        if (*qend == ' ') {
            qend++;
        }
        *query_param = (*qend == '\0') ? NULL : qend;
    }

    return 0;
}

/* Appends one string to another, growing the destination string buffer if
 * needed.
 * Param:
 *  str_buffer - Contains pointer to the destination string buffer. Content of
 *      this parameter can be NULL. Note that content of this parameter will
 *      change if string buffer has been reallocated.
 *  str_buf_size - Contains current buffer size of the string, addressed by
 *      'str_buffer' parameter. Note that content of this parameter will change
 *      if string buffer has been reallocated.
 *  str - String to append.
 * Return:
 *  0 on success, or -1 on failure (memory allocation).
 */
static int
_append_string(char** str_buf, size_t* str_buf_size, const char* str)
{
    const size_t offset = (*str_buf != NULL) ? strlen(*str_buf) : 0;
    const size_t append_bytes = strlen(str) + 1;

    /* Make sure these two match. */
    if (*str_buf == NULL) {
        *str_buf_size = 0;
    }

    if ((offset + append_bytes) > *str_buf_size) {
        /* Reallocate string, so it can fit what's being append to it. Note that
         * we reallocate a bit bigger buffer than is needed in order to minimize
         * number of memory allocation calls in case there are more "appends"
         * coming. */
        const size_t required_mem = offset + append_bytes + 256;
        char* new_buf = (char*)realloc(*str_buf, required_mem);
        if (new_buf == NULL) {
            E("%s: Unable to allocate %d bytes for a string",
              __FUNCTION__, required_mem);
            return -1;
        }
        *str_buf = new_buf;
        *str_buf_size = required_mem;
    }
    memcpy(*str_buf + offset, str, append_bytes);

    return 0;
}

/* Represents camera information as a string formatted as follows:
 *  'name=<devname> channel=<num> pix=<format> facing=<direction> framedims=<widh1xheight1,...>\n'
 * Param:
 *  ci - Camera information descriptor to convert into a string.
 *  str - Pointer to the string buffer where to save the converted camera
 *      information descriptor. On entry, content of this parameter can be NULL.
 *      Note that string buffer addressed with this parameter may be reallocated
 *      in this routine, so (if not NULL) it must contain a buffer allocated with
 *      malloc.  The caller is responsible for freeing string buffer returned in
 *      this parameter.
 *  str_size - Contains byte size of the buffer addressed by 'str' parameter.
 * Return:
 *  0 on success, or != 0 on failure.
 */
static int
_camera_info_to_string(const CameraInfo* ci, char** str, size_t* str_size) {
    int res;
    int n;
    char tmp[128];

    /* Append device name. */
    snprintf(tmp, sizeof(tmp), "name=%s ", ci->device_name);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append input channel. */
    snprintf(tmp, sizeof(tmp), "channel=%d ", ci->inp_channel);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append pixel format. */
    snprintf(tmp, sizeof(tmp), "pix=%d ", ci->pixel_format);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append direction. */
    snprintf(tmp, sizeof(tmp), "dir=%s ", ci->direction);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    /* Append supported frame sizes. */
    snprintf(tmp, sizeof(tmp), "framedims=%dx%d",
             ci->frame_sizes[0].width, ci->frame_sizes[0].height);
    res = _append_string(str, str_size, tmp);
    if (res) {
        return res;
    }
    for (n = 1; n < ci->frame_sizes_num; n++) {
        snprintf(tmp, sizeof(tmp), ",%dx%d",
                 ci->frame_sizes[n].width, ci->frame_sizes[n].height);
        res = _append_string(str, str_size, tmp);
        if (res) {
            return res;
        }
    }

    /* Stringified camera properties should end with EOL. */
    return _append_string(str, str_size, "\n");
}

/* Gets camera information matching a display name.
 * Param:
 *  disp_name - Display name to match.
 *  arr - Array of camera informations.
 *  num - Number of elements in the array.
 * Return:
 *  Matching camera information, or NULL if matching camera information for the
 *  given display name has not been found in the array.
 */
static CameraInfo*
_camera_info_get_by_display_name(const char* disp_name, CameraInfo* arr, int num)
{
    int n;
    for (n = 0; n < num; n++) {
        if (!arr[n].in_use && arr[n].display_name != NULL &&
            !strcmp(arr[n].display_name, disp_name)) {
            return &arr[n];
        }
    }
    return NULL;
}

/* Gets camera information matching a device name.
 * Param:
 *  device_name - Device name to match.
 *  arr - Array of camera informations.
 *  num - Number of elements in the array.
 * Return:
 *  Matching camera information, or NULL if matching camera information for the
 *  given device name has not been found in the array.
 */
static CameraInfo*
_camera_info_get_by_device_name(const char* device_name, CameraInfo* arr, int num)
{
    int n;
    for (n = 0; n < num; n++) {
        if (arr[n].device_name != NULL && !strcmp(arr[n].device_name, device_name)) {
            return &arr[n];
        }
    }
    return NULL;
}

/********************************************************************************
 * CameraServiceDesc API
 *******************************************************************************/

/* Initialized webcam emulation record in camera service descriptor.
 * Param:
 *  csd - Camera service descriptor to initialize a record in.
 *  disp_name - Display name of a web camera ('webcam<N>') to use for emulation.
 *  dir - Direction ('back', or 'front') that emulated camera is facing.
 *  ci, ci_cnt - Array of webcam information for enumerated web cameras connected
 *      to the host.
 */
static void
_wecam_setup(CameraServiceDesc* csd,
             const char* disp_name,
             const char* dir,
             CameraInfo* ci,
             int ci_cnt)
{
    /* Find webcam record in the list of enumerated web cameras. */
    CameraInfo* found = _camera_info_get_by_display_name(disp_name, ci, ci_cnt);
    if (found == NULL) {
        W("Camera name '%s' is not found in the list of connected cameras.\n"
          "Use '-webcam-list' emulator option to obtain the list of connected camera names.\n",
          disp_name);
        return;
    }

    /* Save to the camera info array that will be used by the service. */
    memcpy(csd->camera_info + csd->camera_count, found, sizeof(CameraInfo));
    /* This camera is taken. */
    found->in_use = 1;
    /* Update direction parameter. */
    if (csd->camera_info[csd->camera_count].direction != NULL) {
        free(csd->camera_info[csd->camera_count].direction);
    }
    csd->camera_info[csd->camera_count].direction = ASTRDUP(dir);
    D("Camera %d '%s' connected to '%s' facing %s using %.4s pixel format",
      csd->camera_count, csd->camera_info[csd->camera_count].display_name,
      csd->camera_info[csd->camera_count].device_name,
      csd->camera_info[csd->camera_count].direction,
      (const char*)(&csd->camera_info[csd->camera_count].pixel_format));
      csd->camera_count++;
}

/* Initializes camera service descriptor.
 */
static void
_camera_service_init(CameraServiceDesc* csd)
{
    CameraInfo ci[MAX_CAMERA];
    int connected_cnt;

    /* Enumerate camera devices connected to the host. */
    memset(ci, 0, sizeof(CameraInfo) * MAX_CAMERA);
    memset(csd->camera_info, 0, sizeof(CameraInfo) * MAX_CAMERA);
    csd->camera_count = 0;

    /* Lets see if HW config uses web cameras. */
    if (memcmp(android_hw->hw_camera_back, "webcam", 6) &&
        memcmp(android_hw->hw_camera_front, "webcam", 6)) {
        /* Web camera emulation is disabled. Skip enumeration of webcameras. */
        return;
    }

    /* Enumerate web cameras connected to the host. */
    connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
    if (connected_cnt <= 0) {
        /* Nothing is connected - nothing to emulate. */
        return;
    }

    /* Set up back camera emulation. */
    if (!memcmp(android_hw->hw_camera_back, "webcam", 6)) {
        _wecam_setup(csd, android_hw->hw_camera_back, "back", ci, connected_cnt);
    }

    /* Set up front camera emulation. */
    if (!memcmp(android_hw->hw_camera_front, "webcam", 6)) {
        _wecam_setup(csd, android_hw->hw_camera_front, "front", ci, connected_cnt);
    }
}

/* Gets camera information for the given camera device name.
 * Param:
 *  cs - Initialized camera service descriptor.
 *  device_name - Camera's device name to look up the information for.
 * Return:
 *  Camera information pointer on success, or NULL if no camera information has
 *  been found for the given device name.
 */
static CameraInfo*
_camera_service_get_camera_info_by_device_name(CameraServiceDesc* cs,
                                               const char* device_name)
{
    return _camera_info_get_by_device_name(device_name, cs->camera_info,
                                           cs->camera_count);
}

/********************************************************************************
 * Helpers for handling camera client queries
 *******************************************************************************/

/* Formats paload size according to the protocol, and sends it to the client.
 * To simplify endianess handling we convert payload size to an eight characters
 * string, representing payload size value in hexadecimal format.
 * Param:
 *  qc - Qemu client to send the payload size to.
 *  payload_size - Payload size to report to the client.
 */
static void
_qemu_client_reply_payload(QemudClient* qc, size_t payload_size)
{
    char payload_size_str[9];
    snprintf(payload_size_str, sizeof(payload_size_str), "%08zx", payload_size);
    qemud_client_send(qc, (const uint8_t*)payload_size_str, 8);
}

/*
 * Prefixes for replies to camera client queries.
 */

/* Success, no data to send in reply. */
#define OK_REPLY        "ok"
/* Failure, no data to send in reply. */
#define KO_REPLY        "ko"
/* Success, there are data to send in reply. */
#define OK_REPLY_DATA   OK_REPLY ":"
/* Failure, there are data to send in reply. */
#define KO_REPLY_DATA   KO_REPLY ":"

/* Builds and sends a reply to a query.
 * All replies to a query in camera service have a prefix indicating whether the
 * query has succeeded ("ok"), or failed ("ko"). The prefix can be followed by
 * extra data, containing response to the query. In case there are extra data,
 * they are separated from the prefix with a ':' character.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ok_ko - An "ok", or "ko" selector, where 0 is for "ko", and !0 is for "ok".
 *  extra - Optional extra query data. Can be NULL.
 *  extra_size - Extra data size.
 */
static void
_qemu_client_query_reply(QemudClient* qc,
                         int ok_ko,
                         const void* extra,
                         size_t extra_size)
{
    const char* ok_ko_str;
    size_t payload_size;

    /* Make sure extra_size is 0 if extra is NULL. */
    if (extra == NULL && extra_size != 0) {
        W("%s: 'extra' = NULL, while 'extra_size' = %d",
          __FUNCTION__, (int)extra_size);
        extra_size = 0;
    }

    /* Calculate total payload size, and select appropriate 'ok'/'ko' prefix */
    if (extra_size) {
        /* 'extra' size + 2 'ok'/'ko' bytes + 1 ':' separator byte. */
        payload_size = extra_size + 3;
        ok_ko_str = ok_ko ? OK_REPLY_DATA : KO_REPLY_DATA;
    } else {
        /* No extra data: just zero-terminated 'ok'/'ko'. */
        payload_size = 3;
        ok_ko_str = ok_ko ? OK_REPLY : KO_REPLY;
    }

    /* Send payload size first. */
    _qemu_client_reply_payload(qc, payload_size);
    /* Send 'ok[:]'/'ko[:]' next. Note that if there is no extra data, we still
     * need to send a zero-terminator for 'ok'/'ko' string instead of the ':'
     * separator. So, one way or another, the prefix is always 3 bytes. */
    qemud_client_send(qc, (const uint8_t*)ok_ko_str, 3);
    /* Send extra data (if present). */
    if (extra != NULL) {
        qemud_client_send(qc, (const uint8_t*)extra, extra_size);
    }
}

/* Replies query success ("OK") back to the client.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ok_str - An optional string containing query results. Can be NULL.
 */
static void
_qemu_client_reply_ok(QemudClient* qc, const char* ok_str)
{
    _qemu_client_query_reply(qc, 1, ok_str,
                             (ok_str != NULL) ? (strlen(ok_str) + 1) : 0);
}

/* Replies query failure ("KO") back to the client.
 * Param:
 *  qc - Qemu client to send the reply to.
 *  ko_str - An optional string containing reason for failure. Can be NULL.
 */
static void
_qemu_client_reply_ko(QemudClient* qc, const char* ko_str)
{
    _qemu_client_query_reply(qc, 0, ko_str,
                             (ko_str != NULL) ? (strlen(ko_str) + 1) : 0);
}

/********************************************************************************
 * Camera Factory API
 *******************************************************************************/

/* Handles 'list' query received from the Factory client.
 * Response to this query is a string that represents each connected camera in
 * this format: 'name=devname framedims=widh1xheight1,widh2xheight2,widhNxheightN\n'
 * Strings, representing each camera are separated with EOL symbol.
 * Param:
 *  csd, client - Factory serivice, and client.
 * Return:
 *  0 on success, or != 0 on failure.
 */
static int
_factory_client_list_cameras(CameraServiceDesc* csd, QemudClient* client)
{
    int n;
    size_t reply_size = 0;
    char* reply = NULL;

    /* Lets see if there was anything found... */
    if (csd->camera_count == 0) {
        /* No cameras connected to the host. Reply with "\n" */
        _qemu_client_reply_ok(client, "\n");
        return 0;
    }

    /* "Stringify" each camera information into the reply string. */
    for (n = 0; n < csd->camera_count; n++) {
        const int res =
            _camera_info_to_string(csd->camera_info + n, &reply, &reply_size);
        if (res) {
            if (reply != NULL) {
                free(reply);
            }
            _qemu_client_reply_ko(client, "Memory allocation error");
            return res;
        }
    }

    D("%s Replied: %s", __FUNCTION__, reply);
    _qemu_client_reply_ok(client, reply);
    free(reply);

    return 0;
}

/* Handles a message received from the emulated camera factory client.
 * Queries received here are represented as strings:
 *  'list' - Queries list of cameras connected to the host.
 * Param:
 *  opaque - Camera service descriptor.
 *  msg, msglen - Message received from the camera factory client.
 *  client - Camera factory client pipe.
 */
static void
_factory_client_recv(void*         opaque,
                     uint8_t*      msg,
                     int           msglen,
                     QemudClient*  client)
{
    /*
     * Emulated camera factory client queries.
     */

    /* List cameras connected to the host. */
    static const char _query_list[]     = "list";

    CameraServiceDesc* csd = (CameraServiceDesc*)opaque;
    char query_name[64];
    const char* query_param = NULL;

    /* Parse the query, extracting query name and parameters. */
    if (_parse_query((const char*)msg, query_name, sizeof(query_name),
                     &query_param)) {
        E("%s: Invalid format in query '%s'", __FUNCTION__, (const char*)msg);
        _qemu_client_reply_ko(client, "Invalid query format");
        return;
    }

    D("%s Camera factory query '%s'", __FUNCTION__, query_name);

    /* Dispatch the query to an appropriate handler. */
    if (!strcmp(query_name, _query_list)) {
        /* This is a "list" query. */
        _factory_client_list_cameras(csd, client);
    } else {
        E("%s: Unknown camera factory query name in '%s'",
          __FUNCTION__, (const char*)msg);
        _qemu_client_reply_ko(client, "Unknown query name");
    }
}

/* Emulated camera factory client has been disconnected from the service. */
static void
_factory_client_close(void*  opaque)
{
    /* There is nothing to clean up here: factory service is just an alias for
     * the "root" camera service, that doesn't require anything more, than camera
     * dervice descriptor already provides. */
}

/********************************************************************************
 * Camera client API
 *******************************************************************************/

/* Describes an emulated camera client.
 */
typedef struct CameraClient CameraClient;
struct CameraClient
{
    /* Client name.
     *  On Linux this is the name of the camera device.
     *  On Windows this is the name of capturing window.
     */
    char*               device_name;
    /* Input channel to use to connect to the camera. */
    int                 inp_channel;
    /* Camera information. */
    const CameraInfo*   camera_info;
    /* Emulated camera device descriptor. */
    CameraDevice*       camera;
    /* Buffer allocated for video frames.
     * Note that memory allocated for this buffer
     * also contains preview framebuffer. */
    uint8_t*            video_frame;
    /* Preview frame buffer.
     * This address points inside the 'video_frame' buffer. */
    uint16_t*           preview_frame;
    /* Byte size of the videoframe buffer. */
    size_t              video_frame_size;
    /* Byte size of the preview frame buffer. */
    size_t              preview_frame_size;
    /* Pixel format required by the guest. */
    uint32_t            pixel_format;
    /* Frame width. */
    int                 width;
    /* Frame height. */
    int                 height;
    /* Number of pixels in a frame buffer. */
    int                 pixel_num;
    /* Status of video and preview frame cache. */
    int                 frames_cached;
};

/* Frees emulated camera client descriptor. */
static void
_camera_client_free(CameraClient* cc)
{
    /* The only exception to the "read only" rule: we have to mark the camera
     * as being not used when we destroy a service for it. */
    if (cc->camera_info != NULL) {
        ((CameraInfo*)cc->camera_info)->in_use = 0;
    }
    if (cc->camera != NULL) {
        camera_device_close(cc->camera);
    }
    if (cc->video_frame != NULL) {
        free(cc->video_frame);
    }
    if (cc->device_name != NULL) {
        free(cc->device_name);
    }

    AFREE(cc);
}

/* Creates descriptor for a connecting emulated camera client.
 * Param:
 *  csd - Camera service descriptor.
 *  param - Client parameters. Must be formatted as described in comments to
 *      get_token_value routine, and must contain at least 'name' parameter,
 *      identifiying the camera device to create the service for. Also parameters
 *      may contain a decimal 'inp_channel' parameter, selecting the input
 *      channel to use when communicating with the camera device.
 * Return:
 *  Emulated camera client descriptor on success, or NULL on failure.
 */
static CameraClient*
_camera_client_create(CameraServiceDesc* csd, const char* param)
{
    CameraClient* cc;
    CameraInfo* ci;
    int res;
    ANEW0(cc);

    /*
     * Parse parameter string, containing camera client properties.
     */

    /* Pull required device name. */
    if (get_token_value_alloc(param, "name", &cc->device_name)) {
        E("%s: Allocation failure, or required 'name' parameter is missing, or misformed in '%s'",
          __FUNCTION__, param);
        return NULL;
    }

    /* Pull optional input channel. */
    res = get_token_value_int(param, "inp_channel", &cc->inp_channel);
    if (res != 0) {
        if (res == -1) {
            /* 'inp_channel' parameter has been ommited. Use default input
             * channel, which is zero. */
            cc->inp_channel = 0;
        } else {
            E("%s: 'inp_channel' parameter is misformed in '%s'",
              __FUNCTION__, param);
            return NULL;
        }
    }

    /* Get camera info for the emulated camera represented with this service.
     * Array of camera information records has been created when the camera
     * service was enumerating camera devices during the service initialization.
     * By the camera service protocol, camera service clients must first obtain
     * list of enumerated cameras via the 'list' query to the camera service, and
     * then use device name reported in the list to connect to an emulated camera
     * service. So, if camera information for the given device name is not found
     * in the array, we fail this connection due to protocol violation. */
    ci = _camera_service_get_camera_info_by_device_name(csd, cc->device_name);
    if (ci == NULL) {
        E("%s: Cannot find camera info for device '%s'",
          __FUNCTION__, cc->device_name);
        _camera_client_free(cc);
        return NULL;
    }

    /* We can't allow multiple camera services for a single camera device, Lets
     * make sure that there is no client created for this camera. */
    if (ci->in_use) {
        E("%s: Camera device '%s' is in use", __FUNCTION__, cc->device_name);
        _camera_client_free(cc);
        return NULL;
    }

    /* We're done. Set camera in use, and succeed the connection. */
    ci->in_use = 1;
    cc->camera_info = ci;

    D("%s: Camera service is created for device '%s' using input channel %d",
      __FUNCTION__, cc->device_name, cc->inp_channel);

    return cc;
}

/********************************************************************************
 * Camera client queries
 *******************************************************************************/

/* Client has queried conection to the camera.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_connect(CameraClient* cc, QemudClient* qc, const char* param)
{
    if (cc->camera != NULL) {
        /* Already connected. */
        W("%s: Camera '%s' is already connected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is already connected");
        return;
    }

    /* Open camera device. */
    cc->camera = camera_device_open(cc->device_name, cc->inp_channel);
    if (cc->camera == NULL) {
        E("%s: Unable to open camera device '%s'", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Unable to open camera device.");
        return;
    }

    D("%s: Camera device '%s' is now connected", __FUNCTION__, cc->device_name);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried disconection from the camera.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_disconnect(CameraClient* cc,
                                QemudClient* qc,
                                const char* param)
{
    if (cc->camera == NULL) {
        /* Already disconnected. */
        W("%s: Camera '%s' is already disconnected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is not connected");
        return;
    }

    /* Before we can go ahead and disconnect, we must make sure that camera is
     * not capturing frames. */
    if (cc->video_frame != NULL) {
        E("%s: Cannot disconnect camera '%s' while it is not stopped",
          __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not stopped");
        return;
    }

    /* Close camera device. */
    camera_device_close(cc->camera);
    cc->camera = NULL;

    D("Camera device '%s' is now disconnected", cc->device_name);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried the client to start capturing video.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. Parameters for this query must contain a 'dim', and
 *      a 'pix' parameters, where 'dim' must be "dim=<width>x<height>", and 'pix'
 *      must be "pix=<format>", where 'width' and 'height' must be numerical
 *      values for the capturing video frame width, and height, and 'format' must
 *      be a numerical value for the pixel format of the video frames expected by
 *      the client. 'format' must be one of the V4L2_PIX_FMT_XXX values.
 */
static void
_camera_client_query_start(CameraClient* cc, QemudClient* qc, const char* param)
{
    char* w;
    char dim[64];
    int width, height, pix_format;

    /* Sanity check. */
    if (cc->camera == NULL) {
        /* Not connected. */
        E("%s: Camera '%s' is not connected", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not connected");
        return;
    }

    /*
     * Parse parameters.
     */

    if (param == NULL) {
        E("%s: Missing parameters for the query", __FUNCTION__);
        _qemu_client_reply_ko(qc, "Missing parameters for the query");
        return;
    }

    /* Pull required 'dim' parameter. */
    if (get_token_value(param, "dim", dim, sizeof(dim))) {
        E("%s: Invalid or missing 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid or missing 'dim' parameter");
        return;
    }

    /* Pull required 'pix' parameter. */
    if (get_token_value_int(param, "pix", &pix_format)) {
        E("%s: Invalid or missing 'pix' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid or missing 'pix' parameter");
        return;
    }

    /* Parse 'dim' parameter, and get requested frame width and height. */
    w = strchr(dim, 'x');
    if (w == NULL || w[1] == '\0') {
        E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
        return;
    }
    *w = '\0'; w++;
    errno = 0;
    width = strtoi(dim, NULL, 10);
    height = strtoi(w, NULL, 10);
    if (errno) {
        E("%s: Invalid 'dim' parameter in '%s'", __FUNCTION__, param);
        _qemu_client_reply_ko(qc, "Invalid 'dim' parameter");
        return;
    }

    /* After collecting capture parameters lets see if camera has already
     * started, and if so, lets see if parameters match. */
    if (cc->video_frame != NULL) {
        /* Already started. Match capture parameters. */
        if (cc->pixel_format != pix_format ||cc->width != width ||
            cc->height != height) {
            /* Parameters match. Succeed the query. */
            W("%s: Camera '%s' is already started", __FUNCTION__, cc->device_name);
            _qemu_client_reply_ok(qc, "Camera is already started");
        } else {
            /* Parameters don't match. Fail the query. */
            E("%s: Camera '%s' is already started, and parameters don't match:\n"
              "Current %.4s[%dx%d] != requested %.4s[%dx%d]",
              __FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
              cc->width, cc->height, (const char*)&pix_format, width, height);
            _qemu_client_reply_ko(qc,
                "Camera is already started with different capturing parameters");
        }
        return;
    }

    /*
     * Start the camera.
     */

    /* Save capturing parameters. */
    cc->pixel_format = pix_format;
    cc->width = width;
    cc->height = height;
    cc->pixel_num = cc->width * cc->height;
    cc->frames_cached = 0;

    /* Make sure that pixel format is known, and calculate video framebuffer size
     * along the lines. */
    switch (cc->pixel_format) {
        case V4L2_PIX_FMT_YUV420:
        case V4L2_PIX_FMT_YVU420:
        case V4L2_PIX_FMT_NV12:
        case V4L2_PIX_FMT_NV21:
            cc->video_frame_size = (cc->pixel_num * 12) / 8;
            break;

        default:
            E("%s: Unknown pixel format %.4s",
              __FUNCTION__, (char*)&cc->pixel_format);
            _qemu_client_reply_ko(qc, "Pixel format is unknown");
            return;
    }

    /* Make sure that we have a converters between the original camera pixel
     * format and the one that the client expects. Also a converter must exist
     * for the preview window pixel format (RGB32) */
    if (!has_converter(cc->camera_info->pixel_format, cc->pixel_format) ||
        !has_converter(cc->camera_info->pixel_format, V4L2_PIX_FMT_RGB32)) {
        E("%s: No conversion exist between %.4s and %.4s (or RGB32) pixel formats",
          __FUNCTION__, (char*)&cc->camera_info->pixel_format, (char*)&cc->pixel_format);
        _qemu_client_reply_ko(qc, "No conversion exist for the requested pixel format");
        return;
    }

    /* TODO: At the moment camera framework in the emulator requires RGB32 pixel
     * format for preview window. So, we need to keep two framebuffers here: one
     * for the video, and another for the preview window. Watch out when this
     * changes (if changes). */
    cc->preview_frame_size = cc->pixel_num * 4;

    /* Allocate buffer large enough to contain both, video and preview
     * framebuffers. */
    cc->video_frame =
        (uint8_t*)malloc(cc->video_frame_size + cc->preview_frame_size);
    if (cc->video_frame == NULL) {
        E("%s: Not enough memory for framebuffers %d + %d",
          __FUNCTION__, cc->video_frame_size, cc->preview_frame_size);
        _qemu_client_reply_ko(qc, "Out of memory");
        return;
    }

    /* Set framebuffer pointers. */
    cc->preview_frame = (uint16_t*)(cc->video_frame + cc->video_frame_size);

    /* Start the camera. */
    if (camera_device_start_capturing(cc->camera, cc->camera_info->pixel_format,
                                      cc->width, cc->height)) {
        E("%s: Cannot start camera '%s' for %.4s[%dx%d]: %s",
          __FUNCTION__, cc->device_name, (const char*)&cc->pixel_format,
          cc->width, cc->height, strerror(errno));
        free(cc->video_frame);
        cc->video_frame = NULL;
        _qemu_client_reply_ko(qc, "Cannot start the camera");
        return;
    }

    D("%s: Camera '%s' is now started for %.4s[%dx%d]",
      __FUNCTION__, cc->device_name, (char*)&cc->pixel_format, cc->width,
      cc->height);

    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried the client to stop capturing video.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. There are no parameters expected for this query.
 */
static void
_camera_client_query_stop(CameraClient* cc, QemudClient* qc, const char* param)
{
    if (cc->video_frame == NULL) {
        /* Not started. */
        W("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ok(qc, "Camera is not started");
        return;
    }

    /* Stop the camera. */
    if (camera_device_stop_capturing(cc->camera)) {
        E("%s: Cannot stop camera device '%s': %s",
          __FUNCTION__, cc->device_name, strerror(errno));
        _qemu_client_reply_ko(qc, "Cannot stop camera device");
        return;
    }

    free(cc->video_frame);
    cc->video_frame = NULL;

    D("%s: Camera device '%s' is now stopped.", __FUNCTION__, cc->device_name);
    _qemu_client_reply_ok(qc, NULL);
}

/* Client has queried next frame.
 * Param:
 *  cc - Queried camera client descriptor.
 *  qc - Qemu client for the emulated camera.
 *  param - Query parameters. Parameters for this query are formatted as such:
 *          video=<size> preview=<size> whiteb=<red>,<green>,<blue> expcomp=<comp>
 *      where:
 *       - 'video', and 'preview' both must be decimal values, defining size of
 *         requested video, and preview frames respectively. Zero value for any
 *         of these parameters means that this particular frame is not requested.
 *       - whiteb contains float values required to calculate whilte balance.
 *       - expcomp contains a float value required to calculate exposure
 *         compensation.
 */
static void
_camera_client_query_frame(CameraClient* cc, QemudClient* qc, const char* param)
{
    int video_size = 0;
    int preview_size = 0;
    int repeat;
    ClientFrameBuffer fbs[2];
    int fbs_num = 0;
    size_t payload_size;
    uint64_t tick;
    float r_scale = 1.0f, g_scale = 1.0f, b_scale = 1.0f, exp_comp = 1.0f;
    char tmp[256];

    /* Sanity check. */
    if (cc->video_frame == NULL) {
        /* Not started. */
        E("%s: Camera '%s' is not started", __FUNCTION__, cc->device_name);
        _qemu_client_reply_ko(qc, "Camera is not started");
        return;
    }

    /* Pull required parameters. */
    if (get_token_value_int(param, "video", &video_size) ||
        get_token_value_int(param, "preview", &preview_size)) {
        E("%s: Invalid or missing 'video', or 'preview' parameter in '%s'",
          __FUNCTION__, param);
        _qemu_client_reply_ko(qc,
            "Invalid or missing 'video', or 'preview' parameter");
        return;
    }

    /* Pull white balance values. */
    if (!get_token_value(param, "whiteb", tmp, sizeof(tmp))) {
        if (sscanf(tmp, "%g,%g,%g", &r_scale, &g_scale, &b_scale) != 3) {
            D("Invalid value '%s' for parameter 'whiteb'", tmp);
            r_scale = g_scale = b_scale = 1.0f;
        }
    }

    /* Pull exposure compensation. */
    if (!get_token_value(param, "expcomp", tmp, sizeof(tmp))) {
        if (sscanf(tmp, "%g", &exp_comp) != 1) {
            D("Invalid value '%s' for parameter 'whiteb'", tmp);
            exp_comp = 1.0f;
        }
    }

    /* Verify that framebuffer sizes match the ones that the started camera
     * operates with. */
    if ((video_size != 0 && cc->video_frame_size != video_size) ||
        (preview_size != 0 && cc->preview_frame_size != preview_size)) {
        E("%s: Frame sizes don't match for camera '%s':\n"
          "Expected %d for video, and %d for preview. Requested %d, and %d",
          __FUNCTION__, cc->device_name, cc->video_frame_size,
          cc->preview_frame_size, video_size, preview_size);
        _qemu_client_reply_ko(qc, "Frame size mismatch");
        return;
    }

    /*
     * Initialize framebuffer array for frame read.
     */

    if (video_size) {
        fbs[fbs_num].pixel_format = cc->pixel_format;
        fbs[fbs_num].framebuffer = cc->video_frame;
        fbs_num++;
    }
    if (preview_size) {
        /* TODO: Watch out for preview format changes! */
        fbs[fbs_num].pixel_format = V4L2_PIX_FMT_RGB32;
        fbs[fbs_num].framebuffer = cc->preview_frame;
        fbs_num++;
    }

    /* Capture new frame. */
    tick = _get_timestamp();
    repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
                                      r_scale, g_scale, b_scale, exp_comp);

    /* Note that there is no (known) way how to wait on next frame being
     * available, so we could dequeue frame buffer from the device only when we
     * know it's available. Instead we're shooting in the dark, and quite often
     * device will response with EAGAIN, indicating that it doesn't have frame
     * ready. In turn, it means that the last frame we have obtained from the
     * device is still good, and we can reply with the cached frames. The only
     * case when we need to keep trying to obtain a new frame is when frame cache
     * is empty. To prevent ourselves from an indefinite loop in case device got
     * stuck on something (observed with some Microsoft devices) we will limit
     * the loop by 2 second time period (which is more than enough to obtain
     * something from the device) */
    while (repeat == 1 && !cc->frames_cached &&
           (_get_timestamp() - tick) < 2000000LL) {
        /* Sleep for 10 millisec before repeating the attempt. */
        _camera_sleep(10);
        repeat = camera_device_read_frame(cc->camera, fbs, fbs_num,
                                          r_scale, g_scale, b_scale, exp_comp);
    }
    if (repeat == 1 && !cc->frames_cached) {
        /* Waited too long for the first frame. */
        E("%s: Unable to obtain first video frame from the camera '%s' in %d milliseconds: %s.",
          __FUNCTION__, cc->device_name,
          (uint32_t)(_get_timestamp() - tick) / 1000, strerror(errno));
        _qemu_client_reply_ko(qc, "Unable to obtain video frame from the camera");
        return;
    } else if (repeat < 0) {
        /* An I/O error. */
        E("%s: Unable to obtain video frame from the camera '%s': %s.",
          __FUNCTION__, cc->device_name, strerror(errno));
        _qemu_client_reply_ko(qc, strerror(errno));
        return;
    }

    /* We have cached something... */
    cc->frames_cached = 1;

    /*
     * Build the reply.
     */

    /* Payload includes "ok:" + requested video and preview frames. */
    payload_size = 3 + video_size + preview_size;

    /* Send payload size first. */
    _qemu_client_reply_payload(qc, payload_size);

    /* After that send the 'ok:'. Note that if there is no frames sent, we should
     * use prefix "ok" instead of "ok:" */
    if (video_size || preview_size) {
        qemud_client_send(qc, (const uint8_t*)"ok:", 3);
    } else {
        /* Still 3 bytes: zero terminator is required in this case. */
        qemud_client_send(qc, (const uint8_t*)"ok", 3);
    }

    /* After that send video frame (if requested). */
    if (video_size) {
        qemud_client_send(qc, cc->video_frame, video_size);
    }

    /* After that send preview frame (if requested). */
    if (preview_size) {
        qemud_client_send(qc, (const uint8_t*)cc->preview_frame, preview_size);
    }
}

/* Handles a message received from the emulated camera client.
 * Queries received here are represented as strings:
 * - 'connect' - Connects to the camera device (opens it).
 * - 'disconnect' - Disconnexts from the camera device (closes it).
 * - 'start' - Starts capturing video from the connected camera device.
 * - 'stop' - Stop capturing video from the connected camera device.
 * - 'frame' - Queries video and preview frames captured from the camera.
 * Param:
 *  opaque - Camera service descriptor.
 *  msg, msglen - Message received from the camera factory client.
 *  client - Camera factory client pipe.
 */
static void
_camera_client_recv(void*         opaque,
                    uint8_t*      msg,
                    int           msglen,
                    QemudClient*  client)
{
    /*
     * Emulated camera client queries.
     */

    /* Connect to the camera. */
    static const char _query_connect[]    = "connect";
    /* Disconnect from the camera. */
    static const char _query_disconnect[] = "disconnect";
    /* Start video capturing. */
    static const char _query_start[]      = "start";
    /* Stop video capturing. */
    static const char _query_stop[]       = "stop";
    /* Query frame(s). */
    static const char _query_frame[]      = "frame";

    char query_name[64];
    const char* query_param = NULL;
    CameraClient* cc = (CameraClient*)opaque;

    /*
     * Emulated camera queries are formatted as such:
     *  "<query name> [<parameters>]"
     */

    T("%s: Camera client query: '%s'", __FUNCTION__, (char*)msg);
    if (_parse_query((const char*)msg, query_name, sizeof(query_name),
        &query_param)) {
        E("%s: Invalid query '%s'", __FUNCTION__, (char*)msg);
        _qemu_client_reply_ko(client, "Invalid query");
        return;
    }

    /* Dispatch the query to an appropriate handler. */
    if (!strcmp(query_name, _query_frame)) {
        /* A frame is queried. */
        _camera_client_query_frame(cc, client, query_param);
    } else if (!strcmp(query_name, _query_connect)) {
        /* Camera connection is queried. */
        _camera_client_query_connect(cc, client, query_param);
    } else if (!strcmp(query_name, _query_disconnect)) {
        /* Camera disnection is queried. */
        _camera_client_query_disconnect(cc, client, query_param);
    } else if (!strcmp(query_name, _query_start)) {
        /* Start capturing is queried. */
        _camera_client_query_start(cc, client, query_param);
    } else if (!strcmp(query_name, _query_stop)) {
        /* Stop capturing is queried. */
        _camera_client_query_stop(cc, client, query_param);
    } else {
        E("%s: Unknown query '%s'", __FUNCTION__, (char*)msg);
        _qemu_client_reply_ko(client, "Unknown query");
    }
}

/* Emulated camera client has been disconnected from the service. */
static void
_camera_client_close(void* opaque)
{
    CameraClient* cc = (CameraClient*)opaque;

    D("%s: Camera client for device '%s' on input channel %d is now closed",
      __FUNCTION__, cc->device_name, cc->inp_channel);

    _camera_client_free(cc);
}

/********************************************************************************
 * Camera service API
 *******************************************************************************/

/* Connects a client to the camera service.
 * There are two classes of the client that can connect to the service:
 *  - Camera factory that is insterested only in listing camera devices attached
 *    to the host.
 *  - Camera device emulators that attach to the actual camera devices.
 * The distinction between these two classes is made by looking at extra
 * parameters passed in client_param variable. If it's NULL, or empty, the client
 * connects to a camera factory. Otherwise, parameters describe the camera device
 * the client wants to connect to.
 */
static QemudClient*
_camera_service_connect(void*          opaque,
                        QemudService*  serv,
                        int            channel,
                        const char*    client_param)
{
    QemudClient*  client = NULL;
    CameraServiceDesc* csd = (CameraServiceDesc*)opaque;

    D("%s: Connecting camera client '%s'",
      __FUNCTION__, client_param ? client_param : "Factory");
    if (client_param == NULL || *client_param == '\0') {
        /* This is an emulated camera factory client. */
        client = qemud_client_new(serv, channel, client_param, csd,
                                  _factory_client_recv, _factory_client_close,
                                  NULL, NULL);
    } else {
        /* This is an emulated camera client. */
        CameraClient* cc = _camera_client_create(csd, client_param);
        if (cc != NULL) {
            client = qemud_client_new(serv, channel, client_param, cc,
                                      _camera_client_recv, _camera_client_close,
                                      NULL, NULL);
        }
    }

    return client;
}

void
android_camera_service_init(void)
{
    static int _inited = 0;

    if (!_inited) {
        _camera_service_init(&_camera_service_desc);
        QemudService*  serv = qemud_service_register( SERVICE_NAME, 0,
                                                      &_camera_service_desc,
                                                      _camera_service_connect,
                                                      NULL, NULL);
        if (serv == NULL) {
            derror("%s: Could not register '%s' service",
                   __FUNCTION__, SERVICE_NAME);
            return;
        }
        D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME);
    }
}

void
android_list_web_cameras(void)
{
    CameraInfo ci[MAX_CAMERA];
    int connected_cnt;
    int i;

    /* Enumerate camera devices connected to the host. */
    connected_cnt = enumerate_camera_devices(ci, MAX_CAMERA);
    if (connected_cnt <= 0) {
        return;
    }

    printf("List of web cameras connected to the computer:\n");
    for (i = 0; i < connected_cnt; i++) {
        printf(" Camera '%s' is connected to device '%s' on channel %d using pixel format '%.4s'\n",
               ci[i].display_name, ci[i].device_name, ci[i].inp_channel,
               (const char*)&ci[i].pixel_format);
    }
    printf("\n");
}