Jump to content
Sign in to follow this  
oberon_dvbsbridge

Plugin SDK and transport stream

Recommended Posts

oberon_dvbsbridge

Goodday,

 

I am trying to get transport stream from DVBViewer using plugin SDK. Unfortunately this task is appeared to be more complicated than I originally thought.

I have written a very simple plugin in C++, which essentially does the following (nothing):

 

extern "C" char* __stdcall Copyright()
{
return "Me";
}

extern "C" char* __stdcall Version()
{
return "0.1";
}

extern "C" char* __stdcall LibTyp()
{
return "Plugin";
}

extern "C" char* __stdcall PluginName()
{
return "test plugin";
}

TRebuildFunc g_RebuildFunc;
HWND g_wnd;

extern "C" void __stdcall SetAppHandle(HWND wnd, TRebuildFunc RebuildFunc)
{
g_RebuildFunc = RebuildFunc;
g_wnd = wnd;
}

extern "C" bool __stdcall Execute(TTuner* PTuner, int** PPluginPids)
{ 
return true;
}

extern "C" void __stdcall PidCallback(TTStream* Stream)
{
}

 

However when I copy resulting dll into Plugins folder of DVBViewer and start it up I see that DVBViewer stops producing the audio/video signal - e.g. there is no video playing on the screen. In my logging I see that Copyright, Version, LibTyp, PluginName, SetAppHandle and Execute function are being called. PidCallback is not called.

If I remove the plugin and start DVBViewer again, then everything is Ok. :blush:

 

I have a feeling that this is something very basic, which I do wrong, but I cannot understand what exactly.

Can someone help me please?

Edited by oberon_dvbsbridge

Share this post


Link to post
oberon_dvbsbridge

It is a pity that knowledgeable people on this board often ignore posts about plugin SDK.

I will start answering my own questions myself as I go forward in hope that it will help others.

 

For the fact that streaming did not want to start when my plugin was loaded was caused by incorrect return value of Execute function. Bool in C/Windows most probably does not match directly on Boolean type in Delphi. The problem was solved by returning 0 instead of true. :rolleyes:

extern "C" bool __stdcall Execute(TTuner* PTuner, int** PPluginPids)
{
return 0;
}

The next issue was to install the transport stream callback. If I did inside the Execute function

SendMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0x11);
SendMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0x12);
SendMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0xFFFF);

the PidCallback function was never called after the first Execute. The transport stream always started coming from the second channel change (second Execute). I have assumed that synchronous SendMessage could not have been processed correctly inside the Execute handler and substituted SendMessage with PostMessage. And - yes - the problem was solved.

So, to set the transport stream filters from Execute handler one must do the following

PostMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0x11);
PostMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0x12);
PostMessage(wnd,WM_DVBVIEWER,MSG_STARTFILTER, 0xFFFF);

However to remove the previously set filters SendMessage should be used to race conditions.

 

Now comes the next issue, which I still do not have the answer for. If I install the filters for audio, video, PMT, PCR, NIT, SDT and PAT packets and save the contents to a file -

extern "C" void __stdcall PidCallback(TTStream* Stream)
{
fwrite(Stream, sizeof(TTStream), 1, stream_file);
}

- the resulting file does not play correctly.

Upon close inspection of file contents I have noticed that saved transport stream packets sometimes are longer by one or two bytes then 188 bytes ^_^ e.g. by jumping with 188 bytes through the file, starting from synchrobyte (0x47) I expect that it will land after each jump on 0x47. However it lands from time to time on another value, while 0x47 is the next byte in file.

Does anyone have an explanation to this process and suggestion on how to avoid it?

 

Thanks in advance.

Share this post


Link to post
Lars_MQ

Sorry, sometimes we have a live outside the DVBViewer, you know :rolleyes:

 

Anyway, here is a demo compilation of all usefull callback variations. It's delphi but should be pretty simple to translate to C.

 

Except the RAW callbacks, it's what I use in the netstreaming plugin.

 

Remember the Transponder callback may return the full transponder but some cards with hardwarefilters (SS2 &Co) only return the PIDs, the DVBViewer (or you) set.

unit uMain;
interface

uses
 Windows;

function Copyright: PChar; stdcall;
function EventMsg(EventType: Integer; Data: Pointer): Integer; stdcall;
function LibTyp: PChar; stdcall;
function PluginName: PChar; stdcall;
procedure SetAppHandle(Handle: THandle; RebuildFunc: pointer); stdcall;
function Version: Pchar; stdcall;

implementation

type
 TMSG_PIDCallback = packed record
Pid: Word;
Callback: Pointer;
Priority: Boolean;
 end;
 PMSG_PIDCallback = ^TMSG_PIDCallback;

 TTransCallData = record
TransCall: Pointer;
Param: DWord;
 end;
 PTransCallData = ^TTransCallData;

 TTStream = record
header: array[0..3] of byte;
data: array[0..183] of byte;
 end;
 PTStream = ^TTStream;

 TTunerType = (ttCable, ttSatellite, ttTerrestrial, ttATSC, ttIPTV);
 TChannelGroup = (cgA, cgB, cgC);
 TTunerLanguage = array[0..2] of Char;

 PTuner = ^TTuner;
 TTuner = record
TunerType: TTunerType;  //It's a byte
Group: TChannelGroup;   //It's a byte
Unused1: Byte;
Flags: Byte;
Frequency: DWord;
SymbolRate: DWord;
LOF: Word; 
PMT: Word;
Volume: Byte;
Unused: Byte;
SatModulation: Byte;
AVFormat: Byte;
FEC: Byte;
Audiochannel: Byte;
Unused3: Word;
Polarity: Byte; 
Unused4: Byte;
Unused5: Word;
Tone: Byte;
EPGFlag: Byte;
DiSEqCValue: Word;
DiSEqC: Byte; 
Language: TTunerLanguage;  // 3 bytes
AudioPID: Word;
NetworkNr: Byte;
Favourite: Byte;
VideoPID: Word;
TransportStreamID: Word;
TelePID: Word;
NetworkID: Word;
SID: Word;
PCRPID: Word;
 end;

const
 WM_DVBVIEWER = $B2C2;
 MSG_VERSION = $1018;
 MSG_ADDTSCALL = $2210;
 MSG_DELTSCALL = $2211;
 MSG_ADDRAWTSCALL = $2214;
 MSG_DELRAWTSCALL = $2215;
 MSG_STARTPIDCALLBACK = $45104;
 MSG_STOPPIDCALLBACK = $45105;

 evUnload = 0;
 evInitComplete = 3;
 evTuneChannel = 999;
 evRemoveChannel = 998;

var
 DVBViewerHandle: THandle;
 NewCallback: Boolean = false;
 CurrentTuner: TTuner;

function Version: Pchar; stdcall;
begin
 result := '1.0.0';
end;

function Copyright: PChar; stdcall;
begin
 result := 'written by Lars';
end;

function LibTyp: PChar; stdcall;
begin
 result := 'Plugin';
end;

procedure Terminate;
begin
 //Do your stuff
end;

procedure Init;
begin
//Do your stuff
end;

function PluginName: PChar; stdcall;
begin
 result := 'Demo callback';
end;

procedure SetAppHandle(Handle: THandle; RebuildFunc: pointer); stdcall;
var
 version: Integer;
begin
 DVBViewerHandle := Handle;
 version := SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_VERSION, 0);
 NewCallback := version > $0306;
end;

function IRQ_Transponder_Callback(Buffer: PChar; Size: DWord): Boolean; stdcall;
begin
 result := True;
 //Do your thing.
end;

function IRQ_Stream_Callback(Stream: PTStream): Boolean; stdcall;
begin
 result := True;
 //Do your thing. You get each time one TS Packet (188byte) of the PIDs you set. It is aligned and checked.
end;

procedure AddTranscall;
var
 c: TTransCallData;
begin
 //Gets all Packets received by the Hardware, no PID filtering (except Cards with hardwarefilters) is done.
 //This callback gets always one TS-Packet (188 byte), they are guarantied to be aligned and checked.
 if not NewCallback then
exit;
 c.TransCall := @IRQ_Transponder_Callback;
 SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_ADDTSCALL, DWord(@C));
end;

procedure CloseTranscall;
var
 c: TTransCallData;
begin
 //Remember always clean up!
 if not NewCallback then
exit;
 c.TransCall := @IRQ_Transponder_Callback;
 SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_DELTSCALL, DWord(@C));
end;

procedure AddFilter(Pid: Word);
var
 PC: TMSG_PIDCallback;
begin
 PC.Pid := Pid;
 PC.Callback := @IRQ_Stream_Callback;
 PC.Priority := false;
 if NewCallback then
SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_STARTPIDCALLBACK, DWord(@PC));
end;

procedure CloseFilter(Pid: Word);
var
 PC: TMSG_PIDCallback;
begin
 // A PID = $1FFF does mean close all PIDs used. 
 PC.Pid := Pid;
 PC.Callback := @IRQ_Stream_Callback;
 if NewCallback then
SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_STOPPIDCALLBACK, DWord(@PC));
end;

function EventMsg(EventType: Integer; Data: Pointer): Integer; stdcall;
begin
 result := 0;
 case EventType of
evUnload:
  Terminate;
evInitComplete:
  init;
evRemoveChannel:
begin
  CloseFilter($1FFF);   //for Demo purposes both types of callback.
  CloseTranscall;
end;
evTuneChannel:
if data<>nil then
  begin  
	Move(data^, CurrentTuner, sizeof(TTuner));
	CloseTranscall;
	addtranscall;
	AddFilter(CurrentTuner.PMT);
	//and so on.
 end
 else
 begin  //no tuner close everything.
   CloseFilter($1FFF);   //for Demo purposes both types of callback.
   CloseTranscall;
 end;
 end;
end;

procedure AddRawTranscall;
var
 c: TTransCallData;
begin
 //This callback gets varying sizes data chunks.
 //It is the raw data delivered by the Hardware before any processing by the DVBViewer.
 if not NewCallback then
exit;
 c.TransCall := @IRQ_Transponder_Callback;
 SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_ADDRAWTSCALL, DWord(@C));
end;

procedure CloseRawTranscall;
var
 c: TTransCallData;
begin
 //Remember always clean up!
 if not NewCallback then
exit;
 c.TransCall := @IRQ_Transponder_Callback;
 SendMessage(DVBViewerHandle, WM_DVBVIEWER, MSG_DELRAWTSCALL, DWord(@C));
end;

end.

Share this post


Link to post
oberon_dvbsbridge

Wow, that's impressive code sample. Big thanks. It makes a lot of things clear.

Just a quick additional question - if hardware has a built-in CI module will the audio and video packets, which I receive on PID and transponder callbacks be encrypted or decrypted? [removed]

 

Thanks a lot.

Share this post


Link to post
Lars_MQ

The CI/CAM of a dvb-card does its work before the stream is send to the DVBViewer, this is done in hardware and is completly transparent for a dvb-app.

Share this post


Link to post
oberon_dvbsbridge

Goodday again

 

I have made quite some progress so far thanks to the useful tips and source code, kindly posted in this thread.

Now I have a small question, which is hopefully easy to answer for guru's of this site ;)

 

Is there a way to start DVBViewer without audio/video rendering and keep it that way until program shuts down? Or, alternatively, is there a way to put DVBViewer programmatically (via COM or plugin API) in such mode?

I also need that this "silent" mode stays after channel changes.

 

Thanks a lot in advance.

Share this post


Link to post
Lars_MQ

start with -c and

a. choose disable av on minimize and minimize the viewer or

b. take a look at the actions.ini. Each entry can be send via postmessage(windowhandle,WM_dvbviewer, MSG_REMOTE, ActionID +100);

c. send it via COM Interface sendcommand.

Share this post


Link to post
oberon_dvbsbridge

Hello. It's me again :blush:

I have spent almost a day yesterday trying to make channel changes working via Rebuild function (up until now I used the COM functions, but would like to see whether pure plugin API is sufficient to minimize the dependency). Unfortunately not too much progress so far. Hence the question.

Is the Rebuild function still in use - can it be used to request the channel change?

I use the following C prototype:

typedef void (__stdcall* TRebuildFunc)(TTuner* ptuner);

Is it correct?

My problem is that when I call this function than it only changes the channel in 1 case out of 10. And usually it does not do anything - e.g. no channel change.

Are there mandatory parameters of TTuner structure, which must be set when requesting channel change? Because I assume that things like TunerType and Frequency are really needed to identify the channel while Favourite is actually optional.

(To clarify - the TTuner structure, which I am using is from the example above and it is correct as the data, which I receive in Execute callback matches actual channel parameters)

 

Thanks in advance.

Share this post


Link to post
Lars_MQ

This function is depreciated and does not work.

Share this post


Link to post
oberon_dvbsbridge

It's a pity. Is there any other way to request a channel change to a particular channel without having to use COM interface?

Or let me rephrase :blush: Is there any way to request a channel change in GE/TT/... versions of DVBViewer?

Share this post


Link to post
Lars_MQ

Via sendmessage (see above)

wparam
MSG_SETCHANNEL = 0x2204;

lparam
PSetChannel (a pointer).

 TSetChannel = packed record
Channel: Integer;	//Channelnr. use this.
ChannelName: PChar;  // channelname try not to use
 end;
 PSetChannel = ^TSetChannel;

This should work. Use the channelNr of this struct and keep the name nil. The caller is responsible for the memory handling so sendmessage is the way to go. :blush:

Share this post


Link to post
oberon_dvbsbridge

Thanks. I really love the support that you guys deliver for your product. It is way better then I used to have up until now with other satellite TV programs.

 

For the channel change - is my assumption correct that channel number is the consequential number of channel description structure in the channels.dat file? E.g. first structure in the file is for channel 0, second one - for channel 1 etc.?

Share this post


Link to post
Lars_MQ

No, now that you ask. It's the numering as seen in the channellist. Only the main channels are numbered and the audiosubchannel (which are stored also as channels in the channels.dat for compatibily reasons) have the same channel number as the main channel. Hmm now it gets ugly. :blush:

 

Maybe you should take the channelname, but this only gives you the first found channel...

 

Maybe another aproach:

 

wparam
 MSG_SETSID = 0x2100;

lparam
PSetServiceID  (a pointer)

 TSetServiceID = packed record
ServiceID: Integer;
Transponder: Integer;
DiSEqC: Byte;
 end;
 PSetServiceID = ^TSetServiceID;

Same rules as above. Transponder = frequency of the ttuner structure. set diseqc to 255. then it searches for the frequency/ServiceID part.

Share this post


Link to post
oberon_dvbsbridge

Under this approach (using MSG_SETSID) how the DVBViewer will set the audio PID? Because, for example, the three different channels in DVBViewer - ZDF (2ch), ZDF (AC3) and ZDF (deu) will get the same Freq/SID combination.

Share this post


Link to post
Lars_MQ

It will set the first found (the mainchannel).

Share this post


Link to post
oberon_dvbsbridge

If I may ask the additional question. I have just learned that TechnoTrend, TechniSatz, FireDTV, Satelco and other DVBViewer-offsprings do not feature any of the API. Is this true? Or the plugin API is still supported (including the TS-callbacks and channel change request mechanisms)?

Share this post


Link to post
CiNcH

There is the differentiation between Pro Limited Editions (DVBSHOP.TVplayer for TechnoTrend/Satelco, TT-Viewer, FireDTV Viewer, TechniSat Edition) and GE. First group IMHO does not feature any API, neither C/Delphi nor COM (maybe TechniSat Edition is somehow different...). As for GE it should at least feature the C/Delphi API but I do not know how well they are synchronized (Pro and GE API's), also with respect to the TS/PID callbacks. GE could be pretty well suited as a streaming source as its primary focus is stability (but lacks CI support unfortunately). Think Lars or Griga know better about current API situation, compatibility a.s.o.

Edited by CiNcH

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...