Burn Directory to DVD+R#

Burn the contents of a directory to a blank DVD+R disc.

Source Directory#

The sample code in this article assumes a directory named DataDisc exists under your user home directory. To create the DataDisc directory follow the steps listed in the Source Directory article.

Code#

The following function takes a Device object and path to a directory and burns the contents of the directory to a blank DVD+R disc.

void burn_dir_to_dvd_plus_r(pb::Device* device, std::string source_dir)
{
    using namespace std;

    if (!check_device_and_media(device))
    {
        cout << "Please insert a blank DVD+R in burner and try again." << endl;

        // media is not DVD+R, or is not blank
        device->eject(true, true);

        return;
    }

    // Use DataDisc to burn the DVD image layout
    {
        auto dataDisc = p::make_ref(pb::Library::createDataDisc());

        // set device, and write method
        dataDisc->setDevice(device);

        // WriteMethod.DvdIncremental method is recommended for data 
        dataDisc->setWriteMethod(pb::WriteMethod::DVDIncremental);

        // 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->setImageType(pb::ImageTypeFlags::Udf);

        // set volume label and creation date
        auto volume_label = p::ustring("DATADVDPLUS");
        dataDisc->udfVolumeProps()->setVolumeLabel(volume_label.u16());

        auto now = chrono::system_clock::now();
        time_t now_time = chrono::system_clock::to_time_t(now);
        dataDisc->udfVolumeProps()->setVolumeCreationTime(now_time);

        // set the image layout from the source directory
        auto source_dir_ustr = p::ustring(source_dir);
        dataDisc->setImageLayoutFromFolder(source_dir_ustr.u16());

        // this may take some time
        dataDisc->writeToDisc(true);
    }
}

Complete C++ Code#

#include "primo/burner/pb_api.h"
#include <primo/burner/pb.h>
#include <primo/burner/pb_lib.h>
#include <primo/scsi/scsi2.h>

#include <primo/platform/ustring.h>
#include <primo/platform/reference++.h>

#include <cstdlib>

#include <chrono>
#include <iostream>
#include <filesystem>
#include <format>
#include <thread>

namespace p = primo;
namespace pb = primo::burner;
namespace ps = primo::scsi;

void wait_for_unit_ready(pb::Device* device)
{
    using namespace std;

    uint32_t error = device->unitReadyState();
    while (true)
    {
        if (ps::ScsiSense::Success == error)
            break;

        if (ps::ScsiSense::MediumNotPresent == error)
            break;

        if (ps::ScsiSense::MediumNotPresentTrayClosed == error)
            break;

        if (ps::ScsiSense::MediumNotPresentTrayOpen == error)
            break;

        this_thread::sleep_for(chrono::milliseconds(1000));    
        
        error = device->unitReadyState();
    }
}

bool check_device_and_media(pb::Device* device)
{
    // 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 (pb::MediaReady::Present != device->mediaState())
        return false;

    // check if disc is blank
    if (!device->isMediaBlank())
        return false;

    // for simplicity only accept DVD+R
    pb::MediaProfile::Enum mp = device->mediaProfile();
    if (pb::MediaProfile::DVDPlusR != device->mediaProfile())
        return false;

    return true;
}

void burn_dir_to_dvd_plus_r(pb::Device* device, std::string source_dir)
{
    using namespace std;

    if (!check_device_and_media(device))
    {
        cout << "Please insert a blank DVD+R in burner and try again." << endl;

        // media is not DVD+R, or is not blank
        device->eject(true, true);

        return;
    }

    // Use DataDisc to burn the DVD image layout
    {
        auto dataDisc = p::make_ref(pb::Library::createDataDisc());

        // set device, and write method
        dataDisc->setDevice(device);

        // WriteMethod.DvdIncremental method is recommended for data 
        dataDisc->setWriteMethod(pb::WriteMethod::DVDIncremental);

        // 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->setImageType(pb::ImageTypeFlags::Udf);

        // set volume label and creation date
        auto volume_label = p::ustring("DATADVDPLUS");
        dataDisc->udfVolumeProps()->setVolumeLabel(volume_label.u16());

        auto now = chrono::system_clock::now();
        time_t now_time = chrono::system_clock::to_time_t(now);
        dataDisc->udfVolumeProps()->setVolumeCreationTime(now_time);

        // set the image layout from the source directory
        auto source_dir_ustr = p::ustring(source_dir);
        dataDisc->setImageLayoutFromFolder(source_dir_ustr.u16());

        // this may take some time
        dataDisc->writeToDisc(true);
    }
}

void burn_dir_to_dvd_plus_r(pb::DeviceEnum* enumerator, std::string source_dir)
{
    // Select the first optical drive
    int device_index = 0;

    // You could also get an index from a drive letter, e.g.:
    // int device_index = pb::Library::driveLetterToDeviceIndex('E');

    // create a device; this time _ask_ for exclusive access
    auto device = p::make_ref(enumerator->createDevice(device_index, true));
        
    if (device) {
        burn_dir_to_dvd_plus_r(device.get(), source_dir);
    }
}


void burn_dir_to_dvd_plus_r(pb::Engine* engine, std::string source_dir)
{
    using namespace std;

    // create device enumerator
    auto enumerator = p::make_ref(engine->createDeviceEnumerator());

    for (int i = 0; i < enumerator->count(); i++) {
        // create a device; do not ask for exclusive access
        auto device = p::make_ref(enumerator->createDevice(i, false));
        
        if (device) {
            auto description = p::ustring(device->description());
            cout << format(" ( {}:) - {}", device->driveLetter(), description.str()) << endl;
        }
    }

    burn_dir_to_dvd_plus_r(enumerator.get(), source_dir);
}

void burn_dir_to_dvd_plus_r(std::string source_dir)
{
    // Create engine
    auto engine = p::make_ref(pb::Library::createEngine());

    // Initialize engine
    engine->initialize();

    burn_dir_to_dvd_plus_r(engine.get(), source_dir);

    // terminate engine
    engine->shutdown();
}


std::filesystem::path get_source_directory()
{
    const char* homeDir = std::getenv("HOME");
    if (homeDir != nullptr) {
        return std::filesystem::path(homeDir) / "DataDisc";
    }
    
    return std::filesystem::path();    
}

int main()
{
    // Set license. To run PrimoBurner in demo mode, comment the next line out
    pb::Library::setLicense("license-xml-string");
    
    pb::Library::enableTraceLog();
    
    auto source_dir = get_source_directory();
    if (source_dir.empty()) {
        std::cout << "Could not get user's home directory." << std::endl;
        return 1;
    }

    burn_dir_to_dvd_plus_r(source_dir.c_str());
    
    pb::Library::disableTraceLog();

    return 0;
}