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 /**called when the video stops*/
021 public void OnLastFrameCapture(SkypeObject obj, byte[] image, int width, int height);
022
023 }
024
025 public Video(int oid, Skype skype) {
026 super(oid,skype);
027 }
028
029 private static final int MODULE_ID = 11;
030
031 public static final int moduleID() {
032 return MODULE_ID;
033 }
034
035 /** Properties of the Video class */
036 public enum PROPERTY {
037
038 /** Video.STATUS, type: STATUS */
039 status(130),
040
041 /** 'errorcode errortext' , type: String */
042 error(131),
043
044 /** space-separated string of tokens, type: String */
045 debuginfo(132),
046
047 /** 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 */
048 dimensions(133),
049
050 /** Indicates whether the video object is streaming webcam video or screensharing session, values: MEDIA_SCREENSHARING, MEDIA_VIDEO <br>, type: MEDIATYPE */
051 media_type(134),
052
053 /** conference id to be able to identify remote/local video in the same call, type: int */
054 convo_id(1104),
055
056 /** device path used by video object, type: String */
057 device_path(1105);
058
059 private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
060
061 static {
062 for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
063 lookup.put(s.getId(), s);
064 }
065
066 private final int id;
067
068 private PROPERTY(int value) {
069 this.id = value;
070 }
071
072 public int getId() { return id; }
073
074 public static PROPERTY get(int code) {
075 return lookup.get(code);
076 }
077
078 public static PROPERTY fromString(String s) {
079 for (PROPERTY p : lookup.values()) {
080 if (p.toString() == s) {
081 return p;
082 }
083 }
084 return null;
085 }
086 }
087
088 public Object GetPropertyAsEnum(int propid) {
089 return PROPERTY.get(propid);
090 }
091
092 public String GetStrProperty(PROPERTY prop) {
093 //check in propcache if so then return
094 if (mPropCache.containsKey(new Integer(prop.id))){
095 String value = (String)mPropCache.get(prop.id);
096 if (value != null && !(value.length() == 0) ){
097 return value;
098 }
099 }
100 //else get from skypekit...
101 GetPropertyRequest request = new GetPropertyRequest(11, mObjectId, prop.id);
102
103 String string = null;
104 GetPropertyResponse r = skype.GetProperty(request);
105 if (r != null){
106 string = r.GetAsString();
107 }
108
109 if (string != null)
110 {
111 mPropCache.put(new Integer(prop.id), string);
112 }
113 return string;
114 }
115
116 public int GetIntProperty(PROPERTY prop) {
117 //check in propcache if so then return
118 if (mPropCache.containsKey(new Integer(prop.id))){
119 int value = ((Integer)mPropCache.get(prop.id)).intValue();
120 if (value != 0){
121 return value;
122 }
123 }
124 //else get from skypekit...
125 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
126
127 Integer integer = null;
128 GetPropertyResponse r = skype.GetProperty(request);
129 if (r != null){
130 integer = r.GetAsInt();
131 }
132
133 if (integer != null)
134 {
135 mPropCache.put(new Integer(prop.id), integer);
136 return integer.intValue();
137 }
138 else
139 {
140 return 0;
141 }
142 }
143
144 public boolean GetBooleanProperty(PROPERTY prop) {
145 //check in propcache if so then return
146 if (mPropCache.containsKey(new Integer(prop.id))){
147 return ((Boolean)mPropCache.get(prop.id)).booleanValue();
148 }
149 //else get from skypekit...
150 GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
151
152 Boolean boolResp = null;
153 GetPropertyResponse r = skype.GetProperty(request);
154 if (r != null){
155 boolResp = r.GetAsBoolean();
156 }
157
158 if (boolResp != null)
159 {
160 mPropCache.put(new Integer(prop.id), boolResp);
161 return boolResp.booleanValue();
162 }
163 else
164 {
165 return false;
166 }
167 }
168
169 public byte [] GetBinProperty(PROPERTY prop) {
170 //get from skypekit...
171 GetPropertyRequest request = new GetPropertyRequest(11, mObjectId, prop.id);
172
173 byte [] data = null;
174 GetPropertyResponse r = skype.GetProperty(request);
175 if (r != null) {
176 data = r.GetAsBinary();
177 }
178 return data;
179 }
180
181 /** 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> */
182 public static final String VIDEO_DEVICE = "Lib/Video/Device";
183
184 /** 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> */
185 public static final String VIDEO_DEVICE_PATH = "Lib/Video/DevicePath";
186
187 /** 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> */
188 public static final String VIDEO_AUTOSEND = "Lib/Video/AutoSend";
189
190 /** 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> */
191 public static final String VIDEO_DISABLE = "*Lib/Video/Disable";
192
193 /** 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> */
194 public static final String VIDEO_RECVPOLICY = "Lib/Video/RecvPolicy";
195
196 /** 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> */
197 public static final String VIDEO_ADVERTPOLICY = "Lib/Video/AdvertPolicy";
198
199 /**
200 */
201 public enum STATUS {
202
203 /** */
204 NOT_AVAILABLE(0),
205
206 /** */
207 AVAILABLE(1),
208
209 /** */
210 STARTING(2),
211
212 /** */
213 REJECTED(3),
214
215 /** */
216 RUNNING(4),
217
218 /** */
219 STOPPING(5),
220
221 /** */
222 PAUSED(6),
223
224 /** */
225 NOT_STARTED(7),
226
227 /** */
228 HINT_IS_VIDEOCALL_RECEIVED(8),
229
230 /** */
231 UNKNOWN(9),
232
233 /** */
234 RENDERING(10),
235
236 /** */
237 CHECKING_SUBSCRIPTION(11),
238
239 /** */
240 SWITCHING_DEVICE(12);
241
242 private static final Map<Integer,STATUS> lookup = new HashMap<Integer,STATUS>();
243
244 static {
245 for(STATUS s : EnumSet.allOf(STATUS.class))
246 lookup.put(s.getId(), s);
247 }
248
249 private final int id;
250
251 private STATUS(int value) {
252 this.id = value;
253 }
254
255 public int getId() { return id; }
256
257 public static STATUS get(int code) {
258 return lookup.get(code);
259 }
260
261 public static STATUS fromString(String s) {
262 for (STATUS p : lookup.values()) {
263 if (p.toString() == s) {
264 return p;
265 }
266 }
267 return null;
268 }
269 }
270
271 /**
272 */
273 public enum MEDIATYPE {
274
275 /** */
276 MEDIA_SCREENSHARING(1),
277
278 /** */
279 MEDIA_VIDEO(0);
280
281 private static final Map<Integer,MEDIATYPE> lookup = new HashMap<Integer,MEDIATYPE>();
282
283 static {
284 for(MEDIATYPE s : EnumSet.allOf(MEDIATYPE.class))
285 lookup.put(s.getId(), s);
286 }
287
288 private final int id;
289
290 private MEDIATYPE(int value) {
291 this.id = value;
292 }
293
294 public int getId() { return id; }
295
296 public static MEDIATYPE get(int code) {
297 return lookup.get(code);
298 }
299
300 public static MEDIATYPE fromString(String s) {
301 for (MEDIATYPE p : lookup.values()) {
302 if (p.toString() == s) {
303 return p;
304 }
305 }
306 return null;
307 }
308 }
309
310 /**
311 */
312 public enum VIDEO_DEVICE_CAPABILITY {
313
314 /** */
315 VIDEOCAP_HQ_CAPABLE(0),
316
317 /** */
318 VIDEOCAP_HQ_CERTIFIED(1),
319
320 /** */
321 VIDEOCAP_REQ_DRIVERUPDATE(2),
322
323 /** */
324 VIDEOCAP_USB_HIGHSPEED(3);
325
326 private static final Map<Integer,VIDEO_DEVICE_CAPABILITY> lookup = new HashMap<Integer,VIDEO_DEVICE_CAPABILITY>();
327
328 static {
329 for(VIDEO_DEVICE_CAPABILITY s : EnumSet.allOf(VIDEO_DEVICE_CAPABILITY.class))
330 lookup.put(s.getId(), s);
331 }
332
333 private final int id;
334
335 private VIDEO_DEVICE_CAPABILITY(int value) {
336 this.id = value;
337 }
338
339 public int getId() { return id; }
340
341 public static VIDEO_DEVICE_CAPABILITY get(int code) {
342 return lookup.get(code);
343 }
344
345 public static VIDEO_DEVICE_CAPABILITY fromString(String s) {
346 for (VIDEO_DEVICE_CAPABILITY p : lookup.values()) {
347 if (p.toString() == s) {
348 return p;
349 }
350 }
351 return null;
352 }
353 }
354
355 /**
356 * @param windowh
357 */
358 public void SetScreen( int windowh) {
359
360 Request request = null;
361 try {
362 request = new XCallRequest(11,1);
363 } catch (IOException e) {
364 e.printStackTrace();
365 if (skype.errorListener != null)
366 skype.errorListener.OnSkypeKitFatalError();
367 }
368 request.addParm('O',0,mObjectId);
369 request.addParm('u',1,windowh);
370
371 skype.XCall((XCallRequest)request);
372 }
373
374 /**
375 *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>
376 */
377 public void Start() {
378
379 Request request = null;
380 try {
381 request = new XCallRequest(11,2);
382 } catch (IOException e) {
383 e.printStackTrace();
384 if (skype.errorListener != null)
385 skype.errorListener.OnSkypeKitFatalError();
386 }
387 request.addParm('O',0,mObjectId);
388
389 skype.XCall((XCallRequest)request);
390 }
391
392 /**
393 *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>
394 */
395 public void Stop() {
396
397 Request request = null;
398 try {
399 request = new XCallRequest(11,3);
400 } catch (IOException e) {
401 e.printStackTrace();
402 if (skype.errorListener != null)
403 skype.errorListener.OnSkypeKitFatalError();
404 }
405 request.addParm('O',0,mObjectId);
406
407 skype.XCall((XCallRequest)request);
408 }
409
410 /**
411 * @return SubmitCaptureRequestResult
412 */
413 public SubmitCaptureRequestResult SubmitCaptureRequest() {
414
415 Request request = null;
416 try {
417 request = new XCallRequest(11,11);
418 } catch (IOException e) {
419 e.printStackTrace();
420 if (skype.errorListener != null)
421 skype.errorListener.OnSkypeKitFatalError();
422 }
423 request.addParm('O',0,mObjectId);
424
425 Response r = skype.XCall((XCallRequest)request);
426
427 if (r.isErrCall())
428 return null;
429
430 SubmitCaptureRequestResult result = new SubmitCaptureRequestResult();
431
432 boolean ret = false;
433 ret = r.GetAsBoolean(1);
434 result.ret = ret;
435
436 int requestId = 0;
437 requestId = r.GetAsInt(2);
438 result.requestId = requestId;
439
440 return result;
441 }
442
443 public class SubmitCaptureRequestResult {
444 public boolean ret;
445 public int requestId;
446 }
447
448 /**
449 *This method has no known effect in current version. <br>
450 * @param x0
451 * @param y0
452 * @param width
453 * @param height
454 * @param monitorNumber
455 * @param windowHandle
456 */
457 public void SetScreenCaptureRectangle( int x0, int y0, int width, int height, int monitorNumber, int windowHandle) {
458
459 Request request = null;
460 try {
461 request = new XCallRequest(11,5);
462 } catch (IOException e) {
463 e.printStackTrace();
464 if (skype.errorListener != null)
465 skype.errorListener.OnSkypeKitFatalError();
466 }
467 request.addParm('O',0,mObjectId);
468 request.addParm('i',1,x0);
469 request.addParm('i',2,y0);
470 request.addParm('u',3,width);
471 request.addParm('u',4,height);
472 request.addParm('i',5,monitorNumber);
473 request.addParm('u',6,windowHandle);
474
475 skype.XCall((XCallRequest)request);
476 }
477
478 /**
479 * @param x0
480 * @param y0
481 * @param width
482 * @param height
483 */
484 public void SetRenderRectangle( int x0, int y0, int width, int height) {
485
486 Request request = null;
487 try {
488 request = new XCallRequest(11,6);
489 } catch (IOException e) {
490 e.printStackTrace();
491 if (skype.errorListener != null)
492 skype.errorListener.OnSkypeKitFatalError();
493 }
494 request.addParm('O',0,mObjectId);
495 request.addParm('i',1,x0);
496 request.addParm('i',2,y0);
497 request.addParm('u',3,width);
498 request.addParm('u',4,height);
499
500 skype.XCall((XCallRequest)request);
501 }
502
503 /**
504 *This method has no effect in current version. <br>
505 * @param mediaType
506 * @param webcamName
507 * @param devicePath
508 * @param updateSetup
509 */
510 public void SelectVideoSource( Video.MEDIATYPE mediaType, String webcamName, String devicePath, boolean updateSetup) {
511
512 Request request = null;
513 try {
514 request = new XCallRequest(11,7);
515 } catch (IOException e) {
516 e.printStackTrace();
517 if (skype.errorListener != null)
518 skype.errorListener.OnSkypeKitFatalError();
519 }
520 request.addParm('O',0,mObjectId);
521 request.addParm('e',1,mediaType.getId());
522 request.addParm('S',2,webcamName);
523 request.addParm('S',3,devicePath);
524 request.addParm('b',4,updateSetup);
525
526 skype.XCall((XCallRequest)request);
527 }
528
529 /**
530 * @return GetCurrentVideoDeviceResult
531 */
532 public GetCurrentVideoDeviceResult GetCurrentVideoDevice() {
533
534 Request request = null;
535 try {
536 request = new XCallRequest(11,10);
537 } catch (IOException e) {
538 e.printStackTrace();
539 if (skype.errorListener != null)
540 skype.errorListener.OnSkypeKitFatalError();
541 }
542 request.addParm('O',0,mObjectId);
543
544 Response r = skype.XCall((XCallRequest)request);
545
546 if (r.isErrCall())
547 return null;
548
549 GetCurrentVideoDeviceResult result = new GetCurrentVideoDeviceResult();
550
551 MEDIATYPE mediatype = null;
552 mediatype = MEDIATYPE.get(r.GetAsInt(1));
553 result.mediatype = mediatype;
554
555 String deviceName = null;
556 deviceName = r.GetAsString(2);
557 result.deviceName = deviceName;
558
559 String devicePath = null;
560 devicePath = r.GetAsString(3);
561 result.devicePath = devicePath;
562
563 return result;
564 }
565
566 public class GetCurrentVideoDeviceResult {
567 public MEDIATYPE mediatype;
568 public String deviceName;
569 public String devicePath;
570 }
571
572
573 }