Libburnia is an open-source project for reading, mastering and writing optical discs. This page is about its capability to handle optical media. For now this means CD-R, CD-RW, DVD-RAM, DVD+RW, DVD+R, DVD+R/DL, DVD-RW, DVD-R, DVD-R/DL, BD-R, BD-RE.
Our scope is currently Linux 2.4 and 2.6, or FreeBSD, or Solaris . For ports to other systems we would need : login on a development machine resp. an OS ithat is installable on an AMD 64-bit PC, advise from a system person about the equivalent of Linux sg or FreeBSD CAM, volunteers for testing of realistic use cases.
libburn is the library by which preformatted data get onto optical media. Its code is independent of cdrecord. Its DVD capabilities are learned from studying the code of dvd+rw-tools and MMC-5 specs. No code but only the pure SCSI knowledge has been taken from dvd+rw-tools, though.
cdrskin is a limited cdrecord compatibility wrapper for libburn. cdrecord is a powerful GPL'ed burn program included in Joerg Schilling's cdrtools. cdrskin strives to be a second source for the services traditionally provided by cdrecord. Additionally it provides libburn's DVD/BD capabilities, where only -sao is compatible with cdrecord. cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes have been copied from the message output of cdrecord runs, though. See cdrskin/README for more.
Our build system is based on autotools. User experience tells us that you will need at least autotools version 1.7.
To build libburn and its companion applications go into its toplevel directory and execute
To make the libraries accessible for running resp. developing applications
It's main purpose, nevertheless, is to show you how to use libburn and also to serve the libburnia team as reference application. libburner does indeed define the standard way how above gestures can be implemented and stay upward compatible for a good while.
libburner has two companions, telltoc and dewav, which help to perform some peripheral tasks of burning.
telltoc prints a table of content (sessions, tracks and leadouts), it tells about type and state of media, and also is able to provide the necessary multi-session information for program mkisofs option -C. Especially helpful are its predictions with "Write multi" and "Write modes" where availability of "TAO" indicates that tracks of unpredicted length can be written. See: test/telltoc –help.
dewav extracts raw byte-swapped audio data from files of format .wav (MS WAVE) or .au (SUN Audio). See example in libburner –help.
Click on blue names of functions, structures, variables, etc in oder to get to the according specs of libburn API or libburner sourcecode.
#include "../libburn/libburn.h"
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
{
int ret;
if(drive_adr != NULL && drive_adr[0] != 0)
else
if (ret <= 0 || *driveno <= 0)
return ret;
current_profile_name);
if (current_profile_name[0])
printf("Detected media type: %s\n", current_profile_name);
return 1;
}
{
int ret;
if (strncmp(drive_adr, "stdio:/dev/fd/", 14) == 0 ||
strcmp(drive_adr, "stdio:-") == 0) {
fprintf(stderr, "Will not work with pseudo-drive '%s'\n",
drive_adr);
return 0;
}
if (ret<=0) {
fprintf(stderr, "Address does not lead to a CD burner: '%s'\n",
drive_adr);
return 0;
}
fprintf(stderr,"Aquiring drive '%s' ...\n", libburn_drive_adr);
if (ret <= 0) {
fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
libburn_drive_adr);
} else {
fprintf(stderr,"Done\n");
drive_is_grabbed = 1;
}
return ret;
}
{
int ret, i;
printf("Beginning to scan for devices ...\n");
usleep(100002);
if (drive_count <= 0 && *driveno >= 0) {
printf("FAILED (no drives found)\n");
return 0;
}
printf("Done\n");
printf("\nOverview of accessible drives (%d found) :\n",
drive_count);
printf("-----------------------------------------------------------------------------\n");
for (i = 0; i < (int) drive_count; i++) {
strcpy(adr, "-get_adr_failed-");
printf("%d --drive '%s' : '%s' '%s'\n",
}
printf("-----------------------------------------------------------------------------\n\n");
if (*driveno < 0) {
printf("Pseudo-drive \"-\" given : bus scanning done.\n");
return 2;
}
if ((int) drive_count <= *driveno) {
fprintf(stderr,
"Found only %d drives. Number %d not available.\n",
drive_count, *driveno);
return 0;
}
for (i = 0; i < (int) drive_count; i++) {
if (i == *driveno)
continue;
if (ret != 1)
fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
i, ret);
else
printf("Dropped unwanted drive %d\n",i);
}
if (ret != 1)
return 0;
drive_is_grabbed = 1;
return 1;
}
{
double percent = 1.0;
printf(
"Drive media status: %d (see libburn/libburn.h BURN_DISC_*)\n",
disc_state);
if (current_profile == 0x13) {
;
fprintf(stderr,
"IDLE: Blank media detected. Will leave it untouched\n");
return 2;
;
fprintf(stderr,"FATAL: No media detected in drive\n");
return 0;
} else {
fprintf(stderr,
"FATAL: Unsuitable drive and media state\n");
return 0;
}
fprintf(stderr,
"FATAL : Media is not of erasable type\n");
return 0;
}
printf("Beginning to %s-blank media.\n", (blank_fast?"fast":"full"));
sleep(1);
if(p.sectors>0 && p.sector>=0)
percent = 1.0 + ((double) p.sector+1.0)
/ ((double) p.sectors) * 98.0;
printf("Blanking ( %.1f%% done )\n", percent);
sleep(1);
}
return -1;
printf("Done\n");
return 1;
}
{
double percent = 1.0;
int ret, status, num_formats, format_flag= 0;
off_t size = 0;
unsigned dummy;
if (current_profile == 0x13) {
fprintf(stderr, "IDLE: DVD-RW media is already formatted\n");
return 2;
} else if (current_profile == 0x41 || current_profile == 0x43) {
fprintf(stderr,
"FATAL: BD-R is not blank. Cannot format.\n");
return 0;
}
&num_formats);
fprintf(stderr,
"IDLE: BD media is already formatted\n");
return 2;
}
size = 0;
format_flag = 3<<1;
} else if (current_profile == 0x14) {
size = 128 * 1024 * 1024;
format_flag = 1;
} else {
fprintf(stderr, "FATAL: Can only format DVD-RW or BD\n");
return 0;
}
printf("Beginning to format media.\n");
sleep(1);
if(p.sectors>0 && p.sector>=0)
percent = 1.0 + ((double) p.sector+1.0)
/ ((double) p.sectors) * 98.0;
printf("Formatting ( %.1f%% done )\n", percent);
sleep(1);
}
return -1;
current_profile_name);
if (current_profile == 0x14 || current_profile == 0x13)
printf("Media type now: %4.4xh \"%s\"\n",
current_profile, current_profile_name);
if (current_profile == 0x14) {
fprintf(stderr,
"FATAL: Failed to change media profile to desired value\n");
return 0;
}
return 1;
}
{
struct burn_disc *target_disc;
struct burn_session *session;
struct burn_write_opts *burn_options;
struct burn_track *track, *tracklist[99];
time_t start_time;
int last_sector = 0, padding = 0, trackno, unpredicted_size = 0, fd;
int fifo_chunksize = 2352, fifo_chunks = 1783;
off_t fixed_size;
struct stat stbuf;
padding = 300*1024;
fifo_chunksize = 2048;
fifo_chunks = 2048;
}
adr = source_adr[trackno];
fixed_size = 0;
if (adr[0] == '-' && adr[1] == 0) {
fd = 0;
} else {
fd = open(adr, O_RDONLY);
if (fd>=0)
if (fstat(fd,&stbuf)!=-1)
if((stbuf.st_mode&S_IFMT)==S_IFREG)
fixed_size = stbuf.st_size;
}
if (fixed_size==0)
unpredicted_size = 1;
data_src = NULL;
if (fd>=0)
if (data_src == NULL) {
fprintf(stderr,
"FATAL: Could not open data source '%s'.\n",adr);
if(errno!=0)
fprintf(stderr,"(Most recent system error: %s )\n",
strerror(errno));
return 0;
}
fifo_chunksize, fifo_chunks, 0);
if (fifo_src[trackno] == NULL) {
fprintf(stderr,
"FATAL: Could not create fifo object of 4 MB\n");
return 0;
}
fprintf(stderr,
"FATAL: Cannot attach source object to track object\n");
return 0;
}
printf("Track %d : source is '%s'\n", trackno+1, adr);
}
fprintf(stderr, "FATAL: Closed media with data detected. Need blank or appendable media.\n");
fprintf(stderr, "HINT: Try --blank_fast\n\n");
fprintf(stderr,"FATAL: No media detected in drive\n");
else
fprintf(stderr,
"FATAL: Cannot recognize state of drive and media\n");
return 0;
}
if(simulate_burn)
printf("\n*** Will TRY to SIMULATE burning ***\n\n");
fprintf(stderr, "FATAL: Failed to find a suitable write mode with this media.\n");
fprintf(stderr, "Reasons given:\n%s\n", reasons);
return 0;
}
printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n");
start_time = time(0);
usleep(100002);
if (progress.sectors <= 0 ||
(progress.sector >= progress.sectors - 1 &&
!unpredicted_size) ||
(unpredicted_size && progress.sector == last_sector))
printf(
"Thank you for being patient since %d seconds.",
(int) (time(0) - start_time));
else if(unpredicted_size)
printf("Track %d : sector %d", progress.track+1,
progress.sector);
else
printf("Track %d : sector %d of %d",progress.track+1,
progress.sector, progress.sectors);
last_sector = progress.sector;
if (progress.track >= 0 && progress.track < source_adr_count) {
int size, free_bytes, ret;
char *status_text;
fifo_src[progress.track], &size, &free_bytes,
&status_text);
if (ret >= 0 )
printf(" [fifo %s, %2d%% fill]", status_text,
(int) (100.0 - 100.0 *
((double) free_bytes) /
(double) size));
}
printf("\n");
sleep(1);
}
printf("\n");
}
return -1;
if (multi && current_profile != 0x1a && current_profile != 0x13 &&
current_profile != 0x12 && current_profile != 0x43)
printf("NOTE: Media left appendable.\n");
if (simulate_burn)
printf("\n*** Did TRY to SIMULATE burning ***\n\n");
return 1;
}
static int driveno = 0;
static char source_adr[99][4096];
static int source_adr_count = 0;
static int simulate_burn = 0;
{
int i, insuffient_parameters = 0, print_help = 0;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--audio")) {
} else if (!strcmp(argv[i], "--blank_fast")) {
do_blank = 1;
} else if (!strcmp(argv[i], "--blank_full")) {
do_blank = 2;
} else if (!strcmp(argv[i], "--burn_for_real")) {
simulate_burn = 0;
} else if (!strcmp(argv[i], "--drive")) {
++i;
if (i >= argc) {
fprintf(stderr,"--drive requires an argument\n");
return 1;
} else if (strcmp(argv[i], "-") == 0) {
drive_adr[0] = 0;
driveno = -1;
} else if (isdigit(argv[i][0])) {
drive_adr[0] = 0;
driveno = atoi(argv[i]);
} else {
fprintf(stderr,"--drive address too long (max. %d)\n",
return 2;
}
strcpy(drive_adr, argv[i]);
}
} else if ((!strcmp(argv[i], "--format_overwrite")) ||
(!strcmp(argv[i], "--format"))) {
do_blank = 101;
} else if (!strcmp(argv[i], "--multi")) {
do_multi = 1;
} else if (!strcmp(argv[i], "--stdin_size")) {
i++;
} else if (!strcmp(argv[i], "--try_to_simulate")) {
simulate_burn = 1;
} else if (!strcmp(argv[i], "--help")) {
print_help = 1;
} else if (!strncmp(argv[i], "--",2)) {
fprintf(stderr, "Unidentified option: %s\n", argv[i]);
return 7;
} else {
if(strlen(argv[i]) >= 4096) {
fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
return 5;
}
if(source_adr_count >= 99) {
fprintf(stderr, "Too many tracks (max. 99)\n");
return 6;
}
strcpy(source_adr[source_adr_count], argv[i]);
source_adr_count++;
}
}
insuffient_parameters = 1;
if (driveno < 0)
insuffient_parameters = 0;
if (source_adr_count > 0)
insuffient_parameters = 0;
if (do_blank)
insuffient_parameters = 0;
if (print_help || insuffient_parameters ) {
printf("Usage: %s\n", argv[0]);
printf(" [--drive <address>|<driveno>|\"-\"] [--audio]\n");
printf(" [--blank_fast|--blank_full|--format] [--try_to_simulate]\n");
printf(" [--multi] [<one or more imagefiles>|\"-\"]\n");
printf("Examples\n");
printf("A bus scan (needs rw-permissions to see a drive):\n");
printf(" %s --drive -\n",argv[0]);
printf("Burn a file to drive chosen by number, leave appendable:\n");
printf(" %s --drive 0 --multi my_image_file\n", argv[0]);
printf("Burn a file to drive chosen by persistent address, close:\n");
printf(" %s --drive /dev/hdc my_image_file\n", argv[0]);
printf("Blank a used CD-RW (is combinable with burning in one run):\n");
printf(" %s --drive /dev/hdc --blank_fast\n",argv[0]);
printf("Blank a used DVD-RW (is combinable with burning in one run):\n");
printf(" %s --drive /dev/hdc --blank_full\n",argv[0]);
printf("Format a DVD-RW, BD-RE or BD-R:\n");
printf(" %s --drive /dev/hdc --format\n", argv[0]);
printf("Burn two audio tracks (to CD only):\n");
printf(" lame --decode -t /path/to/track1.mp3 track1.cd\n");
printf(" test/dewav /path/to/track2.wav -o track2.cd\n");
printf(" %s --drive /dev/hdc --audio track1.cd track2.cd\n", argv[0]);
printf("Burn a compressed afio archive on-the-fly:\n");
printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
printf(" %s --drive /dev/hdc -\n", argv[0]);
printf("To be read from *not mounted* media via: afio -tvZ /dev/hdc\n");
if (insuffient_parameters)
return 6;
}
return 0;
}
int main(
int argc,
char **argv)
{
int ret;
if (sizeof(off_t) != 8) {
fprintf(stderr,
"\nFATAL: Compile time misconfiguration. off_t is not 64 bit.\n\n");
exit(39);
}
if (ret)
exit(ret);
printf("Initializing libburnia-project.org ...\n");
printf("Done\n");
else {
printf("FAILED\n");
fprintf(stderr,"\nFATAL: Failed to initialize.\n");
exit(33);
}
if (ret<=0) {
fprintf(stderr,"\nFATAL: Failed to aquire drive.\n");
{ ret = 34; goto finish_libburn; }
}
if (ret == 2)
{ ret = 0; goto release_drive; }
if (do_blank) {
if (do_blank > 100)
else
do_blank == 1);
if (ret<=0)
{ ret = 36; goto release_drive; }
}
if (source_adr_count > 0) {
source_adr, source_adr_count,
do_multi, simulate_burn, all_tracks_type);
if (ret<=0)
{ ret = 38; goto release_drive; }
}
ret = 0;
release_drive:;
if (drive_is_grabbed)
finish_libburn:;
fprintf(stderr,"\nlibburner run aborted\n");
exit(1);
}
exit(ret);
}