Delphi Component Development
#1
Hello!

I'm currently writing a Component for Delphi's RAD Studio 2010 that will interface with the JSON RPC and provide a component style access to the remote features of XBMC.

Currently, I've implemented an "Active" property, the "Input." series of commands, Ping, and a few other things. I'm on a roll and I'm going to try to slog through most of the basic/easy commands first.

Basically, the idea is that you can either dynamically create a ThttpXBMC object and do "things" that you can do with the JSON interface (retrieve permissions, hit the'up" button, get the current playlist, etc), or just drop a component on your form.

I've been whittling away at this for a while, but recently I've started to dig into this.

The question I have is if whether there is sufficient interest in this for me to make it a "public" project? As in release the source on GitHub or something akin to that.

I'm not very familiar with the process of creating and working collaboratively with people on programming projects, but I'm willing to dive in a learn!

Cheers
Reply
#2
To give an update, I currently have working these functions:

Function AudioLibrary_Clean: Boolean;
Function AudioLibrary_Scan(directory: String): Boolean;
Function AudioLibrary_GetSongs: TXBMCAudioDetailsSongsArray;
// -------------------------GUI Commands------------------------------------------------
Function GUI_SetFullScreen(ChooseToggle:TXBMCToggle): Boolean; Overload;
Function GUI_SetFullScreen(isFullScreen: Boolean): Boolean; Overload;
Procedure GUI_ShowNotification(title: String; iMessage: String); overload;
Procedure GUI_ShowNotification(title: String; iMessage: String;
displaytime: Integer); overload;
Procedure GUI_ShowNotification(title: String; iMessage: String;
image: string); overload;
Procedure GUI_ShowNotification(title: String; iMessage: String;
image: String; displaytime: Integer); overload;

// -------------------------JSONRPC------------------------------------------------
Function JSONRPC_Ping: String;
Function JSONRPC_Permissions: TXBMCPermission;
// -------------------------Input------------------------------------------------
Procedure Input_Back;
Procedure Input_ContextMenu;
Procedure Input_Down;
Procedure Input_Home;
Procedure Input_Info;
Procedure Input_Left;
Procedure Input_Right;
Procedure Input_Select;
Procedure Input_ShowCodec;
Procedure Input_ShowOSD;
Procedure Input_Up;
Procedure Input_SendText(aString: String; done: Boolean);
Procedure Input_ExecuteAction(enums: String); Overload;
Procedure Input_ExecuteAction(enums: iCommands); Overload;

// -------------------------PlayList------------------------------------------------

Function PlayList_GetItems(id: word): TXBMCPlaylist;
Function PlayList_Clear(playlistID: integer): Boolean;
Function PlayList_GetPlayLists:TXBMCPlayListsArray;
Function PlayList_GetProperties(playlistID: integer) : TXBMCPlayListProperty;
Function PlayList_Insert(playListID: Integer; position:integer; filename: string; filetype: TXBMCPathFile):Boolean; Overload;
Function PlayList_Insert(playListID: Integer; position:integer; itemID: integer; idType: TXBMCLibraryIDTypes):Boolean; Overload;
Function PlayList_Add(playListID: Integer; filename: string; filetype: TXBMCPathFile):Boolean; Overload;
Function PlayList_Add(playListID: Integer; itemID: integer; idType: TXBMCLibraryIDTypes):Boolean; Overload;
Function PlayList_Remove(playListID: integer; position:integer):Boolean;
Function PlayList_Swap(playListID: integer; position1,position2:integer):Boolean;

// -------------------------Application------------------------------------------------

Procedure Application_SetVolume(volume: word); Overload;
Procedure Application_SetVolume(IncDec: TXBMCGlobalIncrementDecrement);
Overload;
Function Application_SetMute: Boolean; Overload;
Function Application_SetMute(isMuted: Boolean): Boolean; Overload;
Function Application_Quit: Boolean;
Function Application_GetProperties:TXBMCApplicationProperties;
// -------------------------Player------------------------------------------------

Function Player_GetActivePlayers: TXBMCPlayerInfoArray;
Function Player_GoTo(PlayerId: Integer; GoToConst: TXBMCGoTo): Boolean;
Overload;
Function Player_GoTo(PlayerId: Integer; PlayListPosition: Integer)
: Boolean; Overload;
Function Player_Move(PlayerId: Integer; Direction: TXBMCDirection): Boolean;
Function Player_Getitem(PlayerId: word): TXBMCListItemAll;
Function Player_PlayPause(PlayerId: Integer): Integer; overload;
Function Player_PlayPause(PlayerId: Integer; paused: Boolean): Integer;
Overload;
Function Player_Seek(PlayerId: Integer; percent: Extended): TXBMCSeekTime;
Overload;
Function Player_Seek(PlayerId: Integer; SetTime: TXBMCPLayerPositionTime)
: TXBMCSeekTime; Overload;
Function Player_Seek(PlayerId: Integer; StepType: TXBMCSeekSteps)
: TXBMCSeekTime; Overload;
Function Player_SetPartyMode(PlayerID: Integer; toggle:Boolean):Boolean; Overload;
Function Player_SetPartyMode(PlayerID: Integer; toggle:TXBMCToggle):Boolean; Overload;
Function Player_SetRepeat(PlayerID: integer; repeattype: TXBMCPlayerRepeat): Boolean;
Function Player_SetShuffle(PlayerID: Integer; toggle:Boolean):Boolean; Overload;
Function Player_SetShuffle(PlayerID: Integer; toggle:TXBMCToggle):Boolean; Overload;
Function Player_SetSpeed(PlayerID: Integer; speed:Integer):integer; Overload;
Function Player_SetSpeed(PlayerID: Integer; incdec:TXBMCGlobalIncrementDecrement):integer; Overload;
Function Player_SetSubtitle(PlayerID: Integer; subEnum: TXBMCSubTitleEnums; enabled: boolean): Boolean; Overload;
Function Player_SetSubtitle(PlayerID: Integer; index: Integer; enabled: boolean): Boolean; Overload;
Function Player_Stop(playerID: Integer): Boolean;
Function Player_Zoom(playerID: Integer; inout: TXBMCInOut):Boolean; Overload;
Function Player_Zoom(playerID: Integer; zoomLevel: Integer): Boolean; Overload;
Function Player_Rotate(playerID: Integer; direction: TXBMCRotate):Boolean;
Function Player_GetProperties(playerID: Integer): TXBMCPlayerProperties;
Function Player_SetAudioStream(playerID: Integer; stream: TXBMCNextPrevious) : Boolean; Overload;
Function Player_SetAudioStream(playerID: Integer; stream: integer) : Boolean; Overload;
// -------------------------VideoLibrary------------------------------------------------
Function VideoLibary_Clean: Boolean;
Function System_EjectOpticalDrive: Boolean;
Function System_Hibernate: Boolean;
Function System_Reboot: Boolean;
Function System_Shutdown: Boolean;
Function System_Suspend: Boolean;
Function System_GetProperties: TXBMCSystemProperties;


I'm going to update most of the Procedures to functions to give a rudimentary "success" reply.

So, has this piqued anyones interest yet?
Reply
#3
Well, I've added a carpload of more support.

The lastest changes include:

- HTTP or TCP support (now that I've learned to turn off uTorrent... :-) )
- TCP is provided by a threaded handler that will ensure you get the response to your post/inquiry without any funny business.
- Improved Code (I hit Control D and allowed it to format it for me... )
- The addition (or the start of the addition of) Events triggered by notifications. Specifically, I've got the procedures/event headers written, now I just need to pass data to them when an event is triggered.

I'm thinking that if there is sufficient interest it may be possible to port this to C.... Just sayin...
Reply
#4
Port your API calls to be called in C? So you can make native mobile apps?
Reply
#5
(2013-08-08, 00:33)Robotica Wrote: Port your API calls to be called in C? So you can make native mobile apps?

I was initially thinking that this could be ported to be used as a Component with Embarcadero's RAD environment, however, it would be possible to have this ported over to C to be used mobile apps.

Currently, to handle the networking portion of things I'm using Indy 10 (which is built into Delphi 2010).

However, the way I've written the code, it would be a fairly simple rewrite if indy isn't available.

The best way to explain it would be like this:

(Ignore the lack of declarations)

The user has created XBMC object, filled in the TCP Port, Address and the set tcpActive.

It checks to see if there is a valid connection and if so, it sets things active.

Then you just need to pick what you want to do.

XBMC.System_EjectOpticalDrive;

or if you care if the call was sucessful

Result := XBMC.System.EjectOpticalDrive;


Now, behind the scenes what is happening is this:

The function "System_EjectOpticalDrive" calls a few helper functions to format the JSON call then sends the formatted data off to a function called (rather uncreatively) "Transmit" which then handles everything behind the scenes. It calls a few other helper functions, and such.

However, if you wanted to move to a different platform/communication library, etc, you would just need to replace the function "transmit" with something that handled things behind the scene.

I've tried to keep from using anything too exotic in my code, or anything that is die-hard dependant on the the Windows API. So, *in theory* the majority of the porting may be able to be done by a Delphi to C converter, and then a little bit of manpower.

Anyhoo...
Reply
#6
Hi!

I would be very interested in this project, as I am also working on a Delphi project that can communicate with XBMC.

I also work in Rad Studio 2010, and I've started implementing an XBMC module as a DLL.

I have a question, how does your functions receive the results? I assume they wait synchronously for a perioad of time XBMC to send a response.

Again, it would be very cool to see your project.

Cheers
Reply
#7
(2013-08-08, 08:34)radu122 Wrote: Hi!

I would be very interested in this project, as I am also working on a Delphi project that can communicate with XBMC.

I also work in Rad Studio 2010, and I've started implementing an XBMC module as a DLL.

I have a question, how does your functions receive the results? I assume they wait synchronously for a perioad of time XBMC to send a response.

Again, it would be very cool to see your project.

Cheers

I thought about making it a DLL too. However, I have limited experience with the wrtiting of DLL's and I felt it would be less of a PITA to just write it as a component.


As for how I handle the data returning, it depends on whether you are using TCP or HTTP.

HTTP is very straightforward. You send a command, you get a response; plain and simple.

However, for TCP I have to do things in a bit more of a convoluted fashion.

Sending the data is easy, you just write the JSON data to the port.

However, in order to know what you are getting back I've made use of the "id" JSON tag.

The response to your command with have the same ID as the one you've sent. So, keeping this in mind.


So to send data (and use tcp) you call a function of sorts (eg: System_EjectOpticalDrive)

It stores the appropriate JSON string in a "SuperObject" (which is a JSON hander).

It passes the SuperObject to a function called "Transmit" which then does some checking to see if things are good to go.

It increments a global variable currentID. It converts that to a 16 Chr Hex string and replaces the ID in the JSON SuperObject,

Then, it writes that data to the open TCP port.

It then calls a "WaitforPacket" function, which then sits and waits a default period of time for the packet to arrive.

When a packet shows up in the packet queue which has the right ID, it then returns the response back through and passes it to the original function.



What is this packet queue, you say...

Well, in order to handle TCP communications, when you initialize TCP communications it starts a thread that monitors the port and attempts (through a mangled form of bracket counting currently) to piece together complete JSON objects.

If it detects a complete JSON object with an ID tag, it then adds it to the "Queue". Otherwise, it passes the object to an eventhandler to process it and potentially generate an event.

Speaking of which...

The queue is a TDictionary object, which has built in features for matching a Key/Object pair. Coinkydinkaly that works out perfectly for making a packet queue of sorts.


So, basically, the thread happily goes along it's merry way receiving data and adding things to the queue. If it encounters an ID-less packet it generates an event.

Only when you are expecting things do you check the queue for a response.

(I Really shouldn't answer things when I have this much caffiene and sugar in me... Am I making sense?)

Cheers
Gord
Reply
#8
Seems to work very good.

I was asking about the response, because I don't want my application to freeze until I get a response from XBMC, because I have other events going on in my app. Maybe this could work if your functions were called in separate threads.

Maybe I can help you make a DLL from your component. Smile
Reply
#9
Radu122, I would be interested in a collaboration. I need to do some serious code cleanup at the moment, but...


Currently, during the "waiting for a packet" it does make the proggy pause. There are a few approaches that could be taken to prevent it from happening.

A) Have the client program handle the waiting for a response altogether by providing a notification/event when a packet arrives.
B) Pass the client application Handle to the DLL/Object and allow it to run Application.ProcessMessages while it waits for a packet to come in.

I'm going to see if i can make it run ProcessMessages whilst it waits.

Any thoughts?
Reply
#10
I haven't used Application.ProcessMessages before.
I was thinking also of your A) method, that is similar with your queue that waits for responses based on xbmc id's.

Another approach I was thinking of is runnning the part of the code that uses your xbmc class in a new thread, i.e.:

procedure myClientProc;
begin

if Player_SetAudioStream(1, stream) then
ShowMessage('Success');
else
ShowMessage('Failed');

end;

BeginThread (nil, 0, Addr(myClientProc), nil, 0, idThread);

However, these 2 variants require extra code in the client...
Reply
#11
Hello

I am interested in your code for the reason of sending notifications to xbmc from delphi (7) or rad studio ex7

i have made an delphi server to intercept the play and pause commands from xbmc and send them serial to arduino to control my led strips (main lighting)
i also have an android app who can interact with delphi program to control the lights.

i am beginner at delphi (5 years now)

Hope to hear somthing from you guys

thanks from holland.
Reply
#12
Where can we download the component?
Reply
#13
Hi guys!

I have chatted with Gord by email, and he has sent me his code, and we discussed and changed it a bit.

I will provide a public link with the project, even if it is not bulletproof from bugs.

Gord has done much of the work, I have contributed with the following major things (digging up from old emails):
- Perioadically ping XBMC to verify if it is present. If ping failes, perioadically try to reconnect.
- Also, if XBMC is not opened at startup, the component doesn't freeze. (This applies for the tcp connection)
- If xbmc is not connected, all the functions must return false (or whatever, empty string etc).
- Added events for 'connected' and 'disconnected' in the client program, so the program can know the state of the connection.
- Made the code generally thread friendly (asynchronous), so the program does not wait for a specific xmbc function to execute.
- Solved random bugs in the code (sometimes when trying some new xbmc functions, parameters were specified incorrect or something)


Link to the project: https://www.dropbox.com/home/public

There are 2 projects there: mine and Gord's original (you can find it in the original folder)
Mine also uses the following things (you may have to add them to your project from the "lib" folder):
- a modified superobject class
- my own timer implementation (TimerThread), cause Delphi sucks at timer friendly threads
- a basic garbage collector (just frees up an object)

Quick tutorial (with my modified code):

myXbmcHandler := TXBMC.Create;
myXbmcHandler.tcpClient.Host := hostIP;
myXbmcHandler.tcpClient.port := hostPort;
myXbmcHandler.tcpActive := true;
myXbmcHandler.PlayerOnPlay := OnPlay;
myXbmcHandler. PlayerOnStop := OnStop;
myXbmcHandler.PlayerOnPause := OnPause;
myXbmcHandler.onTCPClientDisconnected := onDisconnected;
myXbmcHandler.onTCPClientConnected := onConnected;
myXbmcHandler.onDebugNotification := onDebug;

...

//
myXbmcHandler.PlayList_Clear(0);
speed := myXbmcHandler.Player_PlayPause(0, true);
result := myXbmcHandler.Player_Open_PlayList(0);
myXbmcHandler.Player_Seek(1, TXbmcSeekSteps.smallbackward);

...


etc... you can implement as many XBMC functions as you'd like.

Sorry for not creating a public GIT project and so on, but this is Gord's project, maybe if he steps in, I haven't heard from him in a year.
Cheers Smile
Reply

Logout Mark Read Team Forum Stats Members Help
Delphi Component Development0