65.9K
CodeProject is changing. Read more.
Home

MediaShow

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.69/5 (7 votes)

Oct 11, 2007

2 min read

viewsIcon

48913

downloadIcon

2881

An article on ‘MediaShow,’ a media player based on the famous DirectShow multimedia API from Microsoft.

Screenshot - MediaShow.jpg

Introduction

At long last I have finally made what I have wanted to make for so long. Being new to the coding world, this media player (which I call 'MediaShow') is my first working software. It is based on the famous DirectShow multimedia API from Microsoft.

DirectShow divides the processing of multimedia tasks such as video playback into a set of steps known as filters. Each filter represents a stage in the processing of the data. Filters have a number of input and output pins which connect them together. The generic design of the connection mechanism means that filters can be connected in many different ways for different tasks to build a filter graph.

It is possible to make custom filters which can be used to play a variety of media file formats; clearly displaying the power of DirectShow API. But the complexity for customization and making filters from scratch makes the use of this API rather difficult.

Features

I used a C# example of media playback with DirectShow and modified it to suit my requirements. The following features are there in MediaShow:

  • Play media/audio files (WAV, WMA, **MOV, QT, AVI, DAT, MP3, MPEG/MPG)
  • Playlist in a new format(*.ms)
  • Voice Recorder
  • CD tray Open and Close
  • Subtitles Play
  • Set rate of play of file
  • Single frame step which displays one frame at a time until the user presses a key to move to the next frame
  • Different Screen Modes
  • Volume Control
  • Mute option
  • Position Trackbar to jump to desired position
  • Displays the total duration of file
  • Displays the duration of play of file

Using the Code

The code is divided into two parts:

  • First part uses directshow.dll and performs: basic media playback, set rate of play of file, single frame step, volume control, different screen modes and duration of file.
private void PlayMovieInWindow(string filename)
{
    int hr = 0;

    if (filename == string.Empty)
        return;

    this.graphBuilder = (IGraphBuilder)new FilterGraph();

    // Have the graph builder construct its the appropriate graph 
    // automatically
    hr = this.graphBuilder.RenderFile(filename, null);
    DsError.ThrowExceptionForHR(hr);

    // QueryInterface for DirectShow interfaces
    this.mediaControl = (IMediaControl)this.graphBuilder;
    this.mediaEventEx = (IMediaEventEx)this.graphBuilder;
    this.mediaSeeking = (IMediaSeeking)this.graphBuilder;
    this.mediaPosition = (IMediaPosition)this.graphBuilder;

    // Query for video interfaces, which may not be relevant for audio files
    this.videoWindow = this.graphBuilder as IVideoWindow;
    this.basicVideo = this.graphBuilder as IBasicVideo;

    // Query for audio interfaces, which may not be relevant for 
    // video-only files
    this.basicAudio = this.graphBuilder as IBasicAudio;

    // Is this an audio-only file (no video component)?
    CheckVisibility();

    // Have the graph signal event via window callbacks for performance
    hr = this.mediaEventEx.SetNotifyWindow(this.Handle, WMGraphNotify, 
        IntPtr.Zero);
    DsError.ThrowExceptionForHR(hr);

    if (!this.isAudioOnly)
    {
        this.ClientSize = new Size(379,331);
        // Setup the video window
        hr = this.videoWindow.put_Owner(this.Handle);
        DsError.ThrowExceptionForHR(hr);

        hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | 
            WindowStyle.ClipSiblings | WindowStyle.ClipChildren);
        DsError.ThrowExceptionForHR(hr);

        hr = InitVideoWindow(1, 1);
        DsError.ThrowExceptionForHR(hr);

        GetFrameStepInterface();
    }
    else
    {
        // Initialize the default player size and enable playback menu items
        hr = InitPlayerWindow();
        DsError.ThrowExceptionForHR(hr);

        // EnablePlaybackMenu(true, MediaType.Audio);
    }
    // Complete window initialization
    // CheckSizeMenu(menuFileSizeNormal);
    this.isFullScreen = false;
    this.currentPlaybackRate = 1.0;
    UpdateStatusBar();
    UpdateToolStrip();

#if DEBUG
    rot = new DsROTEntry(this.graphBuilder);
#endif

    this.Focus();

    // Run the graph to play the media file
    hr = this.mediaControl.Run();
    DsError.ThrowExceptionForHR(hr);

    this.currentState = PlayState.Running;
    Increment.Start();
}

private void CloseClip()
{
    int hr = 0;

    // Stop media playback
    if (this.mediaControl != null)
        hr = this.mediaControl.Stop();

    // Clear global flags
    this.currentState = PlayState.Stopped;
    this.isAudioOnly = true;
    this.isFullScreen = false;

    // Free DirectShow interfaces
    CloseInterfaces();

    // Clear file name to allow selection of new file with open dialog
    this.filename = string.Empty;

    // No current media state
    this.currentState = PlayState.Init;
    UpdateStatusBar();
    UpdateToolStrip();
    InitPlayerWindow();
}

private int InitVideoWindow(int nMultiplier, int nDivider)
{
    int hr = 0;
    int lHeight, lWidth;

    if (this.basicVideo == null)
        return 0;

    // Read the default video size
    hr = this.basicVideo.GetVideoSize(out lWidth, out lHeight);
    if (hr == DsResults.E_NoInterface)
        return 0;

    // EnablePlaybackMenu(true, MediaType.Video);

    // Account for requests of normal, half, or double size
    lWidth = lWidth * nMultiplier / nDivider;
    lHeight = lHeight * nMultiplier / nDivider;
    if (!this.isAudioOnly)
    {
        panel1.Size = new Size(lWidth, lHeight);
        PositionTracker.Location = new Point(0, lHeight + 52);
        this.ClientSize = new Size(lWidth+1, lHeight+139);
        Application.DoEvents();

        hr = this.videoWindow.SetWindowPosition(panel1.Left, panel1.Top, 
            panel1.Width, panel1.Height);
    }
    return hr;
}

private void MoveVideoWindow()
{
    int hr = 0;

    // Track the movement of the container window and resize as needed
    if (this.videoWindow != null)
    {
        hr = this.videoWindow.SetWindowPosition(panel1.Left,panel1.Top,
            panel1.Width,panel1.Height);
        DsError.ThrowExceptionForHR(hr);
    }
}

private void CheckVisibility()
{
    int hr = 0;
    OABool lVisible;
    if ((this.videoWindow == null) || (this.basicVideo == null))
    {
        // Audio-only files have no video interfaces.  This might also
        // be a file whose video component uses an unknown video codec.
        this.isAudioOnly = true;
        return;
    }
    else
    {
        // Clear the global flag
        this.isAudioOnly = false;
    }

    hr = this.videoWindow.get_Visible(out lVisible);
    if (hr < 0)
    {
        // If this is an audio-only clip, get_Visible() won't work.
        //
        // Also, if this video is encoded with an unsupported codec,
        // we won't see any video, although the audio will work if it is
        // of a supported format.
        if (hr == unchecked((int)0x80004002)) //E_NOINTERFACE
        {
            this.isAudioOnly = true;
        }

        else
            DsError.ThrowExceptionForHR(hr);
    }
}

I could also use mciSendString to play media/audio files but I preferred not to do so because DirectShow has the capability of playing a wider range of formats than mciSendString.

The code on the whole is very simple to understand and well indented. But as I said before, I am new in to the field of software development and the scope of improving the quality of code is tremendous, so any suggestions regarding that would be very much appreciated.

More…

I am working on the player to make it optimized in every sense of the word. I am also trying to add better features to it. Any suggestions are most welcome. I have also have not checked for bugs which might be in the software so I request all you guys out there to please look into 'MediaShow' and please report the bugs to me.