001 package com.skype.api;
002
003 import java.io.IOException;
004 import java.util.*;
005 import com.skype.ipc.*;
006 /**
007 * This class contains basic video control functionality for live conversations with video. Basically, Video objects represent specific Participant's video state in a live Conversation. The Video class can represent both local (outgoing) and remote (incoming) video streams. Note that as of SkypeKit SDK version 3.2, this class no longer handles video rendering in the UI. Currently available SkypeKit runtimes do not support multi-party video. The API however is designed with future compatibility in mind, so the Video class is attached to Participant class rather than Conversation class. Once multi-party video will become available for SkypeKit, the logic should go like this: <br><br>Let there be 4-way live conversation C and participants P1, P2, P3 and P4. P1 is the local user. Remote participants P2 and P3 are capable of sending video. Remote user P4 is not capable of sending video. You would then end up with 4 video objects: V1, V2, V3 and V0. <br><br> - C->P1->V1-> outgoing video stream <br> - C->P2->V2-> incoming video stream 1 <br> - C->P3->V3-> incoming video stream 2 <br> - C->P4-> no video object as participant P4 does not advertise supporting video <br> - V0-> local webcam preview - this is not attached to any particular conversation, however the corresponding video object can be retrieved with Skype.GetPreviewVideo method. <br><br>As getting from a live conversation to running video streams involves three classes, it can be somewhat less than obvious. The basic state transition logic goes like this: <br><br>You start out with a Conversation, that suddenly becomes live <br><br>CONVERSATION.LOCAL_LIVESTATUS = IM_LIVE <br>At this point you have access to participant list of that conversation. The next step will be to catch Partcipant property changes for PARTICIPANT.VIDEO_STATUS to see if any of the people in conversation have Video available. Note that you should not make assumptions on when this availability happens. Remote users may switch their video on-off as they please. <br><br>PARTICIPANT.VIDEO_STATUS = VIDEO_AVAILABLE <br>If you get to VIDEO_AVAILABLE (not necessarily for all Participants), you can retrieve Video object, with Participant.GetVideo method. <br><br>Now you will need to handle Video.STATUS property changes. In case of successful video call, the sequence of Video.STATUS and Participant.VIDEO_STATUS changes for each Participant and Video object should look like this: <br><br> - Video.STATUS = AVAILABLE <br> - Video.STATUS = STARTING <br> - Video.STATUS = CHECKING_SUBSCRIPTION <br> - Video.STATUS = STARTING <br><br>Participant.VIDEO_STATUS = VIDEO_CONNECTING <br> - Video.STATUS = RUNNING <br> - Participant.VIDEO_STATUS = STREAMING <br>Both Video.STATUS == RUNNING and Participant.VIDEO_STATUS == STREAMING are indicative that the video for that particular participant is up and running, and your UI should update itself accordingly. <br><br>NB! Note that it is not enough to check for Video.STATUS property updates. By the time you obtain the Video object in your client, it may already it may already have progressed to a further status. You should always check the status property immediately after obtaining the Video object. <br>
008 */
009
010
011 public class Video extends SkypeObject {
012
013
014 public interface VideoListener {
015 /** This event gets called when there are changes to Video properties defined in Video.PROPERTY */
016 public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
017
018 public void OnCaptureRequestCompleted(SkypeObject obj, int requestId, boolean isSuccessful, byte[] image, int width, int height);
019
020 }
021
022 public Video(int oid, Skype skype) {
023 super(oid,skype);
024 }
025
026 private static final int MODULE_ID = 11;
027
028 public static final int moduleID() {
029 return MODULE_ID;
030 }
031
032 /** Properties of the Video class */
033 public enum PROPERTY {
034
035 /** Video.STATUS, type: STATUS */
036 status(130),
037
038 /** 'errorcode errortext' , type: String */
039 error(131),
040
041 /** space-separated string of tokens, type: String */
042 debuginfo(132),
043
044 /** This property does not currently work, always containing an empty string. For desktop video, you can get the frame dimensions from the video frame buffers API instead - the buffer struct retrieved with ipc.getFrame() or ipc.getNewFrame() has width and height fields, which you can then use in your UI. With RTP video solutions, you already have the frame sizes in your videohost code. Communicating these to the UI process is currently up to you. <br>, type: String */
045 dimensions(133),
046
047 /** Indicates whether the video object is streaming webcam video or screensharing session, values: MEDIA_SCREENSHARING, MEDIA_VIDEO <br>, type: MEDIATYPE */
048 media_type(134),
049
050 /** conference id to be able to identify remote/local video in the same call, type: Conversation */
051 convo_id(1104),
052
053 /** device path used by video object, type: String */
054 device_path(1105);
055
056 private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
057
058 static {
059 for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
060 lookup.put(s.getId(), s);
061 }
062
063 private final int id;
064
065 private PROPERTY(int value) {
066 this.id = value;
067 }
068
069 public int getId() { return id; }
070
071 public static PROPERTY get(int code) {
072 return lookup.get(code);
073 }
074
075 public static PROPERTY fromString(String s) {
076 for (PROPERTY p : lookup.values()) {
077 if (p.toString() == s) {
078 return p;
079 }
080 }
081 return null;
082 }
083 }
084
085 public Object GetPropertyAsEnum(int propid) {
086 return PROPERTY.get(propid);
087 }
088
089 public String GetStrProperty(PROPERTY prop) {
090 //check in propcache if so then return
091 if (mPropCache.containsKey(new Integer(prop.id))){
092 String value = (String)mPropCache.get(prop.id);
093 if (value != null && !(value.length() == 0) ){
094 return value;
095 }
096 }
097 //else get from skypekit...
098 GetPropertyRequest request = new GetPropertyRequest(11, mObjectId, prop.id);
099
100 String string = null;
101 GetPropertyResponse r = skype.GetProperty(request);
102 if (r != null){
103 string = r.GetAsString();
104 }
105
106 if (string != null)
107 {
108 mPropCache.put(new Integer(prop.id), string);
109 }
110 return string;
111 }
112
113 public int GetIntProperty(PROPERTY prop) {
114 //check in propcache if so then return
115 if (mPropCache.containsKey(new Integer(prop.id))){
116 int value = ((Integer)mPropCache.get(prop.id)).intValue();
117 if (value != 0){
118 return value;
119 }
120 }
121 //else get from skypekit...
122 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
123
124 Integer integer = null;
125 GetPropertyResponse r = skype.GetProperty(request);
126 if (r != null){
127 integer = r.GetAsInt();
128 }
129
130 if (integer != null)
131 {
132 mPropCache.put(new Integer(prop.id), integer);
133 return integer.intValue();
134 }
135 else
136 {
137 return 0;
138 }
139 }
140
141 public boolean GetBooleanProperty(PROPERTY prop) {
142 //check in propcache if so then return
143 if (mPropCache.containsKey(new Integer(prop.id))){
144 return ((Boolean)mPropCache.get(prop.id)).booleanValue();
145 }
146 //else get from skypekit...
147 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
148
149 Boolean boolResp = null;
150 GetPropertyResponse r = skype.GetProperty(request);
151 if (r != null){
152 boolResp = r.GetAsBoolean();
153 }
154
155 if (boolResp != null)
156 {
157 mPropCache.put(new Integer(prop.id), boolResp);
158 return boolResp.booleanValue();
159 }
160 else
161 {
162 return false;
163 }
164 }
165
166 public byte [] GetBinProperty(PROPERTY prop) {
167 //get from skypekit...
168 GetPropertyRequest request = new GetPropertyRequest(11, mObjectId, prop.id);
169
170 byte [] data = null;
171 GetPropertyResponse r = skype.GetProperty(request);
172 if (r != null) {
173 data = r.GetAsBinary();
174 }
175 return data;
176 }
177
178 /** Setupkey SETUPKEY_VIDEO_DEVICE type:string <br>Selected video device name <br>This is account-specific setup key. It can only be used while an account is logged in. <br> */
179 public static final String VIDEO_DEVICE = "Lib/Video/Device";
180
181 /** Setupkey SETUPKEY_VIDEO_DEVICE_PATH type:string <br>Currently selected video device path. <br>This is account-specific setup key. It can only be used while an account is logged in. <br> */
182 public static final String VIDEO_DEVICE_PATH = "Lib/Video/DevicePath";
183
184 /** Setupkey SETUPKEY_VIDEO_AUTOSEND type:int <br>Setting this to 1 starts sending video automatically when call starts <br>This is account-specific setup key. It can only be used while an account is logged in. <br> */
185 public static final String VIDEO_AUTOSEND = "Lib/Video/AutoSend";
186
187 /** Setupkey SETUPKEY_VIDEO_DISABLE type:int <br>Setting this to 1 disables all video functionality. <br>This setup key is machine-specific and affects all local accounts. <br> */
188 public static final String VIDEO_DISABLE = "*Lib/Video/Disable";
189
190 /** Setupkey SETUPKEY_VIDEO_RECVPOLICY type:string default value:"contacts" <br>noone | contacts | callpolicy <br>This is account-specific setup key. It can only be used while an account is logged in. <br> */
191 public static final String VIDEO_RECVPOLICY = "Lib/Video/RecvPolicy";
192
193 /** Setupkey SETUPKEY_VIDEO_ADVERTPOLICY type:string default value:"contacts" <br>noone | contacts | everyone <br>This is account-specific setup key. It can only be used while an account is logged in. <br> */
194 public static final String VIDEO_ADVERTPOLICY = "Lib/Video/AdvertPolicy";
195
196 /**
197 */
198 public enum STATUS {
199
200 /** */
201 NOT_AVAILABLE(0),
202
203 /** */
204 AVAILABLE(1),
205
206 /** */
207 STARTING(2),
208
209 /** */
210 REJECTED(3),
211
212 /** */
213 RUNNING(4),
214
215 /** */
216 STOPPING(5),
217
218 /** */
219 PAUSED(6),
220
221 /** */
222 NOT_STARTED(7),
223
224 /** */
225 HINT_IS_VIDEOCALL_RECEIVED(8),
226
227 /** */
228 UNKNOWN(9),
229
230 /** */
231 RENDERING(10),
232
233 /** */
234 CHECKING_SUBSCRIPTION(11),
235
236 /** */
237 SWITCHING_DEVICE(12);
238
239 private static final Map<Integer,STATUS> lookup = new HashMap<Integer,STATUS>();
240
241 static {
242 for(STATUS s : EnumSet.allOf(STATUS.class))
243 lookup.put(s.getId(), s);
244 }
245
246 private final int id;
247
248 private STATUS(int value) {
249 this.id = value;
250 }
251
252 public int getId() { return id; }
253
254 public static STATUS get(int code) {
255 return lookup.get(code);
256 }
257
258 public static STATUS fromString(String s) {
259 for (STATUS p : lookup.values()) {
260 if (p.toString() == s) {
261 return p;
262 }
263 }
264 return null;
265 }
266 }
267
268 /**
269 */
270 public enum MEDIATYPE {
271
272 /** */
273 MEDIA_SCREENSHARING(1),
274
275 /** */
276 MEDIA_VIDEO(0);
277
278 private static final Map<Integer,MEDIATYPE> lookup = new HashMap<Integer,MEDIATYPE>();
279
280 static {
281 for(MEDIATYPE s : EnumSet.allOf(MEDIATYPE.class))
282 lookup.put(s.getId(), s);
283 }
284
285 private final int id;
286
287 private MEDIATYPE(int value) {
288 this.id = value;
289 }
290
291 public int getId() { return id; }
292
293 public static MEDIATYPE get(int code) {
294 return lookup.get(code);
295 }
296
297 public static MEDIATYPE fromString(String s) {
298 for (MEDIATYPE p : lookup.values()) {
299 if (p.toString() == s) {
300 return p;
301 }
302 }
303 return null;
304 }
305 }
306
307 /**
308 */
309 public enum VIDEO_DEVICE_CAPABILITY {
310
311 /** */
312 VIDEOCAP_HQ_CAPABLE(0),
313
314 /** */
315 VIDEOCAP_HQ_CERTIFIED(1),
316
317 /** */
318 VIDEOCAP_REQ_DRIVERUPDATE(2),
319
320 /** */
321 VIDEOCAP_USB_HIGHSPEED(3);
322
323 private static final Map<Integer,VIDEO_DEVICE_CAPABILITY> lookup = new HashMap<Integer,VIDEO_DEVICE_CAPABILITY>();
324
325 static {
326 for(VIDEO_DEVICE_CAPABILITY s : EnumSet.allOf(VIDEO_DEVICE_CAPABILITY.class))
327 lookup.put(s.getId(), s);
328 }
329
330 private final int id;
331
332 private VIDEO_DEVICE_CAPABILITY(int value) {
333 this.id = value;
334 }
335
336 public int getId() { return id; }
337
338 public static VIDEO_DEVICE_CAPABILITY get(int code) {
339 return lookup.get(code);
340 }
341
342 public static VIDEO_DEVICE_CAPABILITY fromString(String s) {
343 for (VIDEO_DEVICE_CAPABILITY p : lookup.values()) {
344 if (p.toString() == s) {
345 return p;
346 }
347 }
348 return null;
349 }
350 }
351
352 /**
353 * @param windowh
354 */
355 public void SetScreen( int windowh) {
356
357 Request request = null;
358 try {
359 request = new XCallRequest(11,1);
360 } catch (IOException e) {
361 e.printStackTrace();
362 if (skype.errorListener != null)
363 skype.errorListener.OnSkypeKitFatalError();
364 }
365 request.addParm('O',0,mObjectId);
366 request.addParm('u',1,windowh);
367
368 skype.XCall((XCallRequest)request);
369 }
370
371 /**
372 *This method starts either video send or video receive, depending on whether the video object is sender or receiver. In case of desktop video, the receiver side needs to instantiate a renderer object and associate it with the receiveing video (Video.SetRemoteRendererId). <br>
373 */
374 public void Start() {
375
376 Request request = null;
377 try {
378 request = new XCallRequest(11,2);
379 } catch (IOException e) {
380 e.printStackTrace();
381 if (skype.errorListener != null)
382 skype.errorListener.OnSkypeKitFatalError();
383 }
384 request.addParm('O',0,mObjectId);
385
386 skype.XCall((XCallRequest)request);
387 }
388
389 /**
390 *This method stops either video send or video receive, depending on whether the video object is sender or receiver. In case of desktop video, the receiver side needs to dis-associate the video object from the renderer, by calling Video.SetRemoteRendererId(0). <br>
391 */
392 public void Stop() {
393
394 Request request = null;
395 try {
396 request = new XCallRequest(11,3);
397 } catch (IOException e) {
398 e.printStackTrace();
399 if (skype.errorListener != null)
400 skype.errorListener.OnSkypeKitFatalError();
401 }
402 request.addParm('O',0,mObjectId);
403
404 skype.XCall((XCallRequest)request);
405 }
406
407 /**
408 * @return SubmitCaptureRequestResult
409 */
410 public SubmitCaptureRequestResult SubmitCaptureRequest() {
411
412 Request request = null;
413 try {
414 request = new XCallRequest(11,11);
415 } catch (IOException e) {
416 e.printStackTrace();
417 if (skype.errorListener != null)
418 skype.errorListener.OnSkypeKitFatalError();
419 }
420 request.addParm('O',0,mObjectId);
421
422 Response r = skype.XCall((XCallRequest)request);
423
424 if (r.isErrCall())
425 return null;
426
427 SubmitCaptureRequestResult result = new SubmitCaptureRequestResult();
428
429 boolean ret = false;
430 ret = r.GetAsBoolean(1);
431 result.ret = ret;
432
433 int requestId = 0;
434 requestId = r.GetAsInt(2);
435 result.requestId = requestId;
436
437 return result;
438 }
439
440 public class SubmitCaptureRequestResult {
441 public boolean ret;
442 public int requestId;
443 }
444
445 /**
446 *This method has no known effect in current version. <br>
447 * @param x0
448 * @param y0
449 * @param width
450 * @param height
451 * @param monitorNumber
452 * @param windowHandle
453 */
454 public void SetScreenCaptureRectangle( int x0, int y0, int width, int height, int monitorNumber, int windowHandle) {
455
456 Request request = null;
457 try {
458 request = new XCallRequest(11,5);
459 } catch (IOException e) {
460 e.printStackTrace();
461 if (skype.errorListener != null)
462 skype.errorListener.OnSkypeKitFatalError();
463 }
464 request.addParm('O',0,mObjectId);
465 request.addParm('i',1,x0);
466 request.addParm('i',2,y0);
467 request.addParm('u',3,width);
468 request.addParm('u',4,height);
469 request.addParm('i',5,monitorNumber);
470 request.addParm('u',6,windowHandle);
471
472 skype.XCall((XCallRequest)request);
473 }
474
475 /**
476 * @param x0
477 * @param y0
478 * @param width
479 * @param height
480 */
481 public void SetRenderRectangle( int x0, int y0, int width, int height) {
482
483 Request request = null;
484 try {
485 request = new XCallRequest(11,6);
486 } catch (IOException e) {
487 e.printStackTrace();
488 if (skype.errorListener != null)
489 skype.errorListener.OnSkypeKitFatalError();
490 }
491 request.addParm('O',0,mObjectId);
492 request.addParm('i',1,x0);
493 request.addParm('i',2,y0);
494 request.addParm('u',3,width);
495 request.addParm('u',4,height);
496
497 skype.XCall((XCallRequest)request);
498 }
499
500 /**
501 * @param id
502 */
503 public void SetRemoteRendererId( int id) {
504
505 Request request = null;
506 try {
507 request = new XCallRequest(11,14);
508 } catch (IOException e) {
509 e.printStackTrace();
510 if (skype.errorListener != null)
511 skype.errorListener.OnSkypeKitFatalError();
512 }
513 request.addParm('O',0,mObjectId);
514 request.addParm('u',1,id);
515
516 skype.XCall((XCallRequest)request);
517 }
518
519 /**
520 *This method has no effect in current version. <br>
521 * @param mediaType
522 * @param webcamName
523 * @param devicePath
524 * @param updateSetup
525 */
526 public void SelectVideoSource( Video.MEDIATYPE mediaType, String webcamName, String devicePath, boolean updateSetup) {
527
528 Request request = null;
529 try {
530 request = new XCallRequest(11,7);
531 } catch (IOException e) {
532 e.printStackTrace();
533 if (skype.errorListener != null)
534 skype.errorListener.OnSkypeKitFatalError();
535 }
536 request.addParm('O',0,mObjectId);
537 request.addParm('e',1,mediaType.getId());
538 request.addParm('S',2,webcamName);
539 request.addParm('S',3,devicePath);
540 request.addParm('b',4,updateSetup);
541
542 skype.XCall((XCallRequest)request);
543 }
544
545 /**
546 * @return GetCurrentVideoDeviceResult
547 */
548 public GetCurrentVideoDeviceResult GetCurrentVideoDevice() {
549
550 Request request = null;
551 try {
552 request = new XCallRequest(11,10);
553 } catch (IOException e) {
554 e.printStackTrace();
555 if (skype.errorListener != null)
556 skype.errorListener.OnSkypeKitFatalError();
557 }
558 request.addParm('O',0,mObjectId);
559
560 Response r = skype.XCall((XCallRequest)request);
561
562 if (r.isErrCall())
563 return null;
564
565 GetCurrentVideoDeviceResult result = new GetCurrentVideoDeviceResult();
566
567 MEDIATYPE mediatype = null;
568 mediatype = MEDIATYPE.get(r.GetAsInt(1));
569 result.mediatype = mediatype;
570
571 String deviceName = null;
572 deviceName = r.GetAsString(2);
573 result.deviceName = deviceName;
574
575 String devicePath = null;
576 devicePath = r.GetAsString(3);
577 result.devicePath = devicePath;
578
579 return result;
580 }
581
582 public class GetCurrentVideoDeviceResult {
583 public MEDIATYPE mediatype;
584 public String deviceName;
585 public String devicePath;
586 }
587
588
589 }