Simple DVD
This topic describes the steps needed to create a DVD from an MP4 video.
Project Directory
You need a directory to store the source video and the DVDBuilder project. This can be anywhere on your machine. For example create C:\ElephantDVD
directory. We will refer to that as the DVD Project Directory
.
Source Video
Download the clip Elephant_512kb.mp4
from the Internet Archive to the DVD Project Directory
.
Convert Source Video To MPEG-2
DVDBuilder accepts only MPEG-2 Program Stream as input, but you can use AVBlocks to convert the source video from MP4 to MPG (MPEG-2 Program Stream). AVBlocks is already included with DVDBuilder.
wstring source_mp4 = fs::path(DVDProjectDirectory).append(L"Elephant_512kb.mp4");
wstring source_mpeg2 = fs::path(DVDProjectDirectory).append(L"Elephant.mpg");
convert_source_to_mpeg2(source_mp4, source_mpeg2);
namespace p = primo;
namespace avb = primo::avblocks;
namespace fs = experimental::filesystem;
void convert_source_to_mpeg2(wstring input_file, wstring output_file)
{
using namespace avb;
using namespace avb::Library;
if (fs::exists(output_file))
fs::remove(output_file);
auto inputInfo = p::make_ref(
createMediaInfo()
);
inputInfo->setInputFile(input_file.c_str());
if (inputInfo->load()) {
auto inputSocket = p::make_ref(
createMediaSocket(inputInfo.get())
);
auto outputSocket = p::make_ref(
createMediaSocket(Preset::Video::DVD::NTSC_16x9_PCM)
);
outputSocket->setFile(output_file.c_str());
auto transcoder = p::make_ref(
createTranscoder()
);
transcoder->setAllowDemoMode(true);
transcoder->inputs()->add(inputSocket.get());
transcoder->outputs()->add(outputSocket.get());
if (transcoder->open()) {
transcoder->run();
transcoder->close();
}
}
}
Create DVDBuilder Project
DVDBuilder project is an XML file that describes a video DVD.
In the DVD Project Directory
create a file project.xml
with the following contents:
<?xml version='1.0' encoding='utf-8'?>
<dvd version='2.3' xmlns='http://www.primosoftware.com/dvdbuilder/2.3'>
<videoManager firstPlayNavigate='Title=1'/>
<titleSet>
<titles>
<title id='1' chapters='00:00:00;'>
<videoObject file='Elephant.mpg'/>
</title>
</titles>
</titleSet>
</dvd>
Build DVD Structures
Simply create a DVDBuilder object, set the ProjectFile
and OutputFolder
properties, and call the Build
method.
wstring project = fs::path(DVDProjectDirectory).append(L"project.xml");
wstring dvd_files = fs::path(DVDProjectDirectory).append(L"dvd");
build_dvd_structures(project, dvd_files);
namespace p = primo;
namespace dvdb = primo::dvdbuilder;
namespace fs = experimental::filesystem;
void build_dvd_structures(wstring project, wstring dvd_files)
{
using namespace dvdb::Library;
if (fs::exists(dvd_files))
fs::remove_all(dvd_files);
auto dvdBuilder = p::make_ref(
createDVDBuilder()
);
dvdBuilder->setProjectFile(project.c_str());
dvdBuilder->setOutputFolder(dvd_files.c_str());
dvdBuilder->build();
}
Burn DVD
You can use PrimoBurner for DVD burning. A limited version of PrimoBurner, that supports DVD burning only, is included with DVDBuilder.
Create Engine
namespace p = primo;
namespace pb = primo::burner;
void burn_to_dvd(wstring output, char drive_letter)
{
using namespace pb::Library;
auto engine = p::make_ref(
createEngine()
);
engine->initialize();
burn_to_dvd(engine.get(), drive_letter, output);
engine->shutdown();
}
Create Device
namespace p = primo;
namespace pb = primo::burner;
void burn_to_dvd(pb::Engine* engine, char drive_letter, wstring dvd_files)
{
using namespace pb::Library;
int deviceIndex = driveLetterToDeviceIndex(drive_letter);
auto enumerator = p::make_ref(
engine->createDeviceEnumerator()
);
auto device = p::make_ref(
enumerator->createDevice(deviceIndex, true)
);
if (device) {
burn_to_dvd(device.get(), dvd_files);
}
}
Use VideoDVD To Prepare DVD Layout
namespace p = primo;
namespace pb = primo::burner;
void burn_to_dvd(pb::Device* device, wstring dvd_files)
{
using namespace pb;
using namespace pb::Library;
// Use VideoDVD to prepare the DVD image layout
auto videoDVD = p::make_ref(
createVideoDVD()
);
// set the dvd files as layout
videoDVD->setImageLayoutFromFolder(dvd_files.c_str());
videoDVD->setValidation(VideoDVDValidation::Minimal);
// get the actual DVD layout
auto preparedImageLayout = p::make_ref(
videoDVD->copyImageLayout()
);
// Burn
burn_to_dvd(device, preparedImageLayout.get());
}
Burn DVD Layout
namespace p = primo;
namespace pb = primo::burner;
filetime_t to_filetime(const std::chrono::system_clock::time_point& tp);
void burn_to_dvd(pb::Device* device, pb::DataFile* imageLayout)
{
using namespace pb;
using namespace pb::Library;
if (!check_device_and_media(device))
{
wcout << L"Please insert a DVD-R or DVD+R in burner and try again." << endl;
// media is not DVD+R or DVD-R, or not blank
device->eject(true, true);
return;
}
// Use DataDisc to burn the DVD image layout
auto dataDisc = p::make_ref(
createDataDisc()
);
// set device, and write method
dataDisc->setDevice(device);
dataDisc->setWriteMethod(WriteMethod::DVDDao);
// close the disc for true playback compatibility
// this will result in slower disc burning
dataDisc->setCloseDisc(true);
// DataDisc needs this to create the correct disc image
dataDisc->setDVDVideo(true);
// set volume label and creation date
auto creationTime = to_filetime(chrono::system_clock::now());
auto volumeLabel = L"DVDVIDEO";
dataDisc->isoVolumeProps()->setVolumeLabel(volumeLabel);
dataDisc->isoVolumeProps()->setVolumeCreationTime(creationTime);
dataDisc->udfVolumeProps()->setVolumeLabel(volumeLabel);
dataDisc->udfVolumeProps()->setVolumeCreationTime(creationTime);
// set the image layout as prepared by VideoDVD
dataDisc->setImageLayout(imageLayout);
// this may take some time
dataDisc->writeToDisc(true);
}
Complete C++ Code
Here is the complete code.
// SimpleDVD.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
using namespace std;
// or you can use boost::filesystem, http://www.boost.org
namespace fs = experimental::filesystem;
namespace p = primo;
namespace avb = primo::avblocks;
namespace dvdb = primo::dvdbuilder;
namespace pb = primo::burner;
const wstring DVDProjectDirectory = L"C:/ElephantDVD";
const char DVDBurnerDriveLetter = 'E';
void convert_source_to_mpeg2(wstring inputFile, wstring outputFile);
void build_dvd_structures(wstring project, wstring dvdFiles);
void burn_to_dvd(wstring output, char driveLetter);
int main()
{
// Video encoding
primo::avblocks::Library::initialize();
primo::avblocks::Library::setLicense("license_xml_string");
// DVD authoring
primo::dvdbuilder::Library::setLicense("license_xml_string");
// DVD burning
primo::burner::Library::setLicense("license_xml_string");
// Step 1: Convert sources
wstring source_mp4 = fs::path(DVDProjectDirectory).append(L"Elephant_512kb.mp4");
wstring source_mpeg2 = fs::path(DVDProjectDirectory).append(L"Elephant.mpg");
convert_source_to_mpeg2(source_mp4, source_mpeg2);
// Step 2: Build DVD structures
wstring project = fs::path(DVDProjectDirectory).append(L"project.xml");
wstring dvd_files = fs::path(DVDProjectDirectory).append(L"dvd");
build_dvd_structures(project, dvd_files);
// Step 3: Burn to DVD
burn_to_dvd(dvd_files, DVDBurnerDriveLetter);
primo::avblocks::Library::shutdown();
return 0;
}
// Convert sources
void convert_source_to_mpeg2(wstring input_file, wstring output_file)
{
using namespace avb;
using namespace avb::Library;
if (fs::exists(output_file))
fs::remove(output_file);
auto inputInfo = p::make_ref(
createMediaInfo()
);
inputInfo->setInputFile(input_file.c_str());
if (inputInfo->load()) {
auto inputSocket = p::make_ref(
createMediaSocket(inputInfo.get())
);
auto outputSocket = p::make_ref(
createMediaSocket(Preset::Video::DVD::NTSC_16x9_PCM)
);
outputSocket->setFile(output_file.c_str());
auto transcoder = p::make_ref(
createTranscoder()
);
transcoder->setAllowDemoMode(true);
transcoder->inputs()->add(inputSocket.get());
transcoder->outputs()->add(outputSocket.get());
if (transcoder->open()) {
transcoder->run();
transcoder->close();
}
}
}
// Build DVD structures
void build_dvd_structures(wstring project, wstring dvd_files)
{
using namespace dvdb::Library;
if (fs::exists(dvd_files))
fs::remove_all(dvd_files);
auto dvdBuilder = p::make_ref(
createDVDBuilder()
);
dvdBuilder->setProjectFile(project.c_str());
dvdBuilder->setOutputFolder(dvd_files.c_str());
dvdBuilder->build();
}
// Burn to DVD
void wait_for_unit_ready(pb::Device* device)
{
using namespace primo::scsi;
int error = device->unitReadyState();
while (true)
{
if (ScsiSense::Success == error)
break;
if (ScsiSense::MediumNotPresent == error)
break;
if (ScsiSense::MediumNotPresentTrayClosed == error)
break;
if (ScsiSense::MediumNotPresentTrayOpen == error)
break;
this_thread::sleep_for(1s);
error = device->unitReadyState();
}
}
bool check_device_and_media(pb::Device* device)
{
using namespace pb;
// close the device tray and refresh disc information
if (device->eject(false))
{
// wait for the device to become ready
wait_for_unit_ready(device);
// refresh disc information. Need to call this method when media changes
device->refresh();
}
// check if disc is present
if (MediaReady::Present != device->mediaState())
return false;
// check if disc is blank
if (!device->isMediaBlank())
return false;
// for simplicity only accept DVD-R and DVD+R
auto mp = device->mediaProfile();
if (MediaProfile::DVDPlusR != mp && MediaProfile::DVDMinusRSeq != mp)
return false;
return true;
}
// see the full definition below
filetime_t to_filetime(const std::chrono::system_clock::time_point& tp);
void burn_to_dvd(pb::Device* device, pb::DataFile* imageLayout)
{
using namespace pb;
using namespace pb::Library;
if (!check_device_and_media(device))
{
wcout << L"Please insert a DVD-R or DVD+R in burner and try again." << endl;
// media is not DVD+R or DVD-R, or not blank
device->eject(true, true);
return;
}
// Use DataDisc to burn the DVD image layout
auto dataDisc = p::make_ref(
createDataDisc()
);
// set device, and write method
dataDisc->setDevice(device);
dataDisc->setWriteMethod(WriteMethod::DVDDao);
// close the disc for true playback compatibility
// this will result in slower disc burning
dataDisc->setCloseDisc(true);
// DataDisc needs this to create the correct disc image
dataDisc->setDVDVideo(true);
// set volume label and creation date
auto creationTime = to_filetime(chrono::system_clock::now());
auto volumeLabel = L"DVDVIDEO";
dataDisc->isoVolumeProps()->setVolumeLabel(volumeLabel);
dataDisc->isoVolumeProps()->setVolumeCreationTime(creationTime);
dataDisc->udfVolumeProps()->setVolumeLabel(volumeLabel);
dataDisc->udfVolumeProps()->setVolumeCreationTime(creationTime);
// set the image layout as prepared by VideoDVD
dataDisc->setImageLayout(imageLayout);
// this may take some time
dataDisc->writeToDisc(true);
}
void burn_to_dvd(pb::Device* device, wstring dvd_files)
{
using namespace pb;
using namespace pb::Library;
// Use VideoDVD to prepare the DVD image layout
auto videoDVD = p::make_ref(
createVideoDVD()
);
// set the dvd files as layout
videoDVD->setImageLayoutFromFolder(dvd_files.c_str());
videoDVD->setValidation(VideoDVDValidation::Minimal);
// get the actual DVD layout
auto preparedImageLayout = p::make_ref(
videoDVD->copyImageLayout()
);
// Burn
burn_to_dvd(device, preparedImageLayout.get());
}
void burn_to_dvd(pb::Engine* engine, char drive_letter, wstring dvd_files)
{
using namespace pb::Library;
int deviceIndex = driveLetterToDeviceIndex(drive_letter);
auto enumerator = p::make_ref(
engine->createDeviceEnumerator()
);
auto device = p::make_ref(
enumerator->createDevice(deviceIndex, true)
);
if (device) {
burn_to_dvd(device.get(), dvd_files);
}
}
void burn_to_dvd(wstring output, char drive_letter)
{
using namespace pb::Library;
auto engine = p::make_ref(
createEngine()
);
engine->initialize();
burn_to_dvd(engine.get(), drive_letter, output);
engine->shutdown();
}
// Utilities
/**
Converts a time_point to filetime_t (FILETIME)
@ref https://msdn.microsoft.com/en-us/library/windows/desktop/ms724228%28v=vs.85%29.aspx
*/
filetime_t to_filetime(const std::chrono::system_clock::time_point& tp)
{
time_t t = std::chrono::system_clock::to_time_t(tp);
int64_t ll = Int32x32To64(t, 10000000) + 116444736000000000;
filetime_t ft = { 0, 0 };
ft.dwLowDateTime = static_cast<uint32_t>(ll);
ft.dwHighDateTime = static_cast<uint32_t>(ll >> 32);
return ft;
}
Last updated on March 1st, 2016 12:00:00 AM