menu
  Home  ==>  papers  ==>  graphic  ==>  writing_a_flash_player   

Writing a Flash Player - Felix John COLIBRI.


1 - Why build a custom Flash Player ?

There are lots of flash players around. Just use Google to find them. We decided however to build our own version, in order to view demonstration movies.

Many companies now present their new software using .SWF (ShockWave Flash) animations, which are built using tools like Camtasia, Wink or similar tools. Usually you click on an .HTML link and this will load into memory the flash player, then download and display the movie.

The first part we disliked is that when you click on a .SWF link, this calls an ActiveX, and since we disabled ActiveX on our PCs, we have to answer an Internet Explorer dialog to allow using them.

The second, more compelling reason, is that the .SWF players are usually ill prepared for stepping thru a presentation, and taking snapshots of what is presented. When the capture is 1024 x 768 and your screen has the same definition, the image AND the player environment cannot be displayed without scrolling. So when you try to use the "Pause" and "Start" buttons, you have to scroll, thereby loosing some of the action. For instance if the control buttons are at the bottom, you won't see the menu selected by the demonstrator. On the other hand, if you display the top menu of the animation, you can't pause or move backward to display the previous frames.

In addition, the only way to go back is to use some kind of tTrackBar, which has very poor resolution for a one hour presentation. Moving the cursor backward can take you 4 our 5 minutes back, and if all you want to do is replay the 30 previous frames which represent the previous 5 seconds, this is a loss of time.




2 - Encapsulating ShockWave's ActiveX

2.1 - ShockWave's ActiveX

Of course we will not build a .SWF player from scratch. It would be too great an effort to understand the .SWF format, extract the data and render it on our PC. We will use instead the Macromedia ActiveX to do the extraction and playing.

This Macromedia ActiveX, "FLASH9D.OCX" in our case, is located in:

    C:\WINDOWS\system32\Macromed\Flash\Flash9d.ocx



If this ActiveX is not present on your system, you can download it from Macromedia's download page. This will download all kind of .DLLs, .EXEs, .INIs etc, AND put the .OCX in the SYSTEM.32 folder. The size is around 2 Megs :

download_flask_ocx



2.2 - Create the ActiveX Type Library

To use this ActiveX we must build the Delphi Type Library. This is done with the following steps:
   load Delphi
   select "Component | Import ActiveX Control"
   the import dialog is displayed:

shockwave_activex_import

   select the "ShockWave Flash Version" and install it
   the wizard will place the ShockwaveFlashObjects_TLB.PAS in the default Delphi import directory

shockwave_type_library

and install a tShockWaveFlash component on the ActiveX tab of the Palette:

shockwaveflash_component



2.3 - tShockwaveFlash component's main properties

The main properties of this component are:
  • Movie which contains the URL or the file name of the .SWF file
  • Scale which can have the following string values
    • ShowAll to display the complete image
    • ExactFit to adjust the image to the parent container
    • TotalFrames and FrameNum
    • Playing, a boolean, which starts or stops the display
Among the events
  • OnReadyStateChange has a NewState parameters with values
    • 0 when the movie is not ready
    • a value 2 when the movie is ready
Finally to redraw the picture to the Scale size, we call Realign



2.4 - Building the Flash Player

2.4.1 - Selecting the .SWF file

Our flash player will display off line movies. To select the movie on disc, we use a tDirectoryListbox and a tFileListBox. You could also use a tOpenDialog component.



2.4.2 - Dropping a tShockwaveFlash

We drop the tShockwaveFlash component on the tForm and align it to the right



2.4.3 - Starting the Movie

To start the movie, we use the following code:

var g_ready_stateInteger= 0;
    g_pathg_file_nameString;

procedure TForm1.load_Click(SenderTObject);
  begin
    ShockwaveFlash1.Free;

    ShockwaveFlash1:= TShockwaveFlash.Create(nil);
    with ShockwaveFlash1 do
    begin
      Parent:= sw_panel_;
      Align:= alClient;
      Loop:= true;

      Scale:= 'exactFit';

      Menu:= false;

      BackgroundColor:= -1;
      SetFocus;
      Realign;

      if url_edit_.Text<> ''
        then begin
            g_ready_state:= 0;
            Movie:= url_edit_.Text;
            repeat
              Application.ProcessMessages;
            until g_ready_state>= 2;
          end
        else Movie:= g_pathg_file_name;
    end// with ShockwaveFlash1
  end// load_Click



To pause the display, we simply toggle Playing:

procedure TForm1.pause_Click(SenderTObject);
  begin
    ShockwaveFlash1.Playing:= Not ShockwaveFlash1.Playing;
  end// pause_Click



2.4.4 - Moving back an forth

We use a tTimer to save the FrameNum. This value is used to update a TrackBar, and moving the cursor will change the FrameNum value.

In addition, two buttons allow us to decrement or increment the current FrameNum value by an amount contained in a tEdit. So if we are displaying a movie with lots of screen changes, the frame number delta can be set to 100 or 200, whereas when we watch a slideshow we can decrease the delta to 20 or 30



2.4.5 - Adjusting the volume

The volume can be adjusted using another tTrackBar.

Our basic volume MM Api encapsulation routines are:

uses mmsystem;

function f_wave_volumeDWord;
  var l_wave_out_capabilities : TWAVEOUTCAPS;
  begin
    if WaveOutGetDevCaps(WAVE_MAPPER, @l_wave_out_capabilities,
        sizeof(l_wave_out_capabilities)) = MMSYSERR_NOERROR
      then begin
          if l_wave_out_capabilities.dwSupport
              and WAVECAPS_VOLUME = WAVECAPS_VOLUME
            then begin
                WaveOutGetVolume(WAVE_MAPPER, @ Result);
              end
            else display_bug_stop('no_volume_support')  ;
        end
      else display_bug_stop('mmsyserr_noerror');
  end// f_wave_volume

procedure set_wave_volume(const AVolumeDWord);
  varl_wave_out_capabilities : TWAVEOUTCAPS;
  begin
    if WaveOutGetDevCaps(WAVE_MAPPER, @l_wave_out_capabilities,
        sizeof(l_wave_out_capabilities)) = MMSYSERR_NOERROR
      then
        if (l_wave_out_capabilities.dwSupport and WAVECAPS_VOLUME)
            = WAVECAPS_VOLUME
          then WaveOutSetVolume(WAVE_MAPPERAVolume);
  end// set_wave_volume

and when the trackbar cursor is moved by the user, we call:

procedure TForm1.volume_trackbar_Change(SenderTObject);
  var l_volumeInteger;
  begin
    l_volume:= volume_trackbar_.Position;
    l_volume:= 256* l_volume;

    (*$r-*)
    set_wave_volume(MakeLong(Word(l_volume), Word(l_volume)));
    (*$r+*)
  end// volume_trackbar_Change



Note that the highest volume might still be too low. In fact, we had to purchase two outside loudspeakers since some of the .SWF files had a too low audio level.



2.4.6 - Setting the size and the scale

The Sale property is adjusted using two tRadioButtons:

procedure TForm1.real_size_Click(SenderTObject);
  begin
    with ShockwaveFlash1 do
      if Senderreal_size_
        then Scale:= 'ShowAll' else
      if Senderstretch_
        then Scale:= 'ExactFit' else
  end// real_size_Click



When we resize the form, we can click on a button to adjust the picture size:

procedure TForm1.real_size_Click(SenderTObject);
  begin
    with ShockwaveFlash1 do
      if Senderreal_size_
        then Scale:= 'ShowAll' else
      if Senderstretch_
        then Scale:= 'ExactFit' else
  end// real_size_Click

Note that

  • this certainly some kind of hack: we close the movie, resize its container, and reopen it, setting the FrameNum to the value before resizing. This works, but there must be some better way to resize a tShockwaveFlash. Since we do not spend our time resizing the form, we stopped investigating about this resizing


We could resize the picture to the screen dimensions, but this would force some scrolling for our control panel. This is exactly what we wanted to avoid. So we set the control panel size to some minimum value (90 pixels in our example), and then adjust the form size:

procedure TForm1.max_Click(SenderTObject);
  begin
    // Position:= poMaximized;
    WindowState:= wsMaximized;
    Panel1.Width:= 90;
    resize_Click(Nil);
    real_size_.Checked:= True;
    real_size_Click(Nil);
  end// max_Click



2.4.7 - Taking a snapshot

To save a snapshot of a frame, we simply transfer the image using a Windows "Device Context" and then save the bitmap:

function f_snapshot_numberString;
    // -- compute 000frame_number '_' snapshot_name_edit
  var l_indexInteger;
  begin
    with Form1 do
    begin
      Result:= frame_edit_.Text;
      l_index:= Length(Result);
      while l_index< 5 do
      begin
        Result:= '0'Result;
        Inc(l_index);
      end// while l_index
    end// with Form1
  end// f_snapshot_number

procedure TForm1.snapshot_button_Click(SenderTObject);

  procedure save_from_screen_dc;
    var l_screen_rectangleTRect;
        l_widthl_heightInteger;
        l_bitmap_rectangleTRect;
        l_desktop_dcHDC;
        l_c_desktop_canvasTCanvas;
        l_c_bitmaptBitmap;
        l_save_pathl_save_nameString;
    begin
      // -- x1, y1
      l_screen_rectangle:= Rect(Panel1.Width- 5, 10,
          Panel1.Widthsw_panel_.Width+ 20, 10+ sw_panel_.Height+ 20);
      l_width:= l_screen_rectangle.Right - l_screen_rectangle.Left;
      l_height:= l_screen_rectangle.Bottom - l_screen_rectangle.Top;

      l_c_bitmap:= tBitmap.Create;

      l_bitmap_rectangle:= Rect(0, 0, l_widthl_height);

      l_c_bitmap.PixelFormat:= pf24bit;  // optional
      l_c_bitmap.Width:= l_width;
      l_c_bitmap.Height:= l_height;

      l_desktop_dc:= GetWindowDC(GetDeskTopWindow);
      try
        l_c_desktop_canvas:= TCanvas.Create;
        try
          l_c_desktop_canvas.Handle:= l_desktop_dc;
          l_c_bitmap.Canvas.CopyMode:= cmSrcCopy;
          l_c_bitmap.Canvas.CopyRect(l_bitmap_rectangle,
              l_c_desktop_canvasl_screen_rectangle);

          l_save_path:= g_path'_i_image\';
          f_create_path(l_save_path);

          l_save_name:= l_save_path'fig_'f_snapshot_number'_'
              + snapshot_name_edit_.Text'.png';

          // -- now convert to .PNG and save
          // WriteBitmapToPngFile(l_save_name, l_c_bitmap, clNone);
          l_c_bitmap.SaveToFile(l_save_name);

          l_c_bitmap.Free;

          // -- add for .Ed
          ClipBoard.AsText:= 'fig_'snapshot_name_edit_.Text;
        finally
          l_c_desktop_canvas.Free;
        end;
      finally
        ReleaseDC(GetDeskTopWindowl_desktop_dc);
      end;
    end// save_from_screen_dc

  begin // snapshot_button_Click
    save_from_screen_dc;
  end// snapshot_button_Click



We can also trigger a snapshot by typing the file name in an edit:

procedure TForm1.snapshot_name_edit_KeyPress(SenderTObject;
    var KeyChar);
  begin
    if (Keyk_return)
      then begin
          // -- save on disc
          snapshot_button_Click(Nil);

          // -- now get the focus back to pause
          pause_.SetFocus;
          // -- and restart
          if Not ShockwaveFlash1.Playing
            then ShockwaveFlash1.Playing:= True;
        end;
  end// snapshot_name_edit_KeyPress



And

  • each disc snapshot file name starts with the current FrameNum value. This guarantees that the snaphsots are displayed in the directory in increasing order, and we also have the accurate frame number (in case we want to see the movie in the vicinity of this value)
  • We usually use a .PNG format, but for this project, we saved the image as a .BMP.


2.4.8 - Handling "Pause"

We will not display the code (you will find it in the source code .ZIP), but we manage the Focus in order to be able to stopstart playing the movie by simply hitting the space bar. In order to do this, after each action we transfer the focus to the "Pause" button before leaving the action handler.



2.4.9 - Resuming the display

We constantly display the FrameNum value. This value is also saved in a file on disc when we leave the application. If we later restart the application, this FrameNum is read back, and we may start the display from this value. This comes handy when we watch lengthy .SWF files, and we want to split this viewing session in several parts.




3 - Using the Shockwave Flash player



3.1 - Mini HowTo

Here is a quick example of how we use this project:
   compile and run the project
   select the path and file name using the DirectoryListBox1 and FileListBox1:

select_swf_file

   clicking on a .SWF files shows the control panel tab

start_playing

In this snapshot
  • (1) loads the selected file and starts the playing
  • (2) allows to resize the image
  • (3) display the frame count (1.399), the current frame (1031) and allows to save this current number
  • (4) jumps to the frame number displayed (500)
  • (5) toggles between the stretch modes
  • (6) maximizes the form to the screen size, minus 90 for the left control panel
  • (7) saves a snapshot. Simply type the name and hit <Space>
  • (8) will pause the display. Since this button has the focus most of the time, simply hitting <Enter> will toggle between stop and play
  • (9) is the loudspeaker volume
  • (10) allows frame decrement and increment by the amount displayed
  • (11) tracks the current frame number, and moving the cursor with the mouse sets the current frame number


3.2 - Improvements

This player was only built to be able to play demonstration .SWF files. The image is not optimized: shrinking a 1024 x 768 to a 900 x 700 size will not display very nice characters, for instance. Displaying the image with its original size is of course better, but when you did not record the .SWF in the first place, there's not much you can do if you want to avoid scrolling or shrinking.

In addition we did not spend too much time trying to use all the tShockwaveFlash capabilities, but our objective was not to be exhaustive in this area.




4 - Download the Flash Player Sources

Here are the source code files: The .ZIP file(s) contain:
  • the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any other auxiliary form
  • any .TXT for parameters, samples, test data
  • all units (.PAS) for units
Those .ZIP
  • are self-contained: you will not need any other product (unless expressly mentioned).
  • for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
  • will not modify your PC in any way beyond the path where you placed the .ZIP (no registry changes, no path creation etc).
To use the .ZIP:
  • create or select any folder of your choice
  • unzip the downloaded file
  • using Delphi, compile and execute
To remove the .ZIP simply delete the folder.

The Pascal code uses the Alsacian notation, which prefixes identifier by program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lass etc. This notation is presented in the Alsacian Notation paper.



As usual:

  • please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs, broken links or had some problem downloading the file. Resulting corrections will be helpful for other readers
  • we welcome any comment, criticism, enhancement, other sources or reference suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
  • or more simply, enter your (anonymous or with your e-mail if you want an answer) comments below and clic the "send" button
    Name :
    E-mail :
    Comments * :
     

  • and if you liked this article, talk about this site to your fellow developpers, add a link to your links page ou mention our articles in your blog or newsgroup posts when relevant. That's the way we operate: the more traffic and Google references we get, the more articles we will write.



References

  • the Flash ActiveX which will read and play a .SWF file from you disc can be downloaded from the Macromedia Download site
  • the best documentation for using this ActiveX can be found at DelphiFlash.com. We have seldom seen a site with this quality of documentation. They also offer a Shockwave SDK .



5 - The author

Felix John COLIBRI works at the Pascal Institute. Starting with Pascal in 1979, he then became involved with Object Oriented Programming, Delphi, Sql, Tcp/Ip, Html, UML. Currently, he is mainly active in the area of custom software development (new projects, maintenance, audits, BDE migration, Delphi Xe_n migrations, refactoring), Delphi Consulting and Delph training. His web site features tutorials, technical papers about programming with full downloadable source code, and the description and calendar of forthcoming Delphi, FireBird, Tcp/IP, Web Services, OOP  /  UML, Design Patterns, Unit Testing training sessions.
Created: oct-07. Last updated: jul-15 - 98 articles, 131 .ZIP sources, 1012 figures
Copyright © Felix J. Colibri   http://www.felix-colibri.com 2004 - 2015. All rigths reserved
Back:    Home  Papers  Training  Delphi developments  Links  Download
the Pascal Institute

Felix J COLIBRI

+ Home
  + articles_with_sources
    + database
    + web_internet_sockets
    + oop_components
    + uml_design_patterns
    + debug_and_test
    + graphic
      – delphi_3d_designer
      – write_a_flash_player
      – delphi_video_player
    + controls
    + colibri_utilities
    + colibri_helpers
    + delphi
    + firemonkey
    + compilers
  + delphi_training
  + delphi_developments
  + sweet_home
  – download_zip_sources
  + links
Contacts
Site Map
– search :

RSS feed  
Blog