Problem with Libburn and writing CD

Hi everyone!

I’m trying to write a basic Python code that write a single song on a CD-RW. Here is my code:

import os
import sys
import ctypes
from enum import IntEnum
from time import sleep


# Define libburn classes
class BurnDrive(ctypes.Structure):
    pass


class BurnDisc(ctypes.Structure):
    pass


class BurnSession(ctypes.Structure):
    pass


class BurnTrack(ctypes.Structure):
    pass


class BurnSource(ctypes.Structure):
    pass


class BurnDriveInfo(ctypes.Structure):
    _fields_ = [
        ("vendor", ctypes.c_char * 9),
        ("product", ctypes.c_char * 17),
        ("revision", ctypes.c_char * 5),
        ("location", ctypes.c_char * 17),

        ("read_dvdram", ctypes.c_uint, 1),
        ("read_dvdr", ctypes.c_uint, 1),
        ("read_dvdrom", ctypes.c_uint, 1),
        ("read_cdr", ctypes.c_uint, 1),
        ("read_cdrw", ctypes.c_uint, 1),

        ("write_dvdram", ctypes.c_uint, 1),
        ("write_dvdr", ctypes.c_uint, 1),
        ("write_cdr", ctypes.c_uint, 1),
        ("write_cdrw", ctypes.c_uint, 1),

        ("write_simulate", ctypes.c_uint, 1),
        ("c2_error", ctypes.c_uint, 1),
        ("buffer_size", ctypes.c_int),

        ("tao_block_types", ctypes.c_int),
        ("sao_block_types", ctypes.c_int),
        ("raw_block_types", ctypes.c_int),
        ("packet_block_types", ctypes.c_int),

        ("drive", ctypes.c_void_p)
    ]


class BurnProgress(ctypes.Structure):
    _fields_ = [
        ("revision", ctypes.c_int),
        ("sessions", ctypes.c_int),
        ("session", ctypes.c_int),
        ("tracks", ctypes.c_int),
        ("track", ctypes.c_int),
        ("indices", ctypes.c_int),
        ("index", ctypes.c_int),
        ("start_sector", ctypes.c_int64),
        ("sectors", ctypes.c_int64),
        ("sector", ctypes.c_int64),
        ("buffer_capacity", ctypes.c_int64),
        ("buffer_available", ctypes.c_int64),
        ("buffered_bytes", ctypes.c_int64),
        ("buffer_min_fill", ctypes.c_int64)
    ]


class BurnDiscStatus(IntEnum):
    UNREADY = 0
    BLANK = 1
    EMPTY = 2
    APPENDABLE = 3
    FULL = 4
    UNGRABBED = 5
    UNSUITABLE = 6


class BurnDriveStatus(IntEnum):
    IDLE = 0
    SPAWNING = 1
    READING = 2
    WRITING = 3
    WRITING_LEADIN = 4
    WRITING_LEADOUT = 5
    ERASING = 6
    GRABBING = 7
    WRITING_PREGAP = 8
    CLOSING_TRACK = 9
    CLOSING_SESSION = 10
    FORMATTING = 11
    READING_SYNC = 12
    WRITING_SYNC = 13


class BurnSourceStatus(IntEnum):
    OK = 0
    EOF = 1
    FAILED = 2


class BurnWriteOpts(ctypes.Structure):
    pass


# Main code
libburn = ctypes.CDLL("binaries/libburn.so.4")

libburn.burn_initialize.restype = ctypes.c_int
libburn.burn_initialize.argtypes = None
libburn.burn_initialize()

# libburn.burn_set_scsi_logging(3)
# libburn.burn_set_verbosity(2)

DriveInfoPtr = ctypes.POINTER(BurnDriveInfo)

libburn.burn_drive_scan.restype = ctypes.c_int
libburn.burn_drive_scan.argtypes = (
    ctypes.POINTER(DriveInfoPtr),
    ctypes.POINTER(ctypes.c_uint)
)

drives_info = DriveInfoPtr()
n_drives = ctypes.c_uint()

res = libburn.burn_drive_scan(
    ctypes.byref(drives_info),
    ctypes.byref(n_drives)
)
sleep(0.1)

print("Nombre de lecteurs/graveurs :", n_drives.value)
if n_drives.value == 0:
    sys.exit(0)

drive_info = drives_info[0]
# Exemple de string : print(f"Vendor: [{drive_info.vendor.decode('utf-8')}]")
# Exemple de int : print(f"Read DVD-RAM: [{drive_info.read_dvdram}]")
burn_drive = drive_info.drive

# libburn.burn_drive_get_write_speed.restype = ctypes.c_int
# libburn.burn_drive_get_write_speed.argtypes = (ctypes.c_void_p, )
# burn_drive_write_speed = libburn.burn_drive_get_write_speed(burn_drive)

libburn.burn_drive_get_status_v2.restype = ctypes.c_int
libburn.burn_drive_get_status_v2.argtypes = (ctypes.c_void_p, ctypes.c_void_p)
# On donne None car on ne s'occupe pas de la progression
burn_drive_status = libburn.burn_drive_get_status_v2(burn_drive, None)
burn_drive_status = BurnDriveStatus(burn_drive_status)
print(f"Status du lecteur/graveur : {burn_drive_status.name}")

libburn.burn_drive_grab.restype = ctypes.c_int
libburn.burn_drive_grab.argtypes = (ctypes.c_void_p, ctypes.c_int)
res = libburn.burn_drive_grab(burn_drive, ctypes.c_int(0))
print(f"Lecteur/graveur disponible : {bool(res)}")

libburn.burn_disc_get_status.restype = ctypes.c_int
libburn.burn_disc_get_status.argtypes = (ctypes.c_void_p, )
status_result = libburn.burn_disc_get_status(burn_drive)
burn_disc_status = BurnDiscStatus(status_result)
print(f"Status du disque : {burn_disc_status.name}")

if burn_disc_status == BurnDiscStatus.EMPTY:
    sys.exit(0)

libburn.burn_disc_get_profile.restype = ctypes.c_int
libburn.burn_disc_get_profile.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(ctypes.c_int),
    ctypes.POINTER(ctypes.c_char)
)

profile_number = ctypes.c_int()
profile_name = ctypes.create_string_buffer(80)

res = libburn.burn_disc_get_profile(
    burn_drive,
    ctypes.byref(profile_number),
    profile_name
)
print(f"Type de disque : {profile_name.value.decode('utf-8')}")

libburn.burn_write_opts_new.restype = ctypes.POINTER(BurnWriteOpts)
libburn.burn_write_opts_new.argtypes = (ctypes.c_void_p, )
write_opts = libburn.burn_write_opts_new(burn_drive)

BURN_WRITE_TAO = 1
libburn.burn_write_opts_set_write_type.restype = ctypes.c_int
libburn.burn_write_opts_set_write_type.argtypes = (
    ctypes.POINTER(BurnWriteOpts),
    ctypes.c_int,
    ctypes.c_int
)
libburn.burn_write_opts_set_write_type(
    write_opts,
    ctypes.c_int(BURN_WRITE_TAO),
    ctypes.c_int(0)
)

libburn.burn_write_opts_set_multi.restype = None
libburn.burn_write_opts_set_multi.argtypes = (
    ctypes.POINTER(BurnWriteOpts),
    ctypes.c_int
)
libburn.burn_write_opts_set_multi(write_opts, 0)

libburn.burn_drive_set_speed.restype = None
libburn.burn_drive_set_speed.argtypes = (
    ctypes.c_void_p,
    ctypes.c_int,
    ctypes.c_int
)
libburn.burn_drive_set_speed(burn_drive, 0, 0)

# ctypes.c_int64 est important pour les off_t car
# Libburn est compilé en 64 bits
libburn.burn_disc_available_space.restype = ctypes.c_int64
libburn.burn_disc_available_space.argtypes = (ctypes.c_void_p, ctypes.c_void_p)
disc_space = libburn.burn_disc_available_space(burn_drive, write_opts)
print(f"Place disponible : {disc_space / 10 ** 9} Go")

libburn.burn_disc_erasable.restype = ctypes.c_int
libburn.burn_disc_erasable.argtypes = (ctypes.c_void_p, )
is_erasable = libburn.burn_disc_erasable(burn_drive)
is_erasable = bool(is_erasable)
print(f"Disque effaçable : {is_erasable}")

# Create a virtual disc to write
libburn.burn_disc_create.restype = ctypes.POINTER(BurnDisc)
libburn.burn_disc_create.argtypes = ()
disc = libburn.burn_disc_create()

libburn.burn_session_create.restype = ctypes.POINTER(BurnSession)
libburn.burn_session_create.argtypes = ()
session = libburn.burn_session_create()

libburn.burn_track_create.restype = ctypes.POINTER(BurnTrack)
libburn.burn_track_create.argtypes = ()
track = libburn.burn_track_create()

BURN_AUDIO = 64
BURN_COPY = 256

libburn.burn_track_define_data.restype = None
libburn.burn_track_define_data.argtypes = (
    ctypes.POINTER(BurnTrack),
    ctypes.c_int,
    ctypes.c_int,
    ctypes.c_int,
    ctypes.c_int
)
libburn.burn_track_define_data(
    track,
    ctypes.c_int(0),
    ctypes.c_int(0),
    ctypes.c_int(1),
    ctypes.c_int(BURN_AUDIO | BURN_COPY)
)

libburn.burn_file_source_new.restype = ctypes.POINTER(BurnSource)
libburn.burn_file_source_new.argtypes = (
    ctypes.c_char_p,
    ctypes.c_char_p
)
source = libburn.burn_file_source_new(
    ctypes.c_char_p(b"/home/eric/Projects/tests-libburn/audio.raw"),
    None
)

libburn.burn_track_set_source.restype = ctypes.c_int
libburn.burn_track_set_source.argtypes = (
    ctypes.POINTER(BurnTrack),
    ctypes.POINTER(BurnSource)
)
source_status = libburn.burn_track_set_source(track, source)
source_status = BurnSourceStatus(source_status).name
print(f"Source accessible : {source_status}")

file_size = os.path.getsize("/home/eric/Projects/tests-libburn/audio.raw")
libburn.burn_track_set_size.restype = ctypes.c_int
libburn.burn_track_set_size.argtypes = (
    ctypes.POINTER(BurnTrack),
    ctypes.c_int64
)
libburn.burn_track_set_size(track, ctypes.c_int64(file_size))

BURN_POS_END = 100

libburn.burn_session_add_track.restype = ctypes.c_int
libburn.burn_session_add_track.argtypes = (
    ctypes.POINTER(BurnSession),
    ctypes.POINTER(BurnTrack),
    ctypes.c_uint
)
res = libburn.burn_session_add_track(
    session,
    track,
    ctypes.c_uint(BURN_POS_END)
)

libburn.burn_disc_add_session.restype = ctypes.c_int
libburn.burn_disc_add_session.argtypes = (
    ctypes.POINTER(BurnDisc),
    ctypes.POINTER(BurnSession),
    ctypes.c_uint
)
res = libburn.burn_disc_add_session(
    disc,
    session,
    ctypes.c_uint(BURN_POS_END)
)

# Simulate disc writing
BURN_REASONS_LEN = 4096
reasons = ctypes.create_string_buffer(BURN_REASONS_LEN)

libburn.burn_precheck_write.restype = ctypes.c_int
libburn.burn_precheck_write.argtypes = (
    ctypes.POINTER(BurnWriteOpts),
    ctypes.POINTER(BurnDisc),
    ctypes.c_char_p,
    ctypes.c_int
)
res = libburn.burn_precheck_write(
    write_opts,
    disc,
    reasons,
    ctypes.c_int(0)
)
print(f"Gravure possible : {bool(res)}")
print(f"Raisons : \n{reasons.value.decode("utf-8")}")

if bool(res):
    response = input("Do you want to start the burning test? Please answer 'yes' or 'no'.\n")
    if (response == "no") or (response != "yes"):
        sys.exit(0)

    libburn.burn_disc_write.restype = None
    libburn.burn_disc_write.argtypes = (
        ctypes.POINTER(BurnWriteOpts),
        ctypes.POINTER(BurnDisc)
    )
    libburn.burn_disc_write(write_opts, disc)

    while True:
        libburn.burn_drive_get_status_v2.restype = ctypes.c_int
        libburn.burn_drive_get_status_v2.argtypes = (ctypes.c_void_p, ctypes.c_void_p)
        status = libburn.burn_drive_get_status_v2(burn_drive, None)
        status = BurnDriveStatus(status)
        print(f"Statut actuel : {status.name}")

        if status == BurnDriveStatus.IDLE:
            break

        sleep(1)

# Free all assigned resources
libburn.burn_write_opts_free.restype = None
libburn.burn_write_opts_free.argtypes = (ctypes.POINTER(BurnWriteOpts), )
libburn.burn_write_opts_free(write_opts)

libburn.burn_disc_free.restype = None
libburn.burn_disc_free.argtypes = (ctypes.POINTER(BurnDisc), )
libburn.burn_disc_free(disc)

libburn.burn_session_free.restype = None
libburn.burn_session_free.argtypes = (ctypes.POINTER(BurnSession), )
libburn.burn_session_free(session)

libburn.burn_track_free.restype = None
libburn.burn_track_free.argtypes = (ctypes.POINTER(BurnTrack), )
libburn.burn_track_free(track)

libburn.burn_source_free.restype = None
libburn.burn_source_free.argtypes = (ctypes.POINTER(BurnSource), )
libburn.burn_source_free(source)

libburn.burn_drive_release.restype = None
libburn.burn_drive_release.argtypes = (ctypes.c_void_p, ctypes.c_int)
libburn.burn_drive_release(burn_drive, ctypes.c_int(0))

libburn.burn_finish()

And here is my output:

Nombre de lecteurs/graveurs : 1
Status du lecteur/graveur : IDLE
Lecteur/graveur disponible : True
Status du disque : BLANK
Type de disque : CD-RW
Place disponible : 0.736966656 Go
Disque effaçable : True
Source accessible : OK
Gravure possible : True
Raisons : 
CD-RW: 
Do you want to start the burning test? Please answer 'yes' or 'no'.
yes
Statut actuel : IDLE

I’m mostly experiencing, but the burn_disc_write() function seems to do nothing.

And removing the ```status == BurnDriveStatus.IDLE``` condition just creates an infinite loop.

Can someone explain me why is nothing happening?

Thanks for your help!