Setting
up the project
Three simple steps:
- Include vidj.h somewhere in your source code
- Add vidj.lib to your project
- Ensure that vidj.dll is "visible" to
the executable (same folder, PATH env. variable, etc).
You are now ready to use VIDJ at its full extent.
Important: You must call CoInitialize(NULL)
at the very beginning of your application, from the main thread.
Also call CoUninitialize() at the very end, after the
message loop is completed.
Common guidelines
Functions return TRUE on success or FALSE on failure. Use
VIDJ_ErrorGetCode to retrieve the error code for
the last function called. Error codes are stored for each
thread. So if you happen to call 2 or more VIDJ functions
at the same time, they will not interfere with eachother's
error codes.
Initializing
the library
VIDJ_Init
The first thing to do is to call the VIDJ_Init function:
VIDJ_Init (HInstance, hMainWindow, hMasterPreview, lFullScreenDevice, dwOptions);
- hMainWindow is the handle of the main application
window.
- hMasterPreview is the handle of the window that
will be used for preview the mixed video output. Typically
this will be the handle of a panel inside the main window.
The master preview window cannot be changed later, but the
panel itself can be moved or hidden if required.
- lFullScreenDevice is the device that will be used
for full-screen output.
- dwOptions are additional initialization options.
Possible values for lFullScreenDevice are:
- VIDJ_DEVICE_DEFAULT: Same as VIDJ_DEVICE_SECONDARY.
- VIDJ_DEVICE_DISABLEFULLSCREEN: Don't intialize
any full-screen. Only previews will be available.
- VIDJ_DEVICE_PRIMARY: Use the primary display
as full-screen. The full-screen mode can be enabled or disabled
with VIDJ_ShowFullScreen.
- VIDJ_DEVICE_SECONDARY: Use the first secondary
display available. If there's no secodary display, then
the primary display will be used.
- number N: use the N-th secondary display. The primary
screen is 0, the first secondary is 1, the second secondary
is 2 and so on. If the specified display does not exists,
then the behavior will be the same as VID_DEVICE_SECONDARY.
Possible values for dwOptions are:
- VIDJ_INIT_ENABLEDEBUGLOG: log messages and errors
to a log file (vidj.log in the same folder as the main exe)
- VIDJ_INIT_TRIPLEBUFFER: Enables triple-buffer
for video rendering. This may improve the playback smoothness,
but will also use more display memory.
- VIDJ_INIT_SINGLEDEVICEMODE: Forces the 3D engine
to initialize both screens as a single device, as in previous
VIDJ versions (1.x.x). This option should be used for compatibility
tests only, as it will greatly increment the CPU usage.
- (new) VIDJ_INIT_PAUSED:
Initialize the engine but doesn't start the video output
yet. This allows the application to continue its initialization
without the video using CPU. Use the function VIDJ_StartVideoOutput
when you're ready to roll.
If VIDJ_Init call is successfull then you can use
the functions VIDJ_IsFullScreenAvailable, VIDJ_IsFullScreenVisible
and VIDJ_IsSecondaryScreenInUse to gather information
of the resulting screen scenario. When full screen is available
you can show and hide it with the function VIDJ_ShowFullScreen.
Preview panels
Next step (optional), set the window handles that will be
used for video preview on each deck:
VIDJ_DeckSetPreviewWindow (VIDJ_DECKA, hPreviewA);
VIDJ_DeckSetPreviewWindow (VIDJ_DECKB, hPreviewB);
VIDJ_DeckSetPreviewWindow (VIDJ_DECKC, hPreviewC); As with the master preview, hPreviewA,
hPreviewB and hPreviewC will be the handles
of panels inside the main window. Preview windows can be changed
or removed at any time (set to NULL for removing) but is more
efficient to just hide or move the panels.
TIP: Performance can be greatly improved when using dual
screens by increasing the VID_SConfig.dwPreviewFrameSkip
setting (see below).
Configuration
Now, before opening files, you may want to adjust the configuration
parameters. Use the functions VIDJ_SetConfig and
VIDJ_GetConfig. They are pretty self-explained. See
the struct VIDJ_SConfig in vidj.h file for details.
Important: you must set the VIDJ_SConfig.dwBufferLength
value to at least the total ammount of audio data that your
audio engine will read before actually starting playback.
A safe value is 200 ms over your audio buffer lenght, and
a minimum of 300 ms. The default value is 300 ms.
Note: bigger buffer length require more main memory
to allocate the video buffers. The required memory depends
on the clip's properties, but a good estimation is 64 Mb per
each 500 ms of buffer.
Preparing for open files
Before opening any file you should call these two functions:
VIDJ_DeckSetOpenCallback (VIDJ_DECKA, OpenProcedure, dwUserDataA)
VIDJ_DeckSetPositionCallback (VIDJ_DECKA, PositionProcedure, dwUserDataA) Same for deck B and C:
VIDJ_DeckSetOpenCallback (VIDJ_DECKB, OpenProcedure, dwUserDataB)
VIDJ_DeckSetPositionCallback (VIDJ_DECKB, PositionProcedure, dwUserDataB)
VIDJ_DeckSetOpenCallback (VIDJ_DECKC, OpenProcedure, dwUserDataC)
VIDJ_DeckSetPositionCallback (VIDJ_DECKC, PositionProcedure, dwUserDataC) OpenProcedure will be called
during the opening process, keeping you informed on the progress.
This also allows you to abort the opening process:
typedef BOOL (CALLBACK VIDJ_OPENPROC)(float fProgress, DWORD dwProcUser);
fProgress tells you the completion percentage
(from 0.0 to 1.0), so you can show the user a progress bar
or something like. dwProcUser is the user data you
passed to the function VIDJ_DeckSetOpenCallback.
Finally, from this procedure you can return TRUE for letting
the process go or FALSE to abort.
OpenProcedure is called only from inside a VIDJ_DeckOpen
call.
PositionProcedure is very important as it
will allow you to keep the video synchronized with the audio
as you play it throught your audio engine. This callback will
be called very frequently for requesting the current audio
playback state. You must return the position of the song that
is being heard at each time. The video output will then be
played at the same exact rate as the audio.
typedef void (CALLBACK VIDJ_PLAYPROC)(DWORD* pdwPlaybackPosition,
DWORD* pdwPlaybackState,
float* pfPlaybackRate, DWORD dwProcUser); All pointers are guaranteed to be non-NULL.
You must store the appropiate values at the targets:
- Set *pdwPlaybackPosition to the current song's
position in bytes of audio stream.
- Set *pdwPlaybackState to zero if your player is
stopped or paused; set it to non-zero if your player is
running.
- Set *pfPlaybackRate to the current rate at which
you're playing the audio. 1.0 is the original rate.
The last two values are intended to let the decoder know
the rate at which the video frames will be requested, so it
could optimize the decoding (i.e. lower priority when the
player is paused). The PlaybackRate value doesn't need to
be exact.
PositionProcedure is called since VIDJ_DeckOpen
to VIDJ_DeckClose. The call is generated from its
own thread, so no graphic nor other thread-sensitive operations
should be performed inside this procedure.
The VIDJ_TimerUpdate function
This function should be called regularly, always from the
main thread. It handles the internal engine situations that
require an action via the application's main thread. A good
call rate is 5 - 10 times per second.
- Handles all internal automated operations such as background
/ logo changes, layer effects, etc...
- Checks for a "video lost" situation, and restores
all video display surfaces when required. For instance,
a typical "video lost" situation is when the user
presses WINDOWS-L on Windows XP to bring the login window.
When the user returns, VIDJ_TimerUpdate will restore
all video resources and continue video playback normally.
The VIDJ_UPDATEPROC callback
This callback can be set or cleared with the function VIDJ_SetUpdateProc.
When set, the engine calls the callback function once before
each screen's refresh, regardless there are videos opened
or not. This allows to adjust layers, positions, or any other
parameter in a per-frame basis. The call rate will be the
screen's refresh rate.
NOTE: This callback is called from the engine's update
thread. You must have this in mind when using common resources
inside the callback. Also, the screen's refresh won't happen
until the callback exits, so operations here may affect performance.
It's safe to clear the procedure with VIDJ_SetUpdateProc
(NULL, NULL) and then re-set it in order to perform intensive
operations with shared resources.
The following functions can be safely called inside the Update
callback:
VIDJ_LayerSetSurface VIDJ_LayerSetOrigin VIDJ_LayerSetAspectRatio VIDJ_LayerSetText VIDJ_LayerSetTextColor VIDJ_LayerSetShadow VIDJ_LayerSetShadowParams VIDJ_LayerSetOptions VIDJ_LayerSetVisible VIDJ_LayerSetPosition VIDJ_LayerSetAlpha
The following
functions can ONLY be called inside the Update callback:
VIDJ_LayerGetDC VIDJ_LayerReleaseDC Releasing the library
Just call:
VIDJ_Free (); This function closes all open files (if any)
and releases all resources associated with the library.
Opening
and playing files
Opening a media file
Just call:
VIDJ_DeckOpen (VIDJ_DECKA, szFileName, dwOptions); See the vidj.h file for info on the available
options (VIDJ_OPEN_ flags). The PositionProcedure
callback will be called several times during the Open process.
Opening a live input device
Use VIDJ_DeckOpen as abobe but specify "Live://"
as the file name:
VIDJ_DeckOpen (VIDJ_DECKA, "Live://", dwOptions); If multiple devices are present you
can select the one to open by adding its number to the file
name. Thus, "Live://00" opens the first device (default);
"Live://01" opens the second device, and so on.
Each device can be opened in a single deck. Trying to open
an already opened device will return a error.
Opening an internet stream
Use VIDJ_DeckOpen as abobe specifying the stream
full URL as the file name:
VIDJ_DeckOpen (VIDJ_DECKA, "mms://wm.somesite.com/reflector:49850", dwOptions); Note that VIDJ won't interpret Windows
Media playlist files. Some URLs that ends in a .ASX file are
refering to a XML-ASX playlist file which contains the final
Windows Media URL. VIDJ_DeckOpen requires that final URL which
sends the video stream to the user.
ShoutCast audio streams are also supported:
VIDJ_DeckOpen (VIDJ_DECKA, "http://sc.somesite.com:49850", dwOptions); Retrieving the
media format
After the file is successfully open you can retrieve its
format with this function:
VIDJ_DeckGetFormat (VIDJ_DECKA, pFormat); See the vidj.h file for info on the VIDJ_SStreamFormat
structure.
Note: When a video-only file is opened the engine
will provide a 44100-16-st audio silence signal, so the audio
properties of the VIDJ_SStreamFormat will always
be valid. Thus, the VIDJ_DeckRead / VIDJ_DeckSeek
functions can be used without changes with video-only files.
Positioning and reading audio data
To seek to a position inside the file call:
VIDJ_DeckSeek (VIDJ_DECKA, dwPosition); dwPosition is the desired position in
bytes of the audio stream. Check the stream format
to know about the total length in bytes of the song. The position
is automatically rounded down to the nearest sample. After
the function returns you can start reading audio data inmediately.
To read audio data call:
VIDJ_DeckRead (VIDJ_DECKA, pBuffer, dwBufferSize, &dwRead, &dwStartPos, &dwEndPos);
- pBuffer is the byte buffer where the function will
store the audio data.
- dwbufferSize is the size of the buffer, in bytes.
This value is automatically rounded down to the nearest
sample.
- dwRead returns the total amount of data in bytes
written to the buffer. Use NULL if not required.
- dwStartPos returns the stream position in bytes
at the start of the returned block. Use NULL if not required.
- dwEndPos returns the stream position in bytes after
the last byte of the returned block. Use NULL if not required.
Example: a sequence of seeks and reads produces the
following result:
|
dwRead |
dwStartPos |
dwEndPos |
VIDJ_DeckSeek (VIDJ_DECKA, 20000) |
- |
- |
- |
VIDJ_DeckRead (VIDJ_DECKA, pBuffer, 500, &dwRead,
&dwStartPos, &dwEndPos) |
500 |
20000 |
20500 |
VIDJ_DeckRead (VIDJ_DECKA, pBuffer, 500, &dwRead,
&dwStartPos, &dwEndPos) |
500 |
20500 |
21000 |
VIDJ_DeckRead (VIDJ_DECKA, pBuffer, 500, &dwRead,
&dwStartPos, &dwEndPos) |
500 |
21000 |
21500 |
You can call VIDJ_Read just after opening the file.
It will be positioned at the beggining of the stream (0).
Note: You must keep track of the stream position of
the received audio data in order to calculate the actual position
of the song while playback and be able to pass this position
to the PositionProcedure callback. This can be done
by keeping the total audio data read or by storing the dwStartPos
and dwEndPos values.
Note 2: Sometimes the value of dwStartPos returned
by the first read after a seek operation may not be the same
as specified in the VIDJ_DeckSeek function. VIDJ_DeckRead
will return the values just as returned by the video codec,
and some codecs may start decoding audio from a position *near*
the seeked position.
VIDJ_DeckSeek and VIDJ_DeckRead can safely
be called from your own poll thread.
Detecting end of stream
Use the function:
VIDJ_DeckEOS (VIDJ_DECKA); It returns FALSE when a file is opened and the
end has not been reached yet. Returns TRUE in any other case.
When the stream has reached the end then the VIDJ_DeckRead
function will return 0 bytes read.
Mixing video
Set each deck's video output parameters by calling:
VIDJ_DeckSetOutput (VIDJ_DECKA, bShowInMaster, fLevel);
- bShowInMaster tells wether to send the deck's output
to the master or not.
- fLevel can be from 0.0 to 1.0 and represents the
deck's relative video "volume".
When both A and B decks are sent to the master output, their
relative levels are used to determine the "predominance"
of one over the other. For instance, if both decks are sent
to the output and their levels are 1.0 and 1.0, then each
deck is shown at 50%. When only one level is sent to the master
then its level value means the "fade" degree against
the background (1.0 = full visible, 0.0 = completely faded
out).
You can select the master mixing effect anytime by calling:
VIDJ_SetVideoMixEffect (dwEffectId); dwEffectId is one of the VIDJ_EFFECTID_
constants available at the vidj.h file.
Mixing effect only affects Deck A and Deck B. Deck C works
independently. Deck C is always placed over A and B with the
transparency set by its fLevel value.
Closing a file
Just call:
VIDJ_DeckClose (VIDJ_DECKA); Note: when a deck is closed its preview
window stops updating inmediately, so the last displayed frame
will keep visible. After closing you should send a WM_PAINT
message to the preview window to make it repaint itself, thus
clearing the frame.
Retrieving buffer state
You can retrieve the audio and video buffers state anytime
for debugging or information purposes:
VIDJ_DeckGetBufferState (VIDJ_DECKA,
&dwVideoFrames, &dwVideoCapacity,
&dwAudioData, &dwAudioCapacity); dwVideoFrames is the
number of frames actually decoded and ready to be displayed.
dwVideoCapacity is the maximum number of frames that
the video buffer can hold. This number is determined by the
dwBufferLength value of the configuration (VIDJ_SConfig).
The video frames are displayed and the buffer is "advanced"
according to the playback position supplied to the PositionProcedure
callback.
dwAudioData is the amount in bytes of audio data actually
decoded and ready to be served throught the VIDJ_DeckRead
function. dwAudioCapacity is the current size of the
buffer. This size is initially determined by the dwBufferLength
value of the configuration (VIDJ_SConfig), but it dinamically
increases itself if required.
Additional
settings and functions
Aspect ratio adjustment
The members dwAspectRatioX and dwAspectRatioY
of the VIDJ_SConfig struct set the dimensions of the
Master video screen. You can set any standard ratio such as
4:3, 16:9, or any other custom ratio. Even you can set here
the real width and height of the screen using any measure
unit (centimeters, inches...). The numbers themselves are
meaningless, but the ratio between them is used. If any of
the values is zero then the aspect ratio is calculated using
the screen's resolution in pixels (i.e. 1024x768 is 4:3).
The member VIDJ_SConfig.dwAspectRatioMode defines
what to do when screen and picture aspect ratios don't match.
This happens, for instance, when you're trying to play a 4:3
movie in a 16:9 screen, or vice-versa. The availabe modes
are:
- 0 - Auto: (default) chooses between ignore
and shrink-to-frame (below) in order to avoid vertical
black bands.
- 1 - Ignore: no aspect ratio correction. Videos
are stretched to cover the entire screen.
- 2 - Shrink-to-frame: shrinks the picture until
it fits into the screen keeping the picture's aspect ratio.
Top-bottom or left-right black bands may be introduced.
- 3 - Zoom-to-fit: zooms the picture keeping its
aspect ratio until all the screen is filled. Top-down or
left-right portions of the picture may be discarded.
Layout setup (background and logo)
Use the functions VIDJ_SetLayoutConfig and VIDJ_GetLayoutConfig
for setting up the background and logo pictures. The options
are pretty self-explained. See the struct VIDJ_SLayoutConfig
in vidj.h file for details.
You must call VIDJ_TimerUpdate from the application's
main thread in a regular basis in order the changes to have
effect.
Background and logo could be:
- Static images (BPM, DDS, DIB, HDR, JPG, PFM, PNG, PPM,
TGA). Alpha channel is supported.
- Video files (any supported video format, codec-based).
They will be played in a silent endless loop.
- Live input devices. Use the same file format as specified
in "Opening a live input device", below.
Background picture alignment: When using any of the
aspect ratio options for the background picture you can also
force it to be aligned in the way you want. For instance,
if top-down black bars are to be introduced as result of the
aspect ratio correction, then you can force the picture to
align to the upper border instead of keeping in the center
of the bars.
See the VIDJ_LAYER_ defines at the vidj.h file for details.
Ticker text
Ticker text offers two modes for displaying text messages:
scrolling tape or static text. You can set up
all parameters for each mode with the functions VIDJ_SetTickerConfig
and VIDJ_GetTickerConfig. The options are pretty
self-explained. See the struct VIDJ_STickerConfig in
vidj.h file for details.
Showing a text:
VIDJ_StartTicker (dwMode, "Hello world!"); dwMode can be VIDJ_TICKERMODE_STATIC
or VIDJ_TICKERMODE_SCROLLER. The text will be shown according
to the selected mode and its settings configured in VIDJ_SetTickerConfig.
If a text is already being shown then it will be hidden before
showing the new one.
Hidding the text:
VIDJ_StopTicker ( ); You must call VIDJ_TimerUpdate from
the application's main thread in a regular basis in order
the ticker text function to work properly.
Displaying
custom bitmaps
The VIDJ library can show a custom bitmap on a deck instead
of the deck's video output (overlay bitmap). Showing
a bitmap is independent of video playback. Bitmaps can be
shown while video playback and also when no file is open.
Each deck can have its own custom bitmap, which responds properly
to the video output and mixing parameters.
Enabling custom bitmaps
VIDJ_DeckEnableOverlay (VIDJ_DECKA, 300, 216); This tells the library the deck that will be
used for output and the size (width, height) of the source
bitmap, in pixels. At this point the video output for the
selected deck doesn't actually change.
Show or update a custom bitmap
VIDJ_DeckSetOverlayBitmap (VIDJ_DECKA, hBitmap); hBitmap is the handle of the bitmap to
show (HBITMAP type). It must be the same size as specified
in VIDJ_DeckEnableOverlay. Any color format is allowed.
The bitmap is shown the first time this function is called.
Further calls will update the bitmap on screen. It highly
recommended to call this function only when the bitmap changes
in order to avoid overload.
Disable the custom bitmap
VIDJ_DeckDisableOverlay (VIDJ_DECKA); Disables the overaly bitmap for the specified
deck. The deck will show the video output again if a video
file is being played, or the black background is no file is
loaded.
Custom
graphic layers
Understanding layers
A layer is a graphic that is placed at some region of the
screen (rectangle) with a specified opacity. Each layer has
its own Z position, with defines whether its displayed over
or under other layers. Layers are displayed on the Master
video output (both preview and full-screen). The parameters
that define a layer are:
- Position (VIDJ_SRect, floats): left, top,
right, bottom coordinates in the screen. Upper left corner
of the screen is always 0.0, 0.0 and bottom right
corner is 1.0, 1.0. The picture inside the layer
is scaled to fit its rectangle unless aspect-ratio options
are specified (see below). The default position is full-screen
(0,0 - 1,1).
- Opacity or Alpha (float): from 0.0 (fully transparent)
to 1.0 (fully opaque). The picture can also have its own
transparency channel (i.e PNG and TIF bitmaps), which is
combined with the layer's alpha setting. The default alpha
is 1.
- Z-Position (float): front-back setting of the layer.
Layers with greater Z positions are displayed on top of
layers with less Z. Z-Position is specified when creating
the layer.
The videos are displayed and mixed at Z = 0. Layers with
Z > 0 are displayed on top of the videos. Layers with
Z < 0 are displayed below the videos, so there won't
generally be visible unless no videos are being played or
the playing video is fading in or out. It's not a good idea
to create layers with Z=0 as their placement over or under the
videos is undefined.
The layout's Background picture (VIDJ_SetLayoutConfig)
is a layer with Z = -10000. The Logo picture is a layer
with Z = +10000.
- Visible (boolean): true when the layer is visible
and is being rendered. false when the layer is not being
renderer. New layers are created with Visible = false.
- Layer options: zero or more VIDJ_LAYER_ flags.
See the vidj.h file for details.
Aspect ratio in graphic layers
Layer options (VIDJ_LAYER_ flags, see vidj.h) include aspect
ratio options. The layer options can be set up when creating
the layer and/or later with the VIDJ_LayerSetOptions
function.
Note that aspect ratio adjustment does NOT modify the layer's
position rect (VIDJ_LayerSetPosition). It just adjusts
how and where to place the picture inside the rect. Also,
no part of the picture will be ever displayed outside the
position rect.
Important: The aspect ratio adjustment will display
the picture at the correct aspect ratio when the layer's position
rect is a logical square (same width and height). Remember
that screen's logical coordinates are 0,0 for top-left corner
and 1,1 for bottom right corner, so the real rect will typically
be a rectangle. This allows picture scaling even with aspect
ratio correction.
Aspect ratio flags:
- None: (default) Ignores aspect ratio. The picture
is just scaled to fit exactly the layer's position rect.
- VIDJ_LAYER_ASPECTRATIO: Auto mode. Chooses between
ignore and shrink-to-frame (below) in order
to avoid vertical transparent bands.
- VIDJ_LAYER_ASPECTRATIO_SHRINKTOFRAME: Shrinks the
picture until it fits into the layer's position rect while
keeping the picture's aspect ratio. Top-bottom or left-right
transparent bands may be introduced.
- VIDJ_LAYER_ASPECTRATIO_ZOOMTOFIT: Zooms the picture
keeping its aspect ratio until all the layer's rect is filled.
Top-down or left-right portions of the picture may be discarded.
Alignment flags (VIDJ_LAYER_ALIGN_, see vidj.h for details):
- These flags force the picture to be aligned inside the
position rect in a specific way when aspect ratio correction
is applied. For instance, if vertical transparent bands
are to be introduced as result of the aspect ratio correction,
then you can force the picture to be aligned to the left
border instead of keeping the central position.
Creating an emtpy graphic layer
VIDJ_HLAYER hLayer;
VIDJ_LayerCreate (dwWidth, dwHeight, fZPosition, dwOptions, &hLayer); Now you can set the bitmap for the
layer with:
VIDJ_LayerSetBitmap (hLayer, hBitmap) hBitmap is the handle of the bitmap to
show (HBITMAP type). It must be the same size as specified
in VIDJ_LayerCreate. Any color format is allowed.
You can also modify the layer's content by using the layer's
GDI-compatible device context (DC). See below for details.
Creating a layer from a picture file
VIDJ_HLAYER hLayer;
VIDJ_LayerCreateFromFile (szFileName, dwColorKey, fZPosition, dwOptions, &hLayer);
- szFileName is the picture file (.dds, .dib, .hdr,
.jpg, .pfm, .png, .ppm, .tga).
- dwColorKey allows to specify a color which will
be transparent in the layer (32-bit ARGB, i.e. fuchsia is
0xFFFF00FF).
You can also display only a selected region inside the picture
file:
VIDJ_LayerSetOrigin (hLayer, &rcOrigin) rcOrigin is a VIDJ_SRect
structure with the left-top right-bottom coordinates inside
the picture file that will be displayed. Left-top is 0,0 and
right-bottom is 1,1.
If the picture has an special aspect ratio not defined by
its pixel dimensions (i.e. non-square pixels) then you can
specify the picture's aspect ratio with:
VIDJ_LayerSetAspectRatio (hLayer, dwAspectRatioX, dwAspectRatioY) The picture will be properly displayed according
with the screen's aspect ratio specified in VIDJ_SetConfig.
Working with layers
First to do is display the layer:
VIDJ_LayerSetVisible (hLayer, true) Layers are not visible after creation. Thus you
can adjust any of their paramenters before actually displaying
them.
You can move, reposition (Z) or adjust any of the other parameters
anytime:
VIDJ_LayerSetPosition (hLayer, &rcPosition)
VIDJ_LayerSetZPosition (hlayer, 10.7)
VIDJ_LayerSetAlpha (hLayer, 0.5)
VIDJ_LayerSetOptions (hLayer, dwOptions) Using layer's Device Context
(DC)
You can directly access and modify a graphic layer throught
its device context (HDC) with the following code inside a
VIDJ_UPDATEPROC callback:
HDC hDC;
DWORD dwDevice = VIDJ_GetMasterDevice();
if (VIDJ_LayerGetDC (hLayer, dwDevice, &hDC)) { // ( Do something with the HDC here )
VIDJ_LayerReleaseDC (hLayer, dwDevice, hDC); } In a dual-screen
screen scenario the Master and Preview devices will be different.
You must then change each layer's DC two times, once per device.
The following code will work in any scenario:
HDC hDC;
DWORD dwMasterDevice = VIDJ_GetMasterDevice();
DWORD dwPreviewDevice = VIDJ_GetPreviewDevice();
if (VIDJ_LayerGetDC (hLayer, dwMasterDevice, &hDC)) { // ( Do something with the HDC here )
VIDJ_LayerReleaseDC (hLayer, dwMasterDevice, hDC); }
if (dwMasterDevice != dwPreviewDevice && VIDJ_LayerGetDC (hLayer, dwPreviewDevice, &hDC)) { // ( Do with this HDC the same as above )
VIDJ_LayerReleaseDC (hLayer, dwPreviewDevice, hDC); }
Layer updates throught HDC are persistent, so you don't need
to request and update the DC on each screens update. In fact,
is much better to update the layer only when it has really
changed.
For instance, if you have a visualization routine that works
at 30 frames per second, then it's a good idea to use a timer
and upgrade the layer 30 times per second instead of on each
callback, which could be called 60, 100 or more times per
second depending on the screen's refresh rate.
Also, you could implement a counter in order to check the
dwPreviewFrameSkip config option before upgrading the
DC for the preview device. This would save CPU by skipping
innecesary updates.
IMPORTANT: Layer's HDC can ONLY be requested and used
inside a VIDJ_UPDATEPROC callback, which can be set
or cleared with the function VIDJ_SetUpdateProc.
EVEN MORE IMPORTANT: You must ALWAYS release the HDC
right after successfully retrieving it. Otherwise the entire
video engine will not be able to continue rendering the graphic
scene, and will probably end crashing the application.
Deleting layers
Just call:
VIDJ_LayerDelete (hLayer) It's a good idea to make hLayer = NULL after
deleting the layer. VIDJ layer funtions check against null
handles, but invalid or already-freed handles may raise access
violation errors.
All non-deleted layers will be automatically deleted upon
library termination (VIDJ_Free).
Fonts
and Text layers
Understanding fonts and texts
Texts are shown as a special kind of layer with its own special
properties and functions. Most layer functions can be used
also with text layers. The parameters for a text layer are:
- Common layer parameters: Position, Opacity, Z-Position,
Visible, Options.
- Font: Defines the font's appearance: face, size,
style. A single font can be used to create multiple text
layers.
- Text: the string of text that is displayed. The
text will be adjusted to fit inside the layer's position
rect using the font's properties. The text will be formatted
according to the alignment options (VIDJ_LAYER_ALIGN_) and
the text-specific options (VIDJ_LAYER_TEXT_).
- Text Color: self explained.
- Shadow: display the text with an adjustable shadow.
Shadow increases readability as one tyipically can't predict
the background's color (video clips playing).
Font parameters:
- Face: common font name (Arial, Verdana...). Any
font can be used as long as it's available in Windows.
- Size: font height, relative to main screen's height:
100 = 100%. For instance, size 10 means that the text's
height will be 10% of the main screen's height.
- Options: VIDJ_FONT_ flags, for style and aspect-ratio
options. See vidj.h for more information.
Font size and aspect ratio
Font size is specified as height percent of the screen's
height. A font size of 10 means that the text height will
be the tenth part of the screen's height. This allows you
to choose uniform font sizes that will be displayed properly
regardless the final screen's dimensions. This also makes
easy to calculate the layer's position rect for the text (VIDJ_LayerSetPosition),
as screen cordinates are also relative.
Example:
- Font size is 10
- Position rect: (0, 0)-(1, 0.2) -> the rect's height
is 0.2
The above means that the text layer will hold exactly TWO
lines of text: rect's height is 0.2, exactly 20% of the screen's
height. Font size is 10, that is, 10% of the screen's height.
So the layer will hold exactly two lines of text.
Aspect ratio adjustment in texts can be enabled only when
creating the font as a creation option (VIDJ_FONT_ASPECTRATIO).
The aspect ratio is applied by adjusting the font's width
so the letters will match the screen's aspect ratio. The font's
height is always the same.
Unlike graphic layers, font options (bold, italic, aspect
ratio) can't be changed on the fly. You will need to create
a new font with the new options and use the VIDJ_LayerSetFont
function to set the new fonts in the appropiate layers.
Creating and deleting fonts
VIDJ_HFONT hFont;
VIDJ_FontCreate ("Arial", 10, 0, &hFont); Using font options:
VIDJ_FontCreate ("Verdana", 20, VIDJ_FONT_BOLD|VIDJ_FONT_ASPECTRATIO, &hFont); Deleting fonts:
VIDJ_FontDelete (hFont); Important: You must delete all text layers
that are using the font before deleting it. Otherwise you
will receive an error and the font won't be deleted.
Creating text layers
First you need to create a font with VIDJ_FontCreate.
VIDJ_HLAYER hLayer;
VIDJ_TextLayerCreate (hFont, fZPosition, dwOptions, &hLayer); Word-wrap is a feature specific for text
layers:
VIDJ_TextLayerCreate (hFont, fZPosition, VIDJ_LAYER_TEXT_WORDWRAP, &hLayer); Working with text layers
First to do is display the layer:
VIDJ_LayerSetVisible (hLayer, true) As with graphic layers, text layers are not visible
after creation. Thus you can adjust any of their paramenters
before actually displaying them.
You can move, reposition (Z) or adjust any of the other common
layer parameteers parameters anytime:
VIDJ_LayerSetPosition (hLayer, &rcPosition)
VIDJ_LayerSetZPosition (hlayer, 10.7)
VIDJ_LayerSetAlpha (hLayer, 0.5)
VIDJ_LayerSetOptions (hLayer, dwOptions) Text layers feature some specific functions
for changing the text paramerets: text, text color (32-bit
ARGB), and font:
VIDJ_LayerSetText (hLayer, "Hello World\nThis is a sample two-lines text");
VIDJ_LayerSetTextColor (hLayer, dwColor);
VIDJ_LayerSetFont (hLayer, hNewFont); Text layers also feature shadow capabilities:
VIDJ_LayerSetShadow (hLayer, true); If you don't like the default shadow properties,
you can change them:
VIDJ_LayerSetShadowParameters (hLayer, fDistance, fAngle, fAlpha);
- fDistance uses the same units as font size. For
instance, if the font size is 10 then a shadow distance
of 10 will draw it with a "one line height" distance.
- fAngle is degrees, from 0 to 360, 0 = "up",
clockwise.
- fAlpha is the shadow's transparency.
Deleting text layers
Same as graphic layers:
VIDJ_LayerDelete (hLayer) It's a good idea to make hLayer = NULL after
deleting the layer. VIDJ layer funtions check against null
handles, but invalid or already-freed handles may raise access
violation errors.
All non-deleted layers will be automatically deleted upon
library termination (VIDJ_Free).
Important: Deleting a text layer does NOT delete the
font (VIDJ_HFONT). Fonts are intended to be created once then
used many times by text layers.
Layer
effects
Initializing layer effects
Effects for layers are initialized with the function VIDJ_LayerSetEffect.
Each layer can have a single effect active. Setting a new
effect on a layer that already has an active effect will cancel
the ongoing effect and apply the new one. The effect will
remain active until it's completed.
float afParams[] = { param0, param1, param2, param3, ... };
VIDJ_LayerSetEffect (hLayer, dwEffectId, dwOptions, fStartDelay, afParams, dwParamCount);
- dwEffectId is the desired effect (VIDJ_LAYEREFFECT_)
- dwOptions are the options for the effect (VIDJ_EFFECTOPTIONS_)
- fStartDelay is the time (in seconds) that the effect
will delay until start. 0 means "start inmediately".
- afParams is a pointer to an array of floats. dwParamCount
is the number of parameters. The number and meaning of the
parameters depends on each effect.
Available effects (dwEffectId). See details below.
- VIDJ_LAYEREFFECT_ALPHAFADE: transitions with
alpha (opacity).
- VIDJ_LAYEREFFECT_MOVE: layer movement.
Effect options (dwOptions):
- VIDJ_EFFECTOPTIONS_AUTODELETE: automatically
deletes the layer when the effect is completed. The layer
handle will be no longer valid when the effect is completed,
so it's a good idea to set it to NULL after calling VIDJ_LayerSetEffect.
- VIDJ_EFFECTOPTIONS_LOOP: makes the effect start
over when is completed. The effect will be active indefinitely
until the layer is deleted or VIDJ_LayerSetEffect is called
with the effect VIDJ_LAYEREFFECT_OFF.
Alpha transitions
dwEffectId = VIDJ_LAYEREFFECT_ALPHAFADE;
Notes:
- All times are seconds, and are NOT cummulative: each time
refers to its own stage in the effect, independently from
other stages.
- All alpha values are from 0.0 (transparent) to 1.0 (opaque).
- Specifying -1 in any alpha value means "use layer's
current alpha".
- The last time value ("stand-by" time) is useful
for loops. It can be used for specyfing a delay between
each cycle.
- Bias values: "smoothness" of the transition
from a value to the following. 0.5 means lineal transition
(same as with no bias). 0.8 means "starting fast, ending
slow". 0.2 means "starting slow, ending fast".
Simple alpha fade (4 parameters):
float afParams[] = { a0, t0, a1, t1 };
- a0: starting alpha.
- t0: fade duration from a0 to a1
- a1: ending alpha.
- t1: stand-by time with alpha = a1
Simple alpha with bias curve (5 parameters):
float afParams[] = { a0, t0, a1, t1, bias };
- a0, t0, a1, t1: same as above.
- bias: "smoothness" of the transition
curve from a0 to a1, between 0 and 1.
Alpha with 4-points evenlope (8 parameters):
float afParams[] = { a0, t0, a1, t1, a2, t2, a3, t3 };
- a0: starting alpha.
- t0: fade duration from a0 to a1
- a1: second alpha value
- t1: fade duration from a1 to a2
- a2: third alpha value
- t2: fade duration from a2 to a3
- a3: ending alpha
- t3: standby time with alpha = a3
Alpha with 4-points evenlope and bias (11 parameters):
float afParams[] = { a0, t0, a1, t1, a2, t2, a3, t3, bias0,
bias1, bias2 };
- a0, t0, a1, t1, a2, t2, a3, t3: same as above.
- bias0: "smoothness" of the transition
curve from a0 to a1.
- bias1: "smoothness" of the transition
curve from a1 to a2.
- bias2: "smoothness" of the transition
curve from a2 to a3.
Movement effects
dwEffectId = VIDJ_LAYEREFFECT_MOVE;
Notes:
- All times are seconds, and are NOT cummulative: each time
refers to its own stage in the effect, independently from
other stages.
- Only position is changed, not size. The size of the layer
is kept during the effect, so it should be specyfied with
VIDJ_LayerSetPosition before setting the effect.
- Positions are always relative to the screen. 0,0 is the
top-left corner, 1,1 is the bottom right corner.
- The last time value ("stand-by" time) is useful
for loops. It can be used for specyfing a delay between
each cycle.
- Bias values: "smoothness" of the transition
from a value to the following. 0.5 means lineal transition
(same as with no bias). 0.8 means "starting fast, ending
slow". 0.2 means "starting slow, ending fast".
Simple movement (6 parameters):
float afParams[] = { x0, y0, t0, x1, y1, t1 };
- x0,y0: starting position.
- t0: movement duration from x0,y0 to x1,y1
- x1,y1: ending position.
- t1: stand-by time at x1,y1
Simple movement with bias (7 parameters):
float afParams[] = { x0, y0, t0, x1, y1, t1, bias };
- x0,y0, t0, x1,y1, t1: same as above.
- bias: "smoothness" of the transition
curve from x0,y0 to x1,y1, between 0 and 1.
Movement between 4 positions (12 parameters):
float afParams[] = { x0, y0, t0, x1, y1, t1, x2, y2, t2,
x3, y3, t3 };
- x0,y0: starting position.
- t0: movement duration from x0,y0 to x1,y1
- x1,y1: second position
- t1: movement duration from x1,y1 to x2,y2
- x2,y2: third position
- t2: movement duration from x2,y2 to x3,y3
- x3,y3: ending position
- t3: stand-by time at x3,y3
Movement between 4 positions with bias (15 parameters):
float afParams[] = { x0, y0, t0, x1, y1, t1, x2, y2, t2,
x3, y3, t3, bias0, bias1, bias2 };
- x0,y0, t0, x1,y1, t1, x2,y2, t2, x3,y3, t3: same as above.
- bias0: "smoothness" of the transition
curve from x0,y0 to x1,y1.
- bias1: "smoothness" of the transition
curve from x1,y1 to x2,y2.
- bias2: "smoothness" of the transition
curve from x2,y2 to x3,y3.
Additional
functions & advanced topics
Retrieving the format of a file
You can retrieve the format of a file without actually opening
it with the function:
VIDJ_GetFileFormat (szFileName, &Format); This function works like VIDJ_DeckOpen,
but it just opens the file with the minimum features for retrieving
its format and then closes it.
- szFileName is the stream name or URL.
- Format is a VIDJ_SStreamFormat structure,
the same as used in the VIDJ_DeckGetFormat function.
VIDJ_GetFileFormat is useful for "media library"
features, when many files need to be analysed in order to
build a database. It runs completely "isolated"
from the rest of engine, so it can be called from any thread
at any time (provided only than the library has been initialized
with VIDJ_Init). Even multiple simultaneous calls
are supported. See "Multithreading Considerations"
below for more information.
Playing DRM-enabled files
You need to compile a DLL using the enclosed EnableDRM Visual
Studio project:
- Load the project EnableDRM in Visual Studio.
- Remove the "WMStubDRM.lib" reference from the
project and add your own WM stub library.
- Build the project. It will output a DLL file named EnableDRM.dll.
- Rename the DLL file to bassdmc.dll and put it
in the same folder as your executable file.
When a DRM-enabled file is loaded then VIDJ will try to locate
the bassdmc.dll library. If it exists, VIDJ will call its
exported function in order to create a WM certificate for
the file. If a certificate can be created then the file is
loaded and opened properly. Otherwise (i.e. the user has no
license for playing that file) the VIDJ_DeckOpen
function returns VIDJ_ERROR_DRM.
Note: DRM-enabled files cannot be played while debugging
the application. The files can be opened, but they will return
invalid or no data.
How to adquire Windows Media licenses
If opening a DRM file returns VIDJ_ERROR_DRM then the user
must first open the file in Windows Media Player and follow
the instructions for adquiring a license for that file. Once
the license is properly adquired and the file can be played
in Windows Media Player then VIDJ will be able to load and
play the file as well.
Performance tunning
The following initialization option (VIDJ_Init)
affect the usage of display memory:
VIDJ_INIT_TRIPLEBUFFER
- When enabled, each viewport will be initialized with 2
backbuffers instead of one. This typically improves video
playback smoothness on less-powerful computers.
- The display memory used per viewport is increased by 50%
with this setting. For instance, if creating a viewport
takes 20Mb of display memory, enabling triple-buffering
will take 30Mb for the same viewport.
- This setting does NOT affect the CPU usage.
The following config options (VIDJ_SConfig) affect
the usage of CPU and/or display memory:
dwPreviewFrameSkip
- The more frame-skip value, less CPU will be used during
video playback as the preview windows will be refreshed
less frequently. The default value is 2.
dwMaxVideoSize
- By setting a upper limit to the video frame size can save
a bit of display memory. It's useful for playing HD videos
in old display cards with small display memory. The default
value is 0, which means no upper limit.
- Using this setting will force the engine to use a off-screen
surface in order to resize the video frames, which may decrease
the performance on some systems.
Precise seeking in video streams
Most video codecs will always seek to the nearest video frame.
This means that if you call VIDJ_DeckSeek (VIDJ_DECKA,
200000), and then call VIDJ_DeckSeek (VIDJ_DECKA,
205000) you will likely get positioned at the same position.
So the accuracy of our seek operations seems to depend on
the video framerate. This behavior is imposed by the codecs.
However, calling VIDJ_DeckRead after the seek operation
will return the dwStartPos and dwEndPos values
according to the last seeked position. This means that the
codec wants us to believe that it has been positioned at the
exact requested position, but it really hasn't.
It seems that we can do little to improve accuracy. If the
codec seeks to video-frame boundary (it's undefined whether
the position is rounded to the next or to the previous frame)
then actually we can't know what's the exact seeked position
as the upcoming data will be timestampted as if the seek operation
had been exact.
The workaround: there is a little trick we can use
to improve acuracy when positioning in video streams. If we
know the exact framerate of the video stream, then we can
calculate the exact byte position at which each video frame
is located. Thus, we can seek to the start of the video frame
knowing that the video codec will accurately find that frame,
and then read and discard audio data until the exact requested
postition is reached.
For instance, if we want to position at "dwPosition",
then we could use this code:
dwBytesPerFrame = Format.dwBytesPerSecond / Format.fFrameRate;
dwFramePos = (dwPosition/dwBytesPerFrame) * dwBytesPerFrame;
VIDJ_DeckSeek (VIDJ_DECKA, dwFramePos);
VIDJ_DeckRead (VIDJ_DeckA, pBuffer, dwPosition-dwFramePos, NULL, NULL, NULL);
Next VIDJ_DeckRead calls will return
the data from the position "dwPosition".
But... some codecs doesn't just seek to frame-boundary,
but also to KEY frame-boundary. In these formats there are
some "key" frames which are "safe" to
start decoding from. When you seek to some position, the codec
will first seek to that position and then start reading forward
until nearest KEY frame is reached. Actually you can't know
what's the real position you're at, as you cannot know the
frequency nor positions of the key frames unless you manually
decode the file.
In summary, the seek-to-frame workaround above should
work fine in most circumstances. However, in some cases won't
be possible to achieve a good accuracy on positioning. Each
case depends on the file format and the codecs that are used
to read that format.
Multithreading
considerations
The following functions must allways be called from the main
thread:
VIDJ_Init
VIDJ_Free
VIDJ_TimerUpdate The following functions can be called from a
thread other than the main thread, provided that they are
not called at the same time and thet they are always called
*after* VIDJ_DeckOpen and *before* VIDJ_DeckClose:
VIDJ_DeckSeek
VIDJ_DeckRead The following function can be called from any
thread, anytime. Even multiple simultaneous calls are supported:
VIDJ_GetFileFormat All other functions can be called from any thread
provided than only one function is called at once.
Opening files in a separate thread
Files can be opened in a separate thread by creating a worker
thread and calling VIDJ_DeckOpen from it. No special
requirements at all.
Notes:
- Set a low priority to the worker thread. Otherwise, video
frame-skip or sound stutter could happen while loading.
- It's a good idea to set a time-out for the worker thread
to complete (30 - 60 seconds is ok). In some (very rare)
cases the VIDJ_DeckOpen function may never return due to malfunctioning codecs.
If the timeout elapses without response, then kill the worker
thread and assume "load error".
|