Microsoft DirectX 8.1 (C++) |
This document explains how the DVD region selection process works under Microsoft® Windows® 9x and Windows 2000. The first section describes the three sources of DVD Region Information. The second section describes how PC system manufacturers can set a system's initial DVD region. The third section explains how the DVD Navigator determines region match between the drive, decoder and disc. The last section shows how application and decoder developers can implement correct DVD region selection support in their code.
There are three sources of DVD region information that work together to determine the region for DVD playback on a Windows PC.
It is the responsibility of the system manufacturer to select an initial DVD region for the DVD drive in their PCs.
Important Only encrypted discs can be used to set the region.
The system manufacturer can set a Registry key (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\DefaultDVDRegion (binary)) to set the default DVD region for the drive. The value of this key should be set to the region number for which it should be set on first playback. System manufacturers using OPK builds can add this value to the registry during an "audit mode boot," so the end user never sets the initial region. If this key is set before any DVD title is played on a DVD-enabled Windows system, then the Windows components will select that region for the drive on the first attempt to play back a DVD title.
If the system manufacturer sets the default region in the Registry and then plays a multi-region disc that includes the default region, the region is set to the default and the disc just plays. If the disc's region does not match the default region, the user will be prompted to change to the required region. (This will be the only region change allowed for an RPC-1 drive.) If no such default region is specified by the system manufacturer, Windows picks a region based on the Operating System locale, time zone, and other factors. If the first disc played is a multi-region disc, then Windows picks the region based on the locale, time zone, and other factors and looks for a match with the lowest region number. If no region on the multi-region disc matches the region picked by Windows, then the region with the lowest number on disc is selected.
If the manufacturer does not set a region, on first boot the Windows 98 Operating System base components will pick a region based on the Operating System language and the time zone and set the drive to that region. The system manufacturer is free to set the region on the drive and if that is not done during the manufacturing process, it is up to Windows to select the drive's region based on the best guess it can make.
In Windows 2000, the default DVD region is selected based upon the locale that the machine is set up for. Windows 2000 will always set the initial region for a DVD drive using this default region as well as the disc's region, if there is a disc is in the drive. The system manufacturer does not have to do anything to set the initial region for Windows 2000 DVD drives.
For RPC1 drives, if there is no disc in the drive during boot up then the default region is set based only on the locale of the machine. If there is a DVD disc in the drive during boot up, the default region is selected to be the drive's region, provided it matches a region of the disc; otherwise the lowest region on the disc is picked as the initial region of the drive. The user (or system manufacturer) has one remaining change allowed, in case the default was not correct.
For RPC2 drives, if during the setup process Windows 2000 finds that the drive does not have any region set (i.e., fresh drive), it will try to pick a region as above, but only if there is a disc in the drive. (RPC1 drives will choose the region without any disc in drive). Once a region is set for RPC2 drives, it is not auto-changed by either a re-installation or a clean installation of the Operating System.
The registry key on Windows 2000 systems is HKLM\System\CurrentControlSet\Control\Class\<CDROM GUID>\ <instance number>\DefaultDVDRegion(binary) .
The Microsoft DVD Navigator uses the following method to determine region match while playing DVD discs on a PC:
The DVD Navigator gets the disc's region, drive's region and decoder's region. If the disc region is "all regions" then the DVD Navigator plays the disc. If the disc is not marked for all regions, the DVD Navigator checks whether the decoder has a preset region. If the decoder has a preset region and it does not match the disc's region, the DVD Navigator will return an error indicating that it cannot play the disc on the current DVD configuration. If the decoder's region is the same as the disc's region, the DVD Navigator will then check whether the drive's region is the same as the disc's region. If they are the same, the DVD Navigator will play the title. If they are not the same, then the DVD Navigator invokes the code to change the drive's region. If the allowed number of region changes has been exhausted, then the region change attempt fails and the title can't be played on that system.
On Windows 9x, when the DVD Navigator detects that a region change on the DVD drive is necessary, it uses DVDRgn.exe to request a region change on the drive. This application is regionalized along with other components of the operating system. This application is not installed by default under Windows 98. A method of installing it on the target system is demonstrated below.
In Windows 2000, the DVD Navigator invokes a property page on the DVD-ROM drive device in the Device Manager. This property page is also brought up by the DVDPlay.exe application when a region needs to be changed. The UI of this property page is very similar to that of DVDRgn.exe and it is regionalized in the operating system.
For RPC1 drives, the Windows Operating System components manage region changes. RPC2 drives maintain the region setting in their own hardware. When the number of allowed changes are exhausted on a RPC2 drive, the drive will fail the call to change the region and the region-change component in the operating system will indicate that to the user.
Application developers can utilize the DVD region change support built into Windows to change region inside the DVD playback applications. There are two different ways of doing this for the Windows 9x and Windows 2000 platforms.
The following functions are provided for developers who wish to integrate region-change support into their decoders or DVD applications.
/////////////////////////////////////////////////////////////////////
// ChangeDVDRegion : Function to change the DVD drive region.
//
// Parameters:
// In: hWnd - window handle of the application
// that calls this function
// Out: none.
//
// Returns:
// TRUE: if the drive region change is successful
// FALSE: if drive region change fails (probably because
// no drive was found with a valid DVD-V disc).
/////////////////////////////////////////////////////////////////////
BOOL ChangeDVDRegion(HWND hWnd)
{
typedef BOOL (APIENTRY *DVDPPLAUNCHER) (HWND hWnd, CHAR DriveLetter);
BOOL bRegionChanged = FALSE;
TCHAR szCmdLine[MAX_PATH];
// First find out which drive is a DVD drive with a valid DVD-V disc.
TCHAR szDVDDrive[4];
if (! GetDriveLetter (szDVDDrive) )
{
MessageBox(hWnd,
TEXT("No DVD drive was found with DVD-V disc.")
TEXT("\nCannot change DVD region of the drive."),
TEXT("Error"), MB_OK | MB_INFORMATION) ;
return FALSE;
}
// Detect which OS we are running on. For Windows NT, use the storprop.dll,
// while for Windows 9x use the DVDRgn.exe application to change the region.
OSVERSIONINFO ver;
ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&ver);
if (VER_PLATFORM_WIN32_NT == ver.dwPlatformId)
{
// Windows NT platform
HINSTANCE hInstDLL;
DVDPPLAUNCHER dvdPPLauncher;
CHAR szDVDDriveA[4];
#ifdef UNICODE
WideCharToMultiByte(0, 0, szDVDDrive, -1,
szDVDDriveA, sizeof(szDVDDriveA),
NULL, NULL );
#else
strcpy(szDVDDriveA, szDVDDrive);
#endif // UNICODE
GetSystemDirectory(szCmdLine, MAX_PATH);
lstrcat(szCmdLine, TEXT("\\storprop.dll"));
hInstDLL = LoadLibrary (szCmdLine);
if (hInstDLL)
{
dvdPPLauncher = (DVDPPLAUNCHER) GetProcAddress(hInstDLL,
"DvdLauncher");
if (dvdPPLauncher)
{
bRegionChanged = dvdPPLauncher(hWnd, szDVDDriveA[0]);
}
FreeLibrary(hInstDLL);
}
} // end of region change code for Windows NT platform
else // Windows 9x platform
{
// Get path of \windows\dvdrgn.exe for command line string
GetWindowsDirectory(szCmdLine, MAX_PATH);
lstrcat(szCmdLine, TEXT("\\DVDRgn.exe "));
// Add only the drive letter as command line parameter
lstrncat(szCmdLine, szDVDDrive, 1);
// Prepare to execute DVDRgn.exe
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_SHOWNORMAL;
StartupInfo.lpReserved = NULL;
StartupInfo.lpDesktop = NULL;
StartupInfo.lpTitle = NULL;
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = NULL;
if (CreateProcess(csModuleName, szCmdLine,
NULL, NULL, TRUE,
NORMAL_PRIORITY_CLASS,
NULL, NULL, &StartupInfo, &ProcessInfo) )
{
// Wait until DVDRgn.exe finishes
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
DWORD dwRet = 1;
BOOL bRet = GetExitCodeProcess(ProcessInfo.hProcess,
&dwRet);
// If the user changed the drive region
// successfully, the exit code of DVDRgn.exe is 0.
if (bRet && 0 == dwRet)
{
bRegionChanged = TRUE;
}
}
} // end of region change code for Windows 9x platform
if (bRegionChanged) // if region changed successfully
{
// Delete old filter graph and create new filter graph
// via IDvdGraphBuilder::RenderDvdVideoVolume().
}
else // DVD region didn't happen
{
MessageBox(hWnd,
TEXT("DVD drive region could not be changed.")
TEXT("Error"), MB_OK | MB_INFORMATION) ;
}
} //End function ChangeDVDRegion
/////////////////////////////////////////////////////////////////////
// GetDriveLetter : Function to get the drive letter of the currently
// active DVD drive
// Parameters:
// In: pDvdC - pointer to IDvdControl interface of
// DVDNavigator filter..
// Out: pszDrive - the first DVD drive that is found
// to have a valid DVD-V disc.
//
// Returns:
// TRUE: if a DVD drive is found (with valid disc)
// FALSE: if no DVD drive was found with valid DVD-V disc.
/////////////////////////////////////////////////////////////////////
BOOL GetDriveLetter(IDvdControl *pDvdC, TCHAR *pszDrive)
{
CHAR szPathA[MAX_PATH];
TCHAR szPath[MAX_PATH];
ULONG ulActualSize;
pszDrive[0] = pszDrive[3] = 0;
// Get the current root directory
if (pDvdC ->GetRoot(szPathA, MAX_PATH, &ulActualSize))
{
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, szPathA, 0, szPath, MAX_PATH);
#else
lstrcpy(szPath, szPathA);
#endif // UNICODE
lstrcpyn(pszDrive, szPath, 3);
if (DRIVE_CDROM == GetDriveType(pszDrive)) // could be a DVD drive
return TRUE;
}
// Now loop through all the valid drives to detect which one
// is a DVD drive with a valid DVD-V disc in it.
// Get all valid drives
DWORD dwLeng = GetLogicalDriveStrings(MAX_PATH, szPath);
TCHAR *pszTemp = szPath;
// Try all drives one by one
for (DWORD dw = 0; dw < dwLeng; dw += 4)
{
// Look only for CD-ROM drives that has a disc with required (.ifo) files
if (DRIVE_CDROM == GetDriveType(pszTemp))
{
TCHAR szDVDPath1[MAX_PATH]
TCHAR szDVDPath2[MAX_PATH];
lstrcpyn(szDVDPath1, pszTemp, 4);
lstrcpyn(szDVDPath 2, pszTemp, 4);
lstrcat(szDVDPath1, TEXT("Video_ts\\Video_ts.ifo"));
lstrcat(szDVDPath2, TEXT("Video_ts\\Vts_01_0.ifo"));
// If the .ifo files exist on this drive then it has a valid DVD-V disc
if (DoesFileExist(achDVDPath1) && DoesFileExist(achDVDPath2))
{
lstrcpyn(pszDrive, pszTemp, 3);
return TRUE; // return the first drive that has a valid DVD-V disc
}
}
pszTemp += 4;
}
return FALSE ; // didn't find any DVD drive (with DVD-V content)
} //End function GetDriveLetter
/////////////////////////////////////////////////////////////////////
// DoesFileExist : Function to determine if the given filename is an existing file.
//
// Parameters:
// In: pszFile - filename string to check for existence.
// Out: none.
//
// Returns:
// TRUE: if the specified file is found.
// FALSE: if the specified file is not found.
/////////////////////////////////////////////////////////////////////
BOOL DoesFileExist(LPTSTR pszFile)
{
HANDLE hFile = NULL ;
// We don't want any error message box to pop up when we try to test
// if the required file is available to open for read.
UINT uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS |
SEM_NOOPENFILEERRORBOX);
hFile = CreateFile(pszFile, GENERIC_READ,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
SetErrorMode(uErrorMode); // restore error mode
If (INVALID_HANDLE_VALUE == hFile)
return FALSE ;
CloseHandle(hFile);
return TRUE;
} //End function DoesFileExist