    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    }