DekGenius.com
[ Team LiB ] Previous Section Next Section

8.3 Disc Recording Frameworks

The ability to burn data to CDs and DVDs is a centerpiece of the Mac and Apple's Digital Hub strategy. Nearly every Mac sold today can record to CD or DVD media. This creates an opportunity for application developers to provide features in their applications that take advantage of a system's built-in disc burning capabilities.

With Mac OS X 10.2, Apple released two Objective-C frameworks that let applications take advantage of disc capabilities present in most new Macs. These frameworks are DiscRecording and DiscRecordingUI. Together, they provide an API for assembling content in preparation for recording and performing disc burn and erase operations.

The functionality of the Disc Recoding API is split between two frameworks for a good reason. DiscRecording provides the bulk of the API used for creating content and managing burn operations. DiscRecordingUI, on the other hand, provides no more than several NSPanel subclasses that implement standard user interfaces that applications can use to quickly configure and perform burn and erase operations. By separating the presentation from the mechanics, non-GUI applications can use the DiscRecording APIs. This section begins with a discussion of the DiscRecording framework and finishes with an overview of DiscRecordingUI.

8.3.1 The DiscRecording Framework

The DiscRecording framework provides classes that represent the fundamental parts of a recording process. DiscRecording has APIs that assemble filesystem hierarchies that will be recorded to disc, and APIs that record audio. Figure 8-2 shows this framework's class hierarchy.

Figure 8-2. DiscRecording framework class hierarchy
figs/cocn_0802.gif

The DiscRecording API provides a means to perform two primary tasks that people need their disc recording hardware to accomplish: burning a disc and erasing a disc's contents. These two tasks are represented by the classes DRBurn and DRErase.

8.3.1.1 DRBurn

DRBurn manages the process of burning a disc. The interface for this class provides several methods used to configure the burn process. Specifically, DRBurn lets you control the behavior of the burn process and specify how media should be handled after it is recorded to (i.e., should the disc be ejected or mounted as a filesystem).

To initialize a DRBurn object, use the method initWithDevice:. This initializer takes a DRDevice object, which represents the physical hardware device used for recording. All DRErase and DRBurn objects have an associated instance of DRDevice that provides information about and controls the hardware. Generally, you won't need to interact with DRDevice objects, except when you need to obtain information about the device, such as its make and model number. Device objects are configured and used mainly by the burning engine.

Use setProperties: to configure burn objects. Table 8-6 enumerates the keys used in this dictionary.

Table 8-6. Keys used in the burn properties dictionary

Key

Description

DRBurnRequestedSpeedKey

NSNumber containing a float that specifies the burn speed in kilobytes per second. The default is DRDeviceBurnSpeedMax.

DRBurnAppendableKey

BOOL specifying whether the disc should be appendable after the initial burn. The default is NO.

DRBurnVerifyDiscKey

BOOL specifying whether the burn should be verified. The default is YES.

DRBurnCompletionActionKey

Specifies the action that should occur after the burn is completed. The options are the default, DRBurnCompletionActionEject, and DRBurnCompletionActionMount.

DRBurnUnderrunProtectionKey

BOOL that turns run protection on and off for devices that support it. The default is YES.

DRBurnTestingKey

BOOL specifying if the burn should be run as a test burn. The default is NO.

DRSynchronousBehaviorKey

BOOL specifying if burn operations will behave synchronously. The default is NO.

8.3.1.2 DRErase

DRErase represents a disc erasure operation. Like DRBurn objects, DRErase instances are initialized with initWithDevice:. The DiscRecording API supports two types of operations: a quick erase and a complete erase. A quick erase does the minimum amount of work needed to make a disc appear blank, while a complete erase makes sure that every byte of data on the disc is erased. Quick erases, the default type for DRErase, take a couple of minutes to perform, whereas a complete erase can take up to a half an hour.

These two erase types are specified in the API by the constant NSString objects DREraseTypeQuick and DREraseTypeComplete. To specify the type of erase, use the method setEraseType:. The eraseType method returns the current type.

Once an erase object is configured, the erasure operation executes by invoking the start method. This method returns control to the sender immediately. The sender can retrieve information about the progress of an erase operation by polling the status method or listening for notifications.

8.3.1.3 DRTrack

DRTrack provides the burn with data and describes the track used to burn the data to disc. Instances of DRTrack do not actually store the data that will be recorded; rather, track objects are used as an interface to a data producer that provides the actual data. The DiscRecording framework defines the DRTrackDataProduction protocol to which classes should conform if they want to provide data for a DRTrack object.

Providing data is only part of DRTrack's responsibility. It is also responsible for providing properties of the actual track that will be written to disk. This is done by setting track properties with the setProperties: method. This method takes a dictionary, whose keys come from those listed in Table 8-7. Each key listed in this table must have a value assigned to it, or the burn will fail. The Mt. Fuji (IFF-8090i) specification for CD/DVD devices defines the values these properties may take.

Table 8-7. Required properties for a fully configured DRTrack object

Property key

Description

DRTrackLengthKey

Length of the track

DRBlockSizeKey

Size of each block measured in bytes

DRBlockTypeKey

Type of each block in the track

DRDataFormKey

Data form of each track block

DRSessionFormatKey

Session format of the track

DRTrackModeKey

Mode of the track

DRTrack provides two convenience constructors that prepare a DRTrack to burn audio to a disc or the data contained in a directory structure: trackWithAudioOfLength:producer: for audio and trackForRootFolder: for data.

8.3.1.4 Preparing audio content

trackWithAudioOfLength:producer: provides audio content to record. In the first parameter, the length's value is specified as an instance of the class DRMSF. This subclass of NSNumber represents lengths and positions on a disc by minutes, seconds, and frames (thus the MSF moniker). A frame is a subdivision of a second, and there are 75 frames in a second. A frame corresponds to one block on a track, and is thus the smallest possible division of space on a disc. The producer: parameter is an object that conforms to the DRTrackDataProduction protocol. The data producer prepares data and provides it to the track during the burn process.

8.3.1.5 Preparing data content

The second DRTrack convenience constructor is trackForRootFolder:. This method takes a single parameter, which is of type DRFolder. DRFolder is one of two subclasses of the class DRFSObject, which represents a generic filesystem object. The other subclass is DRFile. DRFile and DRFolder both construct a filesystem that will be reproduced on a CD or DVD.

To successfully create a data track, you need to know that files and folders are related to one another in a one-to-many, parent-child relationship, and are arranged in trees. Thus, each folder in the structure may have many children, which are folders or files, and each child has exactly one parent, which must be a folder. Only folders may have children; files may not. This mirrors the organization of any filesystem you're used to working with. Additionally, files and folders may either be real or virtual. A real file or folder corresponds to a real file or folder that exists on the user's source volume. Because they represent actual objects in the source filesystem, real filesystem objects may not be modified once they are added to the data track preparation.

To create a real DRFile, use either the convenience constructor fileWithPath: or the initializer initWithPath:. Both methods take as an argument the path to an actual file on disk. A DRFile object is capable of representing real files, aliases, and symbolically linked files. Similarly, to create a real DRFolder, use either the convenience constructor folderWithPath: or the initializer initWithPath:.

A virtual filesystem object is a placeholder for a file or folder that will be created when the filesystem is written to the disc. The process of assembling virtual files and folders is often referred to as creating a filesystem in the API, referring to the methods in DRFile and DRFolder used to construct the filesystem. Using virtual folders, a program can construct a filesystem that will be created on the disc by adding children to folder objects. These children may be real or virtual DRFSObjects. Virtual DRFolders are the only filesystem objects that may contain children. Thus real files and folders, and virtual files, are all leaf nodes in the filesystem.

When a virtual filesystem object is created, a name is assigned to the object that will be the file or folder name in the end product. Virtual files are also assigned an NSData object that contains what will become the file contents on the destination media. Alternatively, a virtual file object can be associated with an object that conforms to the DRDataTrackProduction protocol as a means of creating a file's contents. You can create a virtual folder by invoking the DRFolder class method folderWithName: or the initializer initWithName:. Virtual files may be created with virtualFileWithName:data: or virtualFileWithName:dataProducer:. In the former method, the parameter is an NSData object, while the latter method requires an object that conforms to the DRTrackDataProduction protocol. There are also equivalent initializers, initWithName:data: and initWithName:dataProducer:.

Children are added to a DRFolder by invoking the method addChild:, and they are removed using the method removeChild:. The count method returns the number of children within a folder.. children returns an NSArray of a folder's children. While a real folder may not have any children, you can convert a real folder into a virtual folder to add children to the directory structure. When a folder is converted from real to virtual, the converted folder's pre-existing contents remain real. Real folders are made virtual by invoking makeVirtual. Once a filesystem structure is created—by choosing a real file or folder or building one from scratch using a combination of real and virtual filesystem objects—the root DRFolder of the tree creates a DRTrack with the convenience constructor trackForRootFolder:.

Example 8-7 shows how to prepare data to be burned to disc.

Example 8-7. Preparing data for burning
// Prepare a real folder or file
DRFolder *rFolder = [DRFolder folderWithPath:@"/Users/mike"];
DRFile *rFile = [DRFile fileWithPath:@"/Users/mike/someDoc.txt"];

// Work with virtual filesystem objects
DRFolder *root = [DRFolder folderWithName:@"Root Folder"];
DRFile *vFile = [DRFile fileWithName:@"Fake File" data:someNSData];

[rFolder makeVirtual];
[rFolder addChild:vFile];
[root addChild:rFolder];
[root addChild:rFile];

// Create a DRTrack object to hold the data
DRTrack *track = [DRTrack trackForRootFolder:root];

8.3.2 The DiscRecordingUI Framework

DiscRecordingUI implements a standard front-end interface to the DiscRecording framework. From reading the previous section, you know that instances of the classes DRBurn and DRErase represent burn and erase operations. The classes of DiscRecordingUI provide an interface to configure instances of DRBurn and DRErase, as well as monitor the progress of recording and erase operations.

Figure 8-3 shows the classes of the DiscRecordingUI framework. Each one is a subclass of NSPanel. DRSetupPanel is an abstract superclass that provides facilities for device selection and handling needed by burn and erase setup panels.

Figure 8-3. DiscRecordingUI framework class hierarchy
figs/cocn_0803.gif

DRBurnSetupPanel, shown in Figure 8-4, is an interface that configures and executes a burn operation. DREraseSetupPanel, shown in Figure 8-5, provides an interface for configuring and executing disc erase operations.

Figure 8-4. The burn disc panel
figs/cocn_0804.gif
Figure 8-5. The erase disc panel
figs/cocn_0805.gif
8.3.2.1 How to record

To create a burn or erase setup panel, invoke setupPanel in either DRBurnSetupPanel or DREraseSetupPanel. Once an instance of either class is obtained, invoke the DRSetupPanel method runSetupPanel, which displays the panel on the screen. Setup panels run as modal windows, which means you can't interact with any other part of the application while the window is open.

The method runSetupPanel is blocking, so execution in the thread it was invoked in stops until the user clicks the panel's Cancel or Burn/Erase buttons. This method returns with an int, indicating which button the user clicked. The return value is equal to one of the constants NSOKButton or NSCancelButton. This return value determines the next course of action: either continue on with our application or begin a burn/erase operation.

In the case of a burn setup panel, after determining that the Burn button was pressed, we can obtain an instance of DRBurn whose state reflects the configuration made in the setup panel. This object is obtained with the method burnObject.

The next step, after preparing your layout is to instantiate DRBurnProgressPanel and start the burn process. To create a burn progress panel, invoke the class method progressPanel. You can start burn operations by using beginProgressPanelForBurn:layout:, to which you supply our burn object and the object representing your layout. Example 8-8 performs these tasks in code.

Example 8-8. Using setup panels in DiscRecordingUI
- (IBAction)showBurnPanel:(id)sender
{
  DRBurnSetupPanel *bp = [DRBurnSetupPanel setupPanel];
  int status = [bp runSetupPanel];

  if ( status == NSOKButton ) {
      DRBurn *burn = [bp burnObject];
      DRBurnProgressPanel *pp = [DRBurnProgressPanel progressPanel];

      // Assume aLayout has been prepared previously
      [pp beginProgressPanelForBurn: burn layout:aLayout];
  }
}
    [ Team LiB ] Previous Section Next Section