Thứ Tư, 23 tháng 1, 2008

Undocumented MessageBoxTimeOut function

unit MsgBoxTimeOut;

interface

uses Windows;

const
MB_TIMEDOUT = 32000;

function MessageBoxTimeOut(hWnd: HWND; lpText: PChar; lpCaption: PChar;
uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

function MessageBoxTimeOutA(hWnd: HWND; lpText: PChar; lpCaption: PChar;
uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

function MessageBoxTimeOutW(hWnd: HWND; lpText: PWideChar; lpCaption: PWideChar;
uType: UINT; wLanguageId: WORD; dwMilliseconds: DWORD): Integer; stdcall;

implementation


function MessageBoxTimeOut; external user32 name 'MessageBoxTimeoutA';
function MessageBoxTimeOutA; external user32 name 'MessageBoxTimeoutA';
function MessageBoxTimeOutW; external user32 name 'MessageBoxTimeoutW';

end.
[/code]

Usage:
[code]
uses ..., MsgBoxTimeout;


procedure TForm1.Button1Click(Sender: TObject);
var
iRet: Integer;
iFlags: Integer;
begin
iFlags := MB_OK or MB_SETFOREGROUND or MB_SYSTEMMODAL or MB_ICONINFORMATION;
iRet := MessageBoxTimeout(Application.Handle, 'Test a timeout of 2 seconds.',
'MessageBoxTimeout Test', iFlags, 0, 2000);
//iRet will = 1 (IDOK)
ShowMessage(IntToStr(iRet));

iFlags := MB_YESNO or MB_SETFOREGROUND or MB_SYSTEMMODAL or MB_ICONINFORMATION;
iRet := MessageBoxTimeout(Application.Handle, 'Test a timeout of 5 seconds.',
'MessageBoxTimeout Test', iFlags, 0, 5000);
//iRet = MB_TIMEDOUT if no buttons clicked, otherwise
// iRet will return the value of the button clicked
case iRet of
IDYES: // Pressed Yes button
ShowMessage('Yes');
IDNO: // Pressed the No button
ShowMessage('No');
MB_TIMEDOUT: // MessageBox timed out
ShowMessage('TimedOut');
end;
end;

Changing Left and Right channel volumes separately

Changing Left and Right channel volumes separately:

These two procedures illustrated bellow set the volumes
of right and left channels separately.

Using Waveoutsetvolume from MMsystem you can set the wave out volme.
to do this you have to set the value into a Dword variable:

The 2 low order bytes : volume for the left channel which can be something
between 0 upto 65535

The 2 high order bytes : volume for the right channel which can be sonething
between 0 upto 65535

using these procedures you can set the channels separately:

uses MMsystem

Procedure Left_volume (value : Dword);
var Rvol, temp : Dword;
begin
waveoutgetvolume(WAVE_MAPPER, @temp);//returns the current volume
Rvol := hiword (temp);
asm
shl Rvol, 16
end;
Rvol := Rvol and $ffff0000;
waveoutsetvolume(WAVE_MAPPER, value or Rvol);
end;


Procedure Right_volume (value : Dword);
var Lvol, temp : Dword;
begin
waveoutgetvolume(WAVE_MAPPER, @temp);
Lvol := Loword (temp);
asm
shl value, 16
end;
value := value and $ffff0000;
waveoutsetvolume(WAVE_MAPPER, value or Lvol);
end;


Note for directshow user :
to change the balance you should use "IBasicAudio"

var pBA : IBasicAudio;

pBA.put_Balance(10000); // from -10000 to 10000

Create XP Look for your application

Many application have "XP" themes. Look like MS Office XP. Look like this



To to this in delphi, it very simple. Download XPMenu from here :
http://www.shagrouni.com/download/xpmenu.zip

"XP Menu is a none-visual component that changes the visual aspects of menus, toolbars and many other controls to nearly the same look and feel of MS Office XP. No code required, you do not have to reconstruct menus or toolbars using controls other than those shipped with Delphi."

You can use this component in FREE/SHAREWARE/COMMERCIAL Software. It free :d

A sample example :
Put a menu, popup menu, some Button in form.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, XPMenuUnicode, StdCtrls, Menus;

type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
File1: TMenuItem;
Exit1: TMenuItem;
N1: TMenuItem;
PrintSetup1: TMenuItem;
Print1: TMenuItem;
N2: TMenuItem;
SaveAs1: TMenuItem;
Save1: TMenuItem;
N3: TMenuItem;
Close1: TMenuItem;
Open1: TMenuItem;
New1: TMenuItem;
Edit1: TMenuItem;
Object1: TMenuItem;
Links1: TMenuItem;
N4: TMenuItem;
GoTo1: TMenuItem;
Replace1: TMenuItem;
Find1: TMenuItem;
N5: TMenuItem;
PasteSpecial1: TMenuItem;
Paste1: TMenuItem;
Copy1: TMenuItem;
Cut1: TMenuItem;
N6: TMenuItem;
Repeatcommand1: TMenuItem;
Undo1: TMenuItem;
Button1: TButton;
Button2: TButton;
XPMenu1: TXPMenu;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
XPMenu1.Active := TRUE;
end;

end.


Note : to apply XP themes to new menu item that create in runtime you can use :
XPMenu1.ActivateMenuItem(ThatMenuItem, TRUE);


Some other component that support menu themes :

BarMenu : http://www.bluecave.net/products/barmenus/
TBX : http://www.jrsoftware.org/tb2k.php (use it with they add-on)

Thứ Bảy, 5 tháng 1, 2008

Intelligent Connect in DirectShow

Intelligent Connect

Intelligent Connect is the mechanism the Filter Graph Manager uses to build filter graphs. It consists of several related algorithms that select filters and add them to the filter graph. For application programming, you rarely need to know the details of Intelligent Connect. Read this section if you are having trouble building a certain filter graph and want to troubleshoot the problem, or if you are writing your own filter and want to make it available for automatic graph building.

Intelligent Connect involves the following IGraphBuilder methods:

  • IGraphBuilder::Render
  • IGraphBuilder::AddSourceFilter
  • IGraphBuilder::RenderFile
  • IGraphBuilder::Connect

The Render method builds a subsection of a graph. It starts from an unconnected output pin and works downstream, adding new filters as needed. The starting filter must already be in the graph. At each step, the Render method searches for a filter that can connect to the previous filter. The stream can branch if a connecting filter has multiple output pins. The search stops when every stream has a renderer. If the Render method gets stuck, it might back up and try again, using a different set of filters.

To connect each output pin, the Render method does the following:

  1. If the pin supports the IStreamBuilder interface, the Filter Graph Manager delegates the entire process to the pin's IStreamBuilder::Render method. By exposing this interface, the pin assumes responsibility for building the remainder of the graph, down to the renderer. However, very few pins support this interface.
  2. The Filter Graph Manager tries to use filters that are cached in memory, if any. Throughout the Intelligent Connect process, the Filter Graph Manager may cache filters from earlier steps in the process.
  3. If the filter graph contains any filters with unconnected input pins, the Filter Graph Manager tries them next. You can force the Render method to try a particular filter by adding that filter to the graph before calling Render.
  4. Finally, the Filter Graph Manager searches the registry, using the IFilterMapper2::EnumMatchingFilters method. It tries to match the output pin's preferred media types against media types listed in the registry.

Each filter is registered with a merit, a numerical value that indicates how preferable the filter is, relative to other filters. The EnumMatchingFilters method returns filters in order of merit, with a minimum merit of MERIT_DO_NOT_USE + 1. It ignores filters with a merit of MERIT_DO_NOT_USE or less. Filters are also grouped into categories, defined by GUID. Categories themselves have merit, and the EnumMatchingFilters method ignores any category with a merit of MERIT_DO_NOT_USE or less, even if the filters in that category have higher merit values.

To summarize, the Render method tries filters in the following order:

  1. Use IStreamBuilder.
  2. Try cached filters.
  3. Try filters in the graph.
  4. Look up filters in the registry.

The AddSourceFilter method adds a source filter that can render a specified file. First it looks in the registry and matches against the protocol (such as http://), the file extension, or a set of predetermined check bytes, which are bytes at particular offsets in the file that match certain patterns. For details, see Registering a Custom File Type. Assuming that the method locates an appropriate source filter, it then creates an instance of that filter, adds it to the graph, and calls the filter's IFileSourceFilter::Load method with the file name.

The RenderFile method builds a default playback graph from a file name. Internally, this method uses AddSourceFilter to locate the correct source filter, and Render to build the rest of the graph.

The Connect method connects and output pin to an input pin. This method adds intermediate filters if needed, using a variation of the algorithm described for the Render method:

  1. Try a direct connection between the filters, with no intermediate filters.
  2. Try cached filters.
  3. Try filters in the graph.
  4. Look up filters in the registry.

Understanding the Intelligent Connect

See how the graphbuilder work :

i'll implement how to "connect intelligent" in our Delphi application.


Step 1 : Add Source Filter

var pSource : IBaseFilter;
pGraph.AddSourceFilter('c:\example.avi', 'Source File (Async)', pSource);

There 2 way to connect between filter :

  • Direct Connect : the graph connect directly 2 "pin" & failed if they can't connect
  • Try Connect : the graph connect it directly & if failed the graph automatically find other filter for make right connect.'
Step 2 : Add the renderer Filter
i usually use this function to add any filter to my graph


function AddFilter(pGraph : IGraphBuilder;GUID: TGUID; Name : WideString; out Filter : IBaseFilter): HResult;
var hr : HRESULT;
begin
Result := S_FALSE;
if not Assigned(pGraph) then exit;

hr := CoCreateInstance(GUID, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, Filter);

if SUCCEEDED(hr) then
begin
hr := pGraph.AddFilter(Filter, PWideChar(Name));
if Failed(hr) then Filter._Release;
end;
result := hr;
end;

You'll need to add the VideoRenderer (For video output) & the Default DirectSound Device (for sound output)


hr := AddFilter(Graph, CLSID_VideoRendererDefault, 'Video Renderer', pVideo);
hr := AddFilter(Graph, CLSID_DSoundRender, 'Default DirectSound Device', pAudio);


Step 3 : Connect the source to the other filter

function GetUnconnectedPin(pFilter: IBaseFilter; PinDir: TPinDirection; out ppPin: IPin): HRESULT;
var
pEnum : IEnumPins;
hr : HRESULT;
pPin, pTmp : Ipin;
ThisPinDir : PIN_DIRECTION;
found : boolean;
begin
found := false;
ppPin := nil;
pEnum := nil;
pPin := nil;
hr := pFilter.EnumPins(pEnum);
if failed(hr) then
begin
result := hr;
exit;
end;
while pEnum.Next(1, pPin, NIL) = S_OK do
begin
pPin.QueryDirection(ThisPinDir);
if (ThisPinDir = PinDir) then
begin
pTmp := nil;
hr := pPin.ConnectedTo(pTmp);
if SUCCEEDED(hr) then // Already connected, not the pin we want.
pTmp := nil
else // Unconnected, this is the pin we want.
begin
found := true;
ppPin := pPin;
break;
end;
end;
pPin := nil;
end;
pEnum := nil;
if found then
result := S_OK
else
result := E_FAIL;
end;
var inpin, outpin : IPIN;
if Succeeded(GetUnconnectedPin(pVideo, PINDIR_INPUT, ip)) then
if Succeeded(GetUnconnectedPin(pSource, PINDIR_OUTPUT, op)) then
if Succeeded(Graph.Connect(op, ip)) then
// we have connect the graph like this :
// Source -> AVI Splitter -> Avi Decoder -> Video Renderer


the final step is connect to sound

function ConnectToSound(pGraph : IGraphBuilder; AudioFilter : IBaseFilter) : HResult;
var FL : TFilterList;
// PL : TPinList;
hr : HResult;
i : integer;
iP, oP : IPIN;
pEnum : IEnumPins;
ThisPinDir : PIN_DIRECTION;
pTmp : Ipin;
begin
hr := GetUnconnectedPin(AudioFilter, PINDIR_INPUT, iP);
if Failed(hr) then
begin
result := hr;
exit;
end;
FL := TFilterList.Create(pGraph);
for i := 0 to FL.Count - 1 do
if FL[i] <> AudioFilter then
begin
hr := FL[i].EnumPins(pEnum);
if Windows.Succeeded(hr) then
begin
while pEnum.Next(1, op, NIL) = S_OK do
begin
op.QueryDirection(ThisPinDir);
if (ThisPinDir = PinDir_OUTPUT) then
begin
pTmp := nil;
hr := op.ConnectedTo(pTmp);
if SUCCEEDED(hr) then // Already connected, not the pin we want.
pTmp := nil
else // Unconnected, this is the pin we want.
begin
hr := pGraph.Connect(op, ip);
if Windows.Succeeded(hr) then
begin
FL.Free;
result := hr;
exit;
end
else op := nil;
end;
end;
end;
end;
end;
FL.Free;
result := -1;
end;



call :
ConnectToSound(pGraph, pAudio);

And it done :D

Why use intelligent conenct ?
Use intelligent connect in case we want to connect our own filter or connect in our way. In my work, i use intelligent to connect my audio filter between Audio Decoder & Audio Renderer.

Conclusion
If you don't have anything special you should use the normal way to connect the graph :
+ Add Filter you want to the Graph
+ Call IGraphBuilder.RenderFile

Thứ Sáu, 4 tháng 1, 2008

Programming DirectShow in Delphi

Introduction to DirectShow
Microsoft® DirectShow® is an architecture for streaming media on the Microsoft Windows® platform. DirectShow provides for high-quality capture and playback of multimedia streams. It supports a wide variety of formats, including Advanced Systems Format (ASF), Motion Picture Experts Group (MPEG), Audio-Video Interleaved (AVI), MPEG Audio Layer-3 (MP3), and WAV sound files. It supports capture from digital and analog devices based on the Windows Driver Model (WDM) or Video for Windows. DirectShow is integrated with other DirectX technologies. It automatically detects and uses video and audio acceleration hardware when available, but also supports systems without acceleration hardware.
DirectShow simplifies media playback, format conversion, and capture tasks. At the same time, it provides access to the underlying stream control architecture for applications that require custom solutions. You can also create your own DirectShow components to support new formats or custom effects.
Examples of the types of C++ applications you can write with DirectShow include file players, TV and DVD players, video editing applications, file format converters, audio-video capture applications, encoders and decoders, digital signal processors, and more. In Visual Basic you can create basic playback applications only. This documentation set covers both C++ and Visual Basic topics. Audio-video playback based on managed code is supported through Managed DirectX 9.0 and is not part of DirectShow.
DirectShow is based on the Component Object Model (COM). To write a DirectShow application or component, you must understand COM client programming. For most applications, you do not need to implement your own COM objects. DirectShow provides the components you need. (If you want to extend DirectShow by writing your own components, however, you must implement them as COM objects.)

Supported Formats in DirectShow

DirectShow is an open architecture, which means that it can support any format as long as there are filters to parse and decode it. The filters provided by Microsoft, either as redistributables through DirectShow or as Windows operating system components, provide default support for the following file and compression formats.
Note An asterisk (*) indicates that DirectShow applications must use the Windows Media® Format SDK to support this format. For more information, see the Audio and Video section of the MSDN Library.
File types:
  • Windows Media® Audio (WMA)*
  • Windows Media® Video (WMV)*
  • Advanced Systems Format (ASF)*
  • Motion Picture Experts Group (MPEG)
  • Audio-Video Interleaved (AVI)
  • QuickTime (version 2 and lower)
  • WAV
  • AIFF
  • AU
  • SND
  • MIDI
Compression formats:
  • Windows Media Video*
  • ISO MPEG-4 video version 1.0*
  • Microsoft MPEG-4 version 3*
  • Sipro Labs ACELP*
  • Windows Media Audio*
  • MPEG Audio Layer-3 (MP3) (decompression only)
  • Digital Video (DV)
  • MPEG-1 (decompression only)
  • MJPEG
  • Cinepak
Microsoft does not provide an MPEG-2 decoder. Several DirectShow-compatible hardware and software MPEG-2 decoders are available from third parties.
For information on the availability of particular third-party codecs for redistribution with DirectShow applications, contact the codec manufacturer.
Note : in Windows Vista DVD Decoder Codec are avaiable
In delphi, you can find the delphi header of directshow/directx here :
http://sourceforge.net/projects/delphi-dx9sdk/
or (with DSPack & BaseClasses)
http://www.progdigy.com/files/DSPACK234.zip
You don't need to install dspack. Just add path to it : Tools -> Environment Option -> Library -> Library Path
(Note : in this tutorial i use delphi 7, other delphi version will work correctly)

How to play a file ?
Start a new console project.

A DirectShow application always performs the same basic steps:

  1. Create an instance of the Filter Graph Manager.
  2. Use the Filter Graph Manager to build a filter graph.
  3. Run the graph, causing data to move through the filters.
Start by calling CoInitialize to initialize the COM library:


var hr : HRESULT;
hr := CoInitialize(nil);

if FAILED(hr) then
begin
// Add error-handling code here. (Omitted for clarity.)
Writeln('ERROR - Could not initialize COM library');
Halt;
end;
To keep things simple, this example ignores the return value, but you should always check the HRESULT value from any method call.
Next, call CoCreateInstance to create the Filter Graph Manager:

var pGraph : IGraphBuilder;
// Create the filter graph manager and query for interfaces.
hr := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, pGraph);
if FAILED(hr) then
begin
Writeln('ERROR - Could not create the Filter Graph Manager.');
Halt;
end;

As shown, the class identifier (CLSID) is CLSID_FilterGraph. The Filter Graph Manager is provided by an in-process DLL, so the execution context is CLSCTX_INPROC_SERVER. DirectShow supports the free-threading model, so you can also call CoInitializeEx with the COINIT_MULTITHREADED flag.

The call to CoCreateInstance returns the IGraphBuilder interface, which mostly contains methods for building the filter graph. Two other interfaces are needed for this example:
  • IMediaControl controls streaming. It contains methods for stopping and starting the graph.
  • IMediaEvent has methods for getting events from the Filter Graph Manager. In this example, the interface is used to wait for playback to complete.
Both of these interfaces are exposed by the Filter Graph Manager. Use the returned IGraphBuilder pointer to query for them:

var pControl : IMediaControl;
pEvent : IMediaEvent;
hr := pGraph.QueryInterface(IID_IMediaControl, pControl);
hr := pGraph.QueryInterface(IID_IMediaEvent, pEvent);
Now you can build the filter graph. For file playback, this is done by a single method call:

hr := pGraph.RenderFile('C:\Example.avi', nil);
The IGraphBuilder::RenderFile method builds a filter graph that can play the specified file. The first parameter is the file name, represented as a wide character (2-byte) string. The second parameter is reserved and must equal NULL.
This method can fail if the specified file does not exist, or the file format is not recognized. Assuming that the method succeeds, however, the filter graph is now ready for playback. To run the graph, call the IMediaControl::Run method:

hr := pControl.Run();
When the filter graph runs, data moves through the filters and is rendered as video and audio. Playback occurs on a separate thread. You can wait for playback to complete by calling the IMediaEvent::WaitForCompletion method:

var evCode : integer = 0; pEvent.WaitForCompletion(INFINITE, evCode);
This method blocks until the file is done playing, or until the specified time-out interval elapses. The value INFINITE means the application blocks indefinitely until the file is done playing.
When the application is finished, release the interface pointers and close the COM library:

pControl._Release(); pEvent._Release(); pGraph._Release(); CoUninitialize();
Sample Code
Here is the complete code for the example described in this article:


program DirectShow_Sample;

{$APPTYPE CONSOLE}
uses
DirectShow9,
Windows,
ActiveX;

var pGraph : IGraphBuilder;
pControl : IMediaControl;
pEvent : IMediaEvent;
hr : HRESULT;
evCode : integer = 0;
begin
hr := CoInitialize(nil);

if FAILED(hr) then
begin
Writeln('ERROR - Could not initialize COM library');
Halt;
end;

// Create the filter graph manager and query for interfaces.
hr := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, pGraph);
if FAILED(hr) then
begin
Writeln('ERROR - Could not create the Filter Graph Manager.');
Halt;
end;

hr := pGraph.QueryInterface(IID_IMediaControl, pControl);
hr := pGraph.QueryInterface(IID_IMediaEvent, pEvent);

// Build the graph. IMPORTANT: Change this string to a file on your system.
hr := pGraph.RenderFile('C:\Example.avi', nil);
if SUCCEEDED(hr) then
begin
// Run the graph.
hr := pControl.Run();
if SUCCEEDED(hr) then
begin
// Wait for completion.
pEvent.WaitForCompletion(INFINITE, evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
end;
end;
pControl._Release();
pEvent._Release();
pGraph._Release();
CoUninitialize();
end.