Describes development procedure of a Wndows Media Player Plugin that will allow us to display captions using common subtitle format files such as srt

Please Sign up or sign in to vote.

Table of Contents

Here's a screenshot of Windows Media Player with the plugin running:

This article discusses implementation, logic, source code and building procedure of a Subtitle Plug-in for Windows Media Player. If you are interested in understanding the source-code you can go through the code related discussions. Otherwise, to install this plugin you can skip to the section, Installing Plug-in without building from Code. Updated source-code of the project is available at Google Code

While most of the players support common formats (srt, sub, ssa/ass) of captions/subtitles, still now Windows Media Player (WMP) does not support them or feature has not been implemented to render captions directory from them. For this reason, we don’t find a way to enable subtitles for common formats in WMP. However, it supports SAMI captions. In this article, we are going develop a plugin to support other types of captions such as subrip (srt).

Let’s briefly talk about solutions that are available.

One of the most popular alternative is DirectVobSub. Forked from VobSub and previously known as VSFilter it supports 8 popular formats of subtitles/captions. Players such as VLC, Media Player Classic and KMPlayer uses it to render subtitles and uses directshow to display them in Windows.

One interesting player is DivX Player which uses Microsoft Media Foundation to render Videos and Captions and, therefore, they don't use DirectVobSub.

With updates to WMP it still includes directshow along with Media Foundation. Therefore, it is possible to enable subtitles using directshow filters. Following directshow filter based projects can enable subtitle on WMP:

Recent editions of Windows Media Player uses Microsoft Media Foundation. Media Foundation has some advantages and performance improvements. Using a directshow filter to render captions with Media Foundation hurts that advancement. Performance issue is noticeable sometimes. It is why, our target is to enable captions in WMP for some unsupported subtitle formats without altering media foundation pipeline. Here is a project that does not use direct-show: http://sourceforge.net/projects/wmpsub/files/ and meets mentioned goals. However, this project is not open-source and does not give us interesting development scenario.

Our target is to build a plugin for Windows Media Player that will:

Create supported SAMI caption from unsupported format

Enable displaying this caption while playing the video

Provide settings to configure this plugin

Because of the plugin we'll be able to load captions with Video when opened with Media Player.

Windows Media Player SDK on msdn provides documentation on development of Windows Media Player Plug-ins. Therefore, for detailed documentation on the subject please navigate to Windows Media Player Plug-ins on msdn.

First step in developing WMP Plugin is to install plugin wizard. Please follow instructions at Getting Started with the Plug-in Wizard. The webpage suggests that you install "Windows SDK, which includes the Windows Media Player SDK". Regarding this, not all Windows SDK include Windows Media Player SDK. Specifically, Windows SDK 7.0 provides this. I have not been able find one with Windows SDK 8.

According to the documentation page, you have to modify wmpwiz.vsz to fix Wizard Version and absolute path for Wizard location to make the Wizard for your Visual Studio. Wizard version has to be set according to your Visual Studio Version. For example, 11.0 is for Visual Studio 2012 and, 12.0 is for Visual Studio 2013. Absolute path can be set to the location where your wizard setup files exist and can be different based on where have extracted the SDK. If you have installed the SDK, default location works well. Here's a sample wmpwiz.vsz I am using for Visual Studio 2013.

VSWIZARD 7 . 0 Wizard=VsWizard.VsWizardEngine. 12 . 0 Param= " WIZARD_NAME = Windows Media Player Plug-in Wizard" Param= " ABSOLUTE_PATH = F:\WinSDK 7.0\multimedia\WMP\Wizards\wmpwiz" Param= " FALLBACK_LCID = 1033"

I copied 3 files to "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcprojects". Afterwards, we are able to create a new plugin project following Using the Plug-in Wizard with Visual Studio. Here's a screenshot that displays Windows Media Player Plugin in New Project dialog box.

There are 4 types of WMP Plugin:

Visualization Plugin

UI Plugin

DSP Plugin

Rendering Plugin (Deprecated)

UI Plugins are several types

Display Area Plug-ins (deprecated)

Settings Area Plug-ins

Metadata Area Plug-ins (deprecated)

Separate Window Plug-ins

Background Plug-ins

We are going to work on Background Plug-in as our plugin does some operations in the background to set up the caption when a media file is opened. For this reason, when Media Player Plugin Wizard dialog appears we select UI Plugin as displayed on the screen-shot below,

Afterwards, Visual Studio creates required files and templates for the project that are ready to modify.

Our Plugin application precisely does following things:

When a file is opened on Windows Media Player it is an event named wmposMediaOpen. When this event occurs we perform following:

Check the extension of opened media file if it can support captions. For example, audio files and some video formats don't support captions.

If caption is supported then we check if sami (.smi) caption file already exists. SAMI caption support is built-in. Hence if such a caption for the media file already exists it is automatically loaded by WMP.

When such a supported caption file is not found, in this stage, we look for unsupported caption file such as subrip (.srt). We convert such file when we find one. Currently, this application only supports converting caption files with .srt extension. However, it is easy to update this application to support conversion from other formats as well.

Besides that, I have also tried to optimize code and follow standard procedure to ignore any kind of memory leak as I am using C++.

Following functions implement the operations mentioned in 'core logic' section:

UpdateMediaFilePath - gets the path of opened media file.

FileExtNotSupportedByPlugin - for file extensions not supported this function returns true. Following file extensions are listed not to be supported primarily because they are audio files.

.m4a .mp3 .wma .wav .mp2 .ivf .mpa .m3u .wax .cda .mid .midi .rmi .au .aac

CaptionAlreadyAvailable - this function returns true when SAMI caption file for the media file is found.

EnableCaptionFromSubtitle - finds existing subtitle in unsupported format, converts it and loads it. -

We add our code inside events cpp file (WMPNativeSubtitleevents.cpp)

case wmposMediaOpen: { UpdateMediaFilePath(); if (FileExtNotSupportedByPlugin(m_sFilePath)) break ; if (CaptionAlreadyAvailable()) break ; if (EnableCaptionFromSubtitle() == FALSE) { break ; } break ; }

The functions that have been called from this source file are defined in WMPNativeSubtitle.cpp. Let's mention what we add in that source file and header file. Following declarations are added to header file "WMPNativeSubtitle.h" inside declaration of class CWMPNativeSubtitle:

class ATL_NO_VTABLE CWMPNativeSubtitle : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CWMPNativeSubtitle, &CLSID_WMPNativeSubtitle>, public IWMPEvents, public IWMPPluginUI { public : CWMPNativeSubtitle(); ~CWMPNativeSubtitle(); DECLARE_REGISTRY_RESOURCEID(IDR_WMPNativeSubtitle) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CWMPNativeSubtitle) COM_INTERFACE_ENTRY(IWMPEvents) COM_INTERFACE_ENTRY(IWMPPluginUI) END_COM_MAP() private : LPTSTR m_sFilePath; BOOL EnableCaptionFromSubtitle(); BOOL CaptionAlreadyAvailable(); LPTSTR GetSubtitleFilePath(); void UpdateMediaFilePath(); };

At the end of same header file we add following declarations:

BOOL FAILMSG(HRESULT hr); BOOL FileExists(TCHAR * file); BOOL FileExtNotSupportedByPlugin(LPCTSTR sFile); BOOL StringEndsWith(LPCTSTR str, LPCTSTR suffix);

We include header file for conversion from srt to SAMI in cpp file (WMPNativeSubtitle.cpp).

#include " SAMIConversion.h"

Afterwards, we add function definitions into the same cpp file:

void CWMPNativeSubtitle::UpdateMediaFilePath() { if (m_sFilePath) delete m_sFilePath; BSTR sFileName; HRESULT hr = m_spCore->get_URL(&sFileName); if (FAILMSG(hr)) return ; const int sfnSize = SysStringLen(sFileName)+1; m_sFilePath = new TCHAR[sfnSize]; _tcscpy_s(m_sFilePath, sfnSize, sFileName); ::SysFreeString(sFileName); }

The function allocates necessary space and acquires media file path using get_URL method of COM interface IWMPCore.

BOOL FileExtNotSupportedByPlugin(LPCTSTR sFile) { if (StringEndsWith(sFile, TEXT( " .m4a" )) || StringEndsWith(sFile, TEXT( " .mp3" )) || StringEndsWith(sFile, TEXT( " .wma" )) || StringEndsWith(sFile, TEXT( " .wav" )) || \ StringEndsWith(sFile, TEXT( " .mp2" )) || StringEndsWith(sFile, TEXT( " .ivf" )) || StringEndsWith(sFile, TEXT( " .mpa" )) || StringEndsWith(sFile, TEXT( " .m3u" )) || \ StringEndsWith(sFile, TEXT( " .wax" )) || StringEndsWith(sFile, TEXT( " .cda" )) || StringEndsWith(sFile, TEXT( " .mid" )) || StringEndsWith(sFile, TEXT( " .midi" )) || \ StringEndsWith(sFile, TEXT( " .rmi" )) || StringEndsWith(sFile, TEXT( " .au" )) || StringEndsWith(sFile, TEXT( " .aac" ))) return TRUE; return FALSE; }

Function FileExtNotSupportedByPlugin returns false encountering any of the mentioned extension.

BOOL CWMPNativeSubtitle::CaptionAlreadyAvailable() { CComPtr<IWMPClosedCaption> spWMPClosedCaption; HRESULT hr = E_FAIL; hr = m_spCore->get_closedCaption(&spWMPClosedCaption); if (FAILMSG(hr)) return true ; if (spWMPClosedCaption) { BSTR smiFileName; hr = spWMPClosedCaption->get_SAMIFileName(&smiFileName); if (FAILMSG(hr)) return TRUE; if (smiFileName != NULL) { BOOL isNotEmpty = (BOOL) wcscmp(smiFileName, L " " ); ::SysFreeString(smiFileName); return isNotEmpty; } } return FALSE; }

This function uses get_closedCaption method of COM interface IWMPCore. and get_SAMIFileName method of COM interface IWMPClosedCaption to check whether a SAMI caption has been loaded.

BOOL CWMPNativeSubtitle::EnableCaptionFromSubtitle() { LPTSTR subInputName = GetSubtitleFilePath(); if (subInputName == NULL) return FALSE; SubToSAMIConverter subToSAMIConverter(subInputName); if (subToSAMIConverter.convert_to_sami()) { BSTR mediaFileName; HRESULT hr = m_spCore->get_URL(&mediaFileName); if (FAILMSG(hr)) return FALSE; m_spCore->close(); m_spCore->put_URL(mediaFileName); ::SysFreeString(mediaFileName); return TRUE; } return FALSE; }

Afterwards, we add function definitions in cpp file (WMPNativeSubtitle.cpp). We get srt subtitle file path in a pointer variable using function GetSubtitleFilePath.

Function GetSubtitleFilePath acquires the media file path and replaces its extension with srt. It also checks whether this final path exists. If file does not exist the function returns NULL.

After getting subtitle file path we pass it to a member function of class SubToSAMIConverter. We discuss about the conversion procedure on next section. This function returns true if conversion procedure is successful. On success we close the media file and reopen to load caption using put_URL method of IWMPCore interace.

Function convert_to_sami which is a public member of class SubToSAMIConverter does required conversion from subrip to SAMI. To understand this conversion procedure we need knowledge on following topics:

SubRip - it is the input format for convert_to_sami

SAMI - it is the output format for convert_to_sami

You can visit Wikipedia's entry on SubRip for detail information. Basically, a subrip text file is a collection of structured caption text such as following:

A numeric number that increments by one for each caption

Time Stamp

Text

Additionally, SubRip supports some basic html formatting.

To know about SAMI (Microsoft Synchronized Accessible Media Interchange) format and its specification please go through Understanding SAMI 1.0 on msdn In brief, a SAMI file looks like a html document (though not really html) which contains caption texts inside body tag

A sync tag mentioning starting time in milliseconds

Caption text under a p tag mentioning class and id to apply required formatting

Before body tag formatting style classes and ids are defined for p tag. SAMI captioning has limited html support and it allows captions in multiple languages.

When an object of the class SubToSAMIConverter is created passing the input subtitle file (.srt) path using constructor it creates following files:

Output SAMI Document(smi text file )

Log file (if specified), it helps debugging

Afterwards, convert_to_sami function does follow:

Writes initial style description and header to the smi file which looks like this:

<SAMI> <head> <STYLE TYPE= " text/css" > <!-- P { font-size: 1 .2em; font-family: Arial, Sans-Serif; font-weight: normal; color: #FFFFFF; background-color: transparent; text-align: center; } .SACAPTION { name: English; lang: EN-US; } --> </STYLE> </head> <body>

Name of our paragraph tag class for english caption is 'SACAPTION'.Then it takes each line from srt file as input and recognizes line type. Line input is taken using following function,

BOOL SubToSAMIConverter::get_sub_line(LPTSTR *lineStr, int *length);

and line type is determined by following function,

LINETYPE get_line_type(LPTSTR line, SubToSAMIConverter* pSamiConverter);

Following types of lines are considered in an srt file:

New line - when a newline is encountered first we verify whether we got this newline after caption in that case w he n e

- when a newline is encountered first we verify whether we got this newline after caption in that case w he n e Number Counter - is named seqeuence in the program; nothing is written to output smi file when it is encountered. However, in future, we can do a checking whether number counter is correct and may write a warning to the log file when such event occurs.

- is named seqeuence in the program; nothing is written to output smi file when it is encountered. However, in future, we can do a checking whether number counter is correct and may write a warning to the log file when such event occurs. TimeStamp - two timestamps are found in these lines in srt file considering standard is followed. Hence, we save starting and ending time and perform necessary logging.

- two timestamps are found in these lines in srt file considering standard is followed. Hence, we save starting and ending time and perform necessary logging. Caption Text - when texts are found I do some checking such as whether timestamps and string pointer are valid. If everything is okay, program does following for each of the two timestamps. for starting time stamp, text is printed with sync time, see following example, <SYNC Start= " 8705" > <p class = " SACAPTION" > I ' m so lonely, broken angel </p> </SYNC> for ending time stamp, a white space is written to file with sync time. Following example illustrates this: <SYNC Start= " 12178" > <p class = " SACAPTION" > </p> </SYNC>

- when texts are found I do some checking such as whether timestamps and string pointer are valid. If everything is okay, program does following for each of the two timestamps. The reason behind this is that, Windows Media Player keeps displaying the same caption till next sync time is found. This can be irritating. Imagine: a short sentence from the speaker in the video, then other events can occur and next sentence might be uttered in a long time.

Default - any garbage: is considered as an error and should return failure. This is strict. Usually, this case is not satisfied.

While reading from subtitle text files it should be considered that input text file can be encoded in following formats:

ANSI Text File

UTF-8 Text with BOM

UTF-8 Text without BOM

UTF-16 Text File (BOM is default)

UTF-32 Text File (BOM is default)

There are two functions where encoding is being taken cared of:

function read_data_into_buffer which is used/called by get_sub_line - reads BYTEs from file and converts them to Unicode buffers

which is used/called by - reads BYTEs from file and converts them to Unicode buffers writeSmiText and writeLog - writes texts by encoding them into utf-8.

get_sub_line function is giving us a string pointer to the line read from file and length of the line in an integer pointer variable using read_data_into_buffer read_data_into_buffer reads 1024 bytes from specified file using win32 ReadFile function (file handles for ReadFile and WriteFile were created in constructor of the class). W e

All text encoding related functions are defined in encoding.cpp and declared in encoding.h

enum TEXT_ENCODE_FORMAT { ANSI_TEXT, UTF8_TEXT_WITHOUT_BOM, UTF8_TEXT_WITH_BOM, UTF16_TEXT, UTF32_TEXT, UNKNOWN_TEXT_TYPE }; LPTSTR ConvertUTF8ToUTF16( __in LPCSTR pszTextUTF8 ); LPTSTR ConvertANSIToUTF16( __in LPCSTR pszTextANSI ); LPSTR ConvertUTF16ToUTF8( __in LPCWSTR pszTextUTF16 ); BOOL is_utf8_encoded(__in const unsigned char * inStr); TEXT_ENCODE_FORMAT get_text_file_encoding(__in const unsigned char * inStr);

Primarily function get_text_file_encoding is giving us type of encoding used by the input text file. Here's how code of this function looks like:

TEXT_ENCODE_FORMAT get_text_file_encoding(__in const unsigned char * inStr) { BYTE firstByte = inStr[ 0 ]; BYTE secondByte = inStr[ 1 ]; BYTE thirdByte = inStr[ 2 ]; if ((firstByte == 0x00 && secondByte == 0x00 && thirdByte == 0xFE && inStr[ 3 ] == 0xFF) || (firstByte == 0xFF && secondByte == 0xFE && thirdByte == 0x00 && inStr[ 3 ] == 0x00)) return UTF32_TEXT; if ((firstByte == 0xFE && secondByte == 0xFF) || (firstByte == 0xFF && secondByte == 0xFE)) return UTF16_TEXT; if ((firstByte == 0xEF && secondByte == 0xBB && thirdByte == 0xBF) && is_utf8_encoded(&inStr[ 3 ])) return UTF8_TEXT_WITH_BOM; if (is_ANSI_encoded(inStr) == FALSE) return UTF8_TEXT_WITHOUT_BOM; return ANSI_TEXT; }

Input to this function, inStr is usually first 1024 bytes of data from the file.

There are several issues to deal with while including a plugin configuration/property dialog box

Keeping up to date state in the plugin class

Persistently storing the settings applied by user

Properly updating the dialog for display with up to date settings

Our plugin property dialog is pretty sample and to demonstrate all the issues to deal successfully.

We maintain a single property. It is used to know whether logging should be enabled or not. At first we declare a variable as private member of class CWMPNativeSubtitle inside WMPNativeSubtitle.h

BOOL m_bLogSetting;

This variable is used to keep up to date information about log setting. By default it is disabled and plugin will not create a log file along side creating an SMI file. Have a look at constructor,

CWMPNativeSubtitle::CWMPNativeSubtitle(): m_sFilePath(NULL), m_bLogSetting(FALSE) { ... }

For example, user has enabled logging using Properties Dialog Box of the plugin. In that case, it is read from Windows Registry in same constructor,

CRegKey key; LONG lResult; lResult = key.Open(HKEY_CURRENT_USER, kwszPrefsRegKey, KEY_READ); if (ERROR_SUCCESS == lResult) { DWORD dwValue = 0 ; DWORD dwType = 0 ; ULONG uLength = sizeof (dwValue); lResult = key.QueryValue(kwszPrefsLogging, &dwType, &dwValue, &uLength); if (ERROR_SUCCESS == lResult) { m_bLogSetting = (BOOL) (dwValue & 0x0001); } }

As you can see for the first time plugin is enabled it will not be able read it from registry as relevant registry keys have not been created yet. In that case, Key Open procedure will fail and variable m_bLogSetting's disabled state will not be changed. Registry Key Path and Name is declared in header file (WMPNativeSubtitle.h)

const WCHAR kwszPrefsRegKey[] = L " Software\\Microsoft\\MediaPlayer\\UIPlugins\\{52738E25-987F-4CA8-A674-5154267BF422}\\WmpNativeSubtitle" ; const WCHAR kwszPrefsLogging[] = L " LogSettings" ;

This information needs to be propagated to the dialog class so that when user clicks they can see it in updated state. It is why OnInitDialog function of the dialog class retrieves it and updates check button,

if (m_pPlugin) { m_pPlugin->get_log_status(&bLogStatus); if (bLogStatus) SendDlgItemMessage(IDC_CHECK_LOG, BM_SETCHECK, BST_CHECKED, ( int ) bLogStatus); else SendDlgItemMessage(IDC_CHECK_LOG, BM_SETCHECK, BST_UNCHECKED, ( int ) bLogStatus); }

There are two method implemented inside class CWMPNativeSubtitle to retrieve and update log status from other classes,

STDMETHODIMP CWMPNativeSubtitle::get_log_status(BOOL *pVal) { if (NULL == pVal) { return E_POINTER; } *pVal = m_bLogSetting; return S_OK; } STDMETHODIMP CWMPNativeSubtitle::set_log_status(BOOL newVal) { m_bLogSetting = newVal; return S_OK; }

When user enables/disables logging from dialog box it is updated both in registry and CWMPNativeSubtitle class member,

LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hwndCtl, BOOL& fHandled) { int logStatus = 0 ; UINT32 state = IsDlgButtonChecked(IDC_CHECK_LOG); if (state == BST_CHECKED) { logStatus = 1 ; } else { logStatus = 0 ; } CRegKey key; LONG lResult; lResult = key.Create(HKEY_CURRENT_USER, kwszPrefsRegKey); if (ERROR_SUCCESS == lResult) { DWORD dwValue = (DWORD) logStatus; lResult = key.SetValue(kwszPrefsLogging, REG_DWORD, &dwValue, sizeof (dwValue)); } if (m_pPlugin) { m_pPlugin->set_log_status(logStatus); } EndDialog( IDOK ); return 0 ; }

Still, there is scope of improvement, such as, to:

Ensure registry write is done only when value has changed.

Eliminate unnecessary registry read when class member is already updated.

This can be important when you have a number of property members in the class.

Our output SAMI files are encoded in utf-8. Hence, it can be speculated that writing Unicode characters for languages other than English should work and that Windows Media Player should by default be able to display them properly. However, this is only a speculation.

Please have a look at following caption record in an smi file,

<SYNC Start= " 272000" > <p class = " SACAPTION" > Long, long live the walls we crashed through<br> زنده.زنده باد دیوارهایی که در هم شکستیم </p>

When we load it with the relevant media file the rendering of caption looks like the one in screenshot,

Reason can be one of the two. One is that Windows Media Player does not really support utf-8. It only parses ANSI. In that case, when you split a 2 byte Unicode character into two you get two ANSI characters. Media Player is displaying those separated ANSI characters. Other reason can be that, probably we have missed something in documentation, merely putting Unicode chars is not probably the option. Probably, an expert in the field can shed light on it.

SAMI document has limited html support. If Unicode characters are encoded in html format Windows Media Player displays them correctly. Have a look at following caption record in the smi file,

<SYNC Start= " 272000" > <p class = " SACAPTION" > Long, long live the walls we crashed through<br> زنده.زنده باد دیوارهایی که در هم شکستیم </p> </SYNC>

Here's the screenshot when this smi is loaded with relevant media file,

1586, 1606, 1586 etc are the decimal Unicode value for the respective Arabic letter. Let's have a peek into the implementation in source file (SAMIConversion.cpp),

void SubToSAMIConverter::EmbedUnicodeSymbols(LPWSTR pSubStr, LPWSTR pLine) { size_t pSubLen = wcslen(pSubStr); for ( int i = 0 ; pLine[i] != _T( ' \0' ); i++) { if (( unsigned int )pLine[i] <= 0xFF) { pSubStr[pSubLen++] = pLine[i]; } else { pSubStr[pSubLen++] = L ' &' ; pSubStr[pSubLen++] = L ' #' ; WCHAR numbuf[ 12 ]; _itow_s(( int )pLine[i], numbuf, 10 , 10 ); for ( int j = 0 ; numbuf[j] != L ' \0' ; j++) pSubStr[pSubLen++] = numbuf[j]; pSubStr[pSubLen++] = L ' ;' ; } } pSubStr[pSubLen] = L ' \0' ; }

To optimize the plugin, we only call EmbedUnicodeSymbols function only when Unicode character has been encountered before. First string pointer in the parameters of the function is the one that we write to smi file later and second parameter string pointer is the one we found originally in subrip file and converted to Unicode string. So what it basically does, is that, the function,

Iterates till null terminating character of pLine is encountered

For each of the characters in pLine string it checks whether it is in ansi char range (ASCII code 0 to 255)

If the character is in range it is appended to the first string.

Otherwise, it appends '&#' Then appends decimal number found by converting the Unicode value of the character and finally appends a ';'

Here is a tentative list of features to implement,

Currently smi file is created in the same location of media file. If the directory is not writeable Plugin won' be able display caption. Solution is to change smi file location to a temporary directory which will always be writeable. This feature has been implemented and has been discussed on next part of the article.

Add option in properties dialog box to change font, color and other style of subtitle/caption (currently we are doing this by modifying registry manually)

Add option in properties dialog box to change caption height (currently we have to modify registry manually to achieve this)

Create Installer and release both 64 bit and 32 bit

There are two ways to get the source code of the project.

Using the download link from the top of the article page - after downloading extract the zip archive

Using github: Project is located at https://github.com/atiq-cs/wmp-native-subtitle-plugin. For updated source code you can checkout source following instructions at: https://github.com/atiq-cs/wmp-native-subtitle-plugin

If you wish to use 64 bit plugin please set 64 bit Windows Media Player as default. You can find instructions on Microsoft Answers page on how to set 64 bit Windows Media Player as default. If you have downloaded source using attached zip please use the zip archive mentioning x64. Open the solution file with Visual Studio. After building the project we get an output dll file. msdn documentation suggests running plugin project using Visual Studio with administrator privilege. UI Plugin Project generates a dll file which has to be registered into the system using regsvr32 command that requires admin privilege. However, I built the project as regular user. Later, I applied the command manually using a command prompt with admin privilege. Note, you have to provide correct path of the dll file in the command,

regsvr32 " F:\Sourcecodes\Plugin-App\WMPNativeSubtitle\x64\Release\WMPNativeSubtitle_plugin_x64.dll"

When it is successful a dialog box confirms it.

After that you have to ensure whether local caption is enabled in Windows Media Player. Please refer to section: Enabling Caption/Subtitle and follow all of the step to enable the plugin finally.

Instructions for 32 builds are almost same as for 64 bit builds. Except, if you download source attached zip you must download the one mentioning x86. Note, if you acquire the source using github.com's project page you will find that there are two directories containing Visual Studio Project configuration files: one name x64 and other one is x86. x86 directory contains Visual Studio solution and project files for 32 bit. Copy this files and overwrite files in x64 which give you required files for 32 bit build.

After building dll from the code use regsvr32 command to register the dll. regsvr32 works for 32 bit dlls as well. Hence, registering the dll will be an easy work of apply a command with privilege elevated.

By default local captions are not enabled in Windows Media Player.

Step 1 - Turn Local Captions On

To enable captions to be shown from local source, you have to go to options,

Then navigate to "Security" tab and tick the check box saying "Show local captions when present" as displayed in screenshot below,

And click ok to save settings.

Step 2 - Enable English Caption

This step to enable default English caption, lyrics, subtitle is also important. Without enabling it captions won't be displayed.

At this stage subtitle/caption will be displayed if smi file is available with same name as of video file name. However, to enable captions for subrip (.srt) files we have to proceed to next step.

Step 3 - Enable WmpNativeSubtitle Plugin

Our final step is to enable the plugin. To do that, we have to navigate to Plug-Ins tab from More options. And then select "Background" from Category. Then, we must enable the plugin by checking the tick box on the left of the Plugin Name.

At this stage, With all these steps properfly followed Windows Media Player is now capable of displaying Captions for both .srt and .smi files.

Please download proper release dll zip archive (x64 for 64 bit plugin and x86 for 32 bit plugin). A 64 Windows OS (except probably Server 2012) can run both 32 bit and 64 bit application. If you want to use the 64 bit plugin you must set Windows Media Player default following instruction from http://answers.microsoft.com/en-us/windows/forum/windows_7-windows_programs/making-windows-media-player-64-bit-default/bd4872b3-75e8-4d81-ae8a-df50798d5113

After downloading extract the zip archive. There are two dll files inside the extracted directory:

WMPNativeSubtitle_plugin.dll or WMPNativeSubtitle_plugin_x64.dll

msvcr120.dll

For example, if directory path is: F:\Plugin then following command will install/register the 64 bit dll into system (requires elevated/admin command prompt):

regsvr32 " F:\Plugin\WMPNativeSubtitle_plugin_x64.dll"

To install the 32 bit plugin, modify the file name to exclude "_x64",

regsvr32 " F:\Plugin\WMPNativeSubtitle_plugin.dll"

Finally, you must follow instructions from section Enabling Caption/Subtitle to enable caption.

How to change caption height

Windows Media Players caption rendering system, you may note, is bit different. There is a darker background around caption text though transparency is enabled. Default caption height seems to be large. However, it is easy to change it. If you navigate to following registry path,

[HKEY_CURRENT_USER\Software\Microsoft\MediaPlayer\Player\Tasks\NowPlaying] " CaptionsHeight" =dword: 00000064

There is a key named CaptionsHeight under it. 0x64 is large. I use hexadecimal value 0x48, which is comfortable for me.

While there are plenty of audio/video player projects around there is appeal of Windows Media Player to some people. It's clean and simple; coming with the OS this software is still classic favorite to many. While Windows Media Player supports many of the latest video file formats and encoding it provides better hardware acceleration and clarity having advantage of the support from the OS. I hope this article will be useful to fans of Windows Media Player.

Points of Interest

Any kind of suggestion, review to improve this article are welcome. While developing the plugin to make an optimal solution and to implement some components from scratch you might notice I have reinvented some of the wheels (whether this has been better or worse, experts can tell).

Side-note: I created this application almost 8 months ago. I am sorry that I am late to reproduce it here. Sharing is caring. There are other sides to the original app but they should be in different topic, in different article.

I should also mention that while typing this into code-project editor I found a few irritating bugs of the editor such as unnecessary white space coming automatically at end of lines and crash of web-page on Internet Explorer 11 while copy pasting some elements in editor (later, when I reopened the article editor I found almost 700 lines of same content!). Had the technical team have looked into it could make our future experience better.

Thanks everyone.

History

October 2013 - Plug-In developed

May 8, 2014 - article, first revision

June 16, 2014 - article, second revision

June 21, 2014 - article, third revision