Deejaysystem VIDJ Video Playback Engine - User's Guide    
  VIDJ Library 2.2.0 / 2010.05.18
(c) 2003-2010 Angel Garcia Voces - All Rights Reserved
www.vidj.com
   
 
 
 

Content:

  • Setting up the project
    • Common guidelines
  • Initializing the library
    • VIDJ_Init (updated)
    • Preview panels
    • Configuration
    • Preparing for open files
    • The VIDJ_TimerUpdate function
    • The VIDJ_UPDATEPROC callback
    • Releasing the library
  • Opening and playing files
    • Opening a media file
    • Opening a live input device
    • Opening an internet stream
    • Retrieving the media format
    • Positioning and reading audio data
    • Detecting end of stream
    • Mixing video
    • Closing a file
    • Retrieving buffer state
  • Additional settings and functions
    • Aspect-ratio adjustment
    • Layout setup (background and logo)
    • Ticker text
  • Displaying custom bitmaps
    • Enabling custom bitmaps
    • Show or update a custom bitmap
    • Disable the custom bitmap
  • Custom graphic layers
    • Understanding layers
    • Aspect-ratio in graphic layers
    • Creating an empty graphic layer
    • Creating a layer from a picture file
    • Working with layers
    • Using layer's Device Context (DC)
    • Deleting layers
  • Fonts and text layers
    • Understanding fonts and texts
    • Font size and aspect-ratio
    • Creating and deleting fonts
    • Creating text layers
    • Working with text layers
    • Deleting text layers
  • Layer effects
    • Initializing layer effects
    • Alpha transition
    • Movement effects
  • Additional functions & advanced topics
    • Retrieving the format of a file
    • Playing DRM-enabled files
    • Performance tunning
    • Precise seeking in video streams
  • Multithreading considerations
    • Multithreading rules
    • Opening files in a separate thread
 
 
 

Setting up the project

Three simple steps:

  1. Include vidj.h somewhere in your source code
  2. Add vidj.lib to your project
  3. 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:

  1. Load the project EnableDRM in Visual Studio.
  2. Remove the "WMStubDRM.lib" reference from the project and add your own WM stub library.
  3. Build the project. It will output a DLL file named EnableDRM.dll.
  4. 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".