Help - Search - Members - Calendar
Full Version: problem setting input to a local port
Skype Community > English > Development, Betas and Skype Garage > Skype Public API
alienpenguin
Hi all, i've got a problem using the skype api (skype4com) to change the default input device for a call to a local tcp port.
I am using skype4com 1.0.27.2 (tried 1.0.29.0 too) with skype 3.5.0.239 on windows xp sp3.

I created a simple server that writes on a tcp socket chunks of 320bytes that are 10ms of raw audio samples (16bit, 16KHz, mono) as described in the documentation. When i tell skype to connect to this server port i can indeed see the connection and i am also positive i am writing the packets over the socket but remote party just hears silence. I also write the packets to a file and afterwards i can confirm that the file just contains raw pcm samples at the desired frequency. Any idea of what i am doing wrong and/or suggestion to debug?

thanks

P.S. another server that reads from skype and redirects somewhere else, works flawlessly (so just the audio going TO skype is not heard)
Avo Nappo
Hmm.. basically
This works:
Call.InputDevice[callIoDeviceTypePort] := 1234;
And this doesnt:
Call.OutputDevice[callIoDeviceTypePort] := 1234;

Correct?

Trying to reproduce, stay tuned.
alienpenguin
QUOTE (Avo Nappo @ Thu Jun 26 2008, 16:28)
Go to the original post
Hmm.. basically
This works:
Call.InputDevice[callIoDeviceTypePort] := 1234;
And this doesnt:
Call.OutputDevice[callIoDeviceTypePort] := 1234;

Correct?

Trying to reproduce, stay tuned.


Hi Avo, quite the contrary smile.png
redirecting the input does NOT work (i.e. i am not able to tell skype to get audio FROM a port instead of the mic)
redirecting the output DOES work (i can redirect the audio coming from skype successfully)

thanks
Avo Nappo
> redirecting the input does NOT work (i.e. i am not able to tell skype to get
> audio FROM a port instead of the mic)

No. You see, input/output designation sort of depends on spot in a input -> output chain you imagine yourself being.

What you are thinking as output is like this:
Application output -> TCP socket -> Skype

Whilst the guy who specced the API syntax apparently thought like this:

TCP socket output -> Skype input -> **Skype output** -> Audio Device input
Skype sends stuff out to audio device, hence this is what we call ouput.

Audio source -> **Skype input** -> Skype output -> TCP socket input
Skype gets stuff in from audio source, hence this is what we call input.

Or more simply:
* Input is what Skype gets from local audio device (call's outgoing transmission).
* Output is what Skype sends out to local audio device (call's incoming transmission).

After awhile, this actually starts making sense.
alienpenguin
Hi Avo, probably my knowledge of english does not make me to be clear enough... i think i did understand the skype naming of input/output devices for a call:

when i use call.setInputDevice() i am trying to change the audio source used by skype to gather sound that it will send over the Internet (i.e. default is the microphone)

when i use call.setOutputDevice() i am trying to change the audio sink used by skype to play sound that arrived from the Internet (i.e. default are the speakers)

that stated, i was telling you that:

1)changing the output from speakers to local tcp port works for me (i have a local server that receives the sound skype gathered from the internet and stores it to a file correctly). Hence i am using correctly the setOutputDevice() function.

2) changing input (**skype_input** to use your naming convention smile.png ) to a local tcp port, does not work for me. skype does a local tcp connection but it seems not to send over internet the packets i write from my app to that local tcp port.

thanks smile.png
alienpenguin
Hi Avo, any news on this topic? did you manage to reproduce it?
thanks
Avo Nappo
Sorry it took so long..

> did you manage to reproduce it?

I managed to get something that looks as if it might be a bug.. but apparently not the same case you describe.

I wrote a test app that reads PCM stream from file and then injects it into Skype call's Input or Output.

Expected behaviour would be that if I inject the stream into input, that stream will be audible from remote parrty side of the call. This works.

If I inject tha stream into output, I would get the same noise out from local headset.

This does not work for me and quite possibly is a bug. Workaround in this case would be that if you need to reproduce sound locally, you do not necessarily have to go through Skype API to do it.
alienpenguin
QUOTE (Avo Nappo @ Fri Jul 4 2008, 11:04)
Go to the original post
> Sorry it took so long..

never mind smile.png

>> did you manage to reproduce it?

> I managed to get something that looks as if it might be a bug.. but apparently not the same case you describe.

> I wrote a test app that reads PCM stream from file and then injects it into Skype call's Input or Output.

> Expected behaviour would be that if I inject the stream into input, that stream will be audible from remote parrty side of the call. This works.

and that is what does not work for me... would it be possible to take a look at the sample code you wrote?

> If I inject tha stream into output, I would get the same noise out from local headset.

uhm i dunno if that is a bug, injecting audio into skyeoutput might indeed produce strange results. Anyway redirecting remote party audio to a local file works for me wink.png

Avo Nappo
> would it be possible to take a look at the sample code you wrote?

Sure.

CODE
Unit Unit1;

Interface

Uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  OleCtrls, SKYPE4COMLib_TLB, StdCtrls, ExtCtrls, IdBaseComponent, IdComponent,
  IdCustomTCPServer, IdTCPServer, IdContext, Buttons;

Const Protocol = 8;

Const CallFailed = [clsFailed, clsMissed, clsRefused, clsBusy, clsCancelled];

Const MicPort = 3754;
      OutPort = 3755;

Const FileName = 'c:\test.raw';


Type
  TSoundBuff = Array [1..320] of Byte;
  TFileBuff  = Array of TSoundBuff;


Type
  TState = (ToEnabled, ToDisabled);

Type
  TForm1 = Class(TForm)
    Memo1       : TMemo;
    Panel1: TPanel;
    btnInject: TButton;
    Timer1: TTimer;
    Procedure   FormCreate(Sender: TObject);
    Procedure   SkypeCallStatus(Sender: TObject; const pCall: ICall; Status: TOleEnum);
    Procedure   SkypeAttachmentStatus(Sender: TObject; Status: TOleEnum);
    Procedure   Log (S : String);
    procedure   btnInjectClick(Sender: TObject);
    procedure   Timer1Timer(Sender: TObject);
  Private
    PacketSize  : Integer;
    Skype       : TSkype;
    Call        : ICall;
    OutServer   : TIdTCPServer;
    OutContext  : TIdContext;
    F           : File;
    FileBuff    : TFileBuff;
    FrameCount  : Integer;
    SentFrames  : Integer;
    OutFrame    : TBytes;
    Procedure   SetButtons  (State : TState);
    Procedure   OutServerConnect(AContext: TIdContext);
    Procedure   OutServerDisconnect(AContext: TIdContext);
    Procedure   OutServerExecute(AContext: TIdContext);
    Procedure   ReadRawFile;
  End;



Var
  Form1: TForm1;

Implementation

{$R *.dfm}

//------------------------------------------------------------------------------
// Link this method to Form1's OnCreate property.


Procedure TForm1.SetButtons (State : TState);

Var I : Byte;

Begin
  With Panel1 do
    For I := 0 to ControlCount-1 do
     if Controls[I]is TButton Then
      if State = ToEnabled Then Controls[I].Enabled := True
        Else Controls[I].Enabled := False;
End;


Procedure TForm1.FormCreate(Sender: TObject);

Begin
  Skype := TSkype.Create(Self);

  Skype.OnAttachmentStatus  := SkypeAttachmentStatus;
  Skype.OnCallStatus        := SkypeCallStatus;

  SetLength(OutFrame, 320);
  OutServer               := TIdTCPServer.Create(Self);
  OutServer.DefaultPort   := OutPort;
  OutServer.OnConnect     := OutServerConnect;
  OutServer.OnDisconnect  := OutServerDisconnect;
  OutServer.OnExecute     := OutServerExecute;

  OutServer.Active := True;


  Skype.Attach(Protocol, False);

  Caption := 'port injection test';

  // Prettying up the UI
  Memo1.Align := alClient;
  Memo1.ReadOnly := True;
  Memo1.Clear;
  SetButtons(ToDisabled);
End;


//------------------------------------------------------------------------------
// Fired when Skype call status changes.

Procedure TForm1.SkypeCallStatus(Sender: TObject; const pCall: ICall; Status: TOleEnum);

Begin
  if Status = clsInProgress Then
  Begin
    SetButtons(ToEnabled);
    Call := pCall;
  End
    Else Begin
      SetButtons(ToDisabled);
      Call := Nil;
      PacketSize := 0;
    End;
End;


//------------------------------------------------------------------------------
// Fired when Skype attachment status changes. Note that this handler also
// automatically attempts to reattach to the API if connection was temporarily lost

Procedure TForm1.SkypeAttachmentStatus(Sender: TObject; Status: TOleEnum);

Begin
if Status = apiAttachAvailable Then Skype.Attach(8, False);
Log(Skype.Convert.AttachmentStatusToText(Status));
End;


//------------------------------------------------------------------------------
// Log output to Memo1

Procedure TForm1.Log(S: string);

Begin
  Memo1.Lines.Add(S);
End;



Procedure TForm1.OutServerConnect(AContext: TIdContext);

Begin
  Log('Out has connected to socket ' + IntToStr(OutServer.DefaultPort));
  OutContext := AContext;
  Timer1.Enabled := True;
End;

Procedure TForm1.OutServerDisconnect(AContext: TIdContext);

Begin
  Log('Out has disconnected from socket ' + IntToStr(OutServer.DefaultPort));
End;


Procedure TForm1.ReadRawFile;

Var Bytes, Frames : Cardinal;
    I : Integer;

Begin
  if not FileExists(FileName) Then Exit;
  AssignFile(F, FileName);

  Reset(F, 1);
  Bytes := FileSize(F);
  Frames := Bytes Div 320;
  FrameCount := Frames;

  Memo1.Lines.Add('File contains ' + IntToStr(Bytes) + 'bytes; ' +  IntToStr(Frames) + ' packets');

  SetLength(FileBuff, Frames+1);
  Memo1.Lines.Add(IntToStr(SizeOf(FileBuff)));

  For I := 1 to FrameCount do
  Begin
    // Log('reading: ' + IntToStr(I));
    BlockRead(F, FileBuff[I][1], 320);
  End;

  // Memo1.Lines.Add(IntToStr(FrameCount));

  CloseFile(F);
End;

procedure TForm1.Timer1Timer(Sender: TObject);

begin
  // Log('Sending frame ' + IntToStr(SentFrames));

  Move(FileBuff[SentFrames][1], OutFrame[0], 320);

  if OutContext.Connection.Connected Then
    OutContext.Connection.IOHandler.Write(OutFrame);

  Inc(SentFrames);
  if SentFrames = FrameCount Then Timer1.Enabled := False;
end;

procedure TForm1.btnInjectClick(Sender: TObject);
begin
  Log('reading..');
  ReadRawFile;
  Log('frames: '  + IntToStr(FrameCount));
  if FrameCount = 0 Then Exit;

  SentFrames := 1;
  Log('muting mic input..');
  Call.OutputDevice[callIoDeviceTypeSoundcard] := '';

  {
  Log('output -> port');
  Call.OutputDevice[callIoDeviceTypePort] := IntToStr(OutServer.DefaultPort);
  }
  Log('input -> port');
  Call.InputDevice[callIoDeviceTypePort] := IntToStr(OutServer.DefaultPort);

end;


Procedure TForm1.OutServerExecute(AContext: TIdContext);

Begin
  // Presence of this function is required to make a socket active,
  // However, we do not need to do anything useful here.
End;

End.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.