utils.cpp
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
*/ |
/* |
These are utility routines that are used to parse the command line arguments |
and extract the specified file location, type and format |
*/ |
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) |
#include <AudioToolbox/AudioToolbox.h> |
#else |
#include "AudioToolbox.h" |
#include <stdlib.h> |
#include <QTML.h> |
#include "QTLicensedTech.h" // So we can access the AAC aenc |
#include "CAWindows.h" |
#endif |
#include "CAXException.h" |
#include "CAStreamBasicDescription.h" |
// external to this file the following function needs to be defined |
int ConvertFile (CFURLRef inputFileURL, |
CAStreamBasicDescription &inputFormat, |
CFURLRef outputFileURL, |
AudioFileTypeID outputFileType, |
CAStreamBasicDescription &outputFormat, |
UInt32 outputBitRate); |
void UsageString(int exitCode) |
{ |
printf ("Usage: ConvertFile /path/to/input/file [-d formatID] [-r sampleRate] [-bd bitDepth] [-f fileType] [-b bitrate] [-h]\n"); |
printf (" output file written is /tmp/outfile.<EXT FOR FORMAT>\n"); |
printf (" if -d is specified, out file is written with that format\n"); |
printf (" if no format is specified and input file is 'lpcm', IMA is written\n"); |
printf (" if input file is compressed (ie. not 'lpcm'), then 'lpcm' is written\n"); |
printf (" if -r is specified, input file's format must be ('lpcm') and output ('lpcm') is written with new sample rate\n"); |
printf (" if -bd is specified, input file's format must be compressed (ie. not 'lpcm') and output is written with new bit depth\n"); |
printf (" if -f is not specified, CAF File is written ('caff')\n"); |
printf (" if -b is specified, the bit rate for the output file when using a VBR encoder\n"); |
printf (" if -h is specified, will print out this usage message\n"); |
exit(exitCode); |
} |
void str2OSType (const char * inString, OSType &outType) |
{ |
if (inString == NULL) { |
outType = 0; |
return; |
} |
size_t len = strlen(inString); |
if (len <= 4) { |
char workingString[5]; |
workingString[4] = 0; |
workingString[0] = workingString[1] = workingString[2] = workingString[3] = ' '; |
memcpy (workingString, inString, strlen(inString)); |
outType = *(workingString + 0) << 24 | |
*(workingString + 1) << 16 | |
*(workingString + 2) << 8 | |
*(workingString + 3); |
return; |
} |
if (len <= 8) { |
int32_t tmp; |
if (sscanf (inString, "%x", &tmp) == 0) { |
printf ("* * Bad conversion for OSType\n"); |
UsageString(1); |
} |
outType = tmp; |
return; |
} |
printf ("* * Bad conversion for OSType\n"); |
UsageString(1); |
} |
void ParseArgs (int argc, char * const argv[], |
AudioFileTypeID & outFormat, |
Float64 & outSampleRate, |
OSType & outFileType, |
CFURLRef& outInputFileURL, |
CFURLRef& outOutputFileURL, |
UInt32 & outBitDepth, |
UInt32 & outBitRate) |
{ |
if (argc < 2) { |
printf ("No Input File specified\n"); |
UsageString(1); |
} |
// support "ConvertFile -h" usage |
if (argc == 2 && !strcmp("-h", argv[1])) { |
UsageString(0); |
} |
// first validate our initial condition |
const char* inputFileName = argv[1]; |
outInputFileURL = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8 *)inputFileName, strlen(inputFileName), false); |
if (!outInputFileURL) { |
printf ("* * Bad input file path\n"); |
UsageString(1); |
} |
outBitRate = 0; |
outBitDepth = 0; |
// look to see if a format or different file output has been specified |
for (int i = 2; i < argc; ++i) { |
if (!strcmp ("-d", argv[i])) { |
str2OSType (argv[++i], outFormat); |
outSampleRate = 0; |
} |
else if (!strcmp ("-r", argv[i])) { |
sscanf (argv[++i], "%lf", &outSampleRate); |
outFormat = 0; |
} |
else if (!strcmp("-bd", argv[i])) { |
int temp; |
sscanf (argv[++i], "%d", &temp); |
outBitDepth = temp; |
} |
else if (!strcmp ("-f", argv[i])) { |
str2OSType (argv[++i], outFileType); |
} |
else if (!strcmp ("-b", argv[i])) { |
int temp; |
sscanf (argv[++i], "%u", &temp); |
outBitRate = temp; |
} |
else if (!strcmp ("-h", argv[i])) { |
UsageString(0); |
} |
else { |
printf ("* * Unknown command: %s\n", argv[i]); |
UsageString(1); |
} |
} |
// output file |
UInt32 size = sizeof(CFArrayRef); |
CFArrayRef extensions; |
OSStatus err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_ExtensionsForType, |
sizeof(OSType), &outFileType, |
&size, &extensions); |
XThrowIfError (err, "Getting the file extensions for file type"); |
// just take the first extension |
CFStringRef ext = (CFStringRef)CFArrayGetValueAtIndex(extensions, 0); |
char extstr[32]; |
Boolean res = CFStringGetCString(ext, extstr, 32, kCFStringEncodingUTF8); |
XThrowIfError (!res, "CFStringGetCString"); |
// release the array as we're done with this now |
CFRelease (extensions); |
char outFname[256]; |
#if TARGET_OS_WIN32 |
char drive[3], dir[256]; |
_splitpath_s(inputFileName, drive, 3, dir, 256, NULL, 0, NULL, 0); |
_makepath_s(outFname, 256, drive, dir, "outfile", extstr); |
#else |
// char outFname[64]; |
sprintf (outFname, "/tmp/outfile.%s", extstr); |
#endif |
outOutputFileURL = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8 *)outFname, strlen(outFname), false); |
if (!outOutputFileURL) { |
printf ("* * Bad output file path\n"); |
UsageString(1); |
} |
} |
void GetFormatFromInputFile (AudioFileID inputFile, CAStreamBasicDescription & inputFormat) |
{ |
bool doPrint = true; |
UInt32 size; |
XThrowIfError(AudioFileGetPropertyInfo(inputFile, |
kAudioFilePropertyFormatList, &size, NULL), "couldn't get file's format list info"); |
UInt32 numFormats = size / sizeof(AudioFormatListItem); |
AudioFormatListItem *formatList = new AudioFormatListItem [ numFormats ]; |
XThrowIfError(AudioFileGetProperty(inputFile, |
kAudioFilePropertyFormatList, &size, formatList), "couldn't get file's data format"); |
numFormats = size / sizeof(AudioFormatListItem); // we need to reassess the actual number of formats when we get it |
if (numFormats == 1) { |
// this is the common case |
inputFormat = formatList[0].mASBD; |
} else { |
if (doPrint) { |
printf ("File has a %d layered data format:\n", (int)numFormats); |
for (unsigned int i = 0; i < numFormats; ++i) |
CAStreamBasicDescription(formatList[i].mASBD).Print(); |
printf("\n"); |
} |
// now we should look to see which decoders we have on the system |
XThrowIfError(AudioFormatGetPropertyInfo(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size), "couldn't get decoder id's"); |
UInt32 numDecoders = size / sizeof(OSType); |
OSType *decoderIDs = new OSType [ numDecoders ]; |
XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_DecodeFormatIDs, 0, NULL, &size, decoderIDs), "couldn't get decoder id's"); |
unsigned int i = 0; |
for (; i < numFormats; ++i) { |
OSType decoderID = formatList[i].mASBD.mFormatID; |
bool found = false; |
for (unsigned int j = 0; j < numDecoders; ++j) { |
if (decoderID == decoderIDs[j]) { |
found = true; |
break; |
} |
} |
if (found) break; |
} |
delete [] decoderIDs; |
if (i >= numFormats) { |
fprintf (stderr, "Cannot play any of the formats in this file\n"); |
throw kAudioFileUnsupportedDataFormatError; |
} |
inputFormat = formatList[i].mASBD; |
} |
delete [] formatList; |
} |
void ConstructOutputFormatFromArgs ( CFURLRef inputFileURL, |
OSType fileType, OSType format, Float64 sampleRate, |
CAStreamBasicDescription &inputFormat, |
UInt32 bitDepth, |
CAStreamBasicDescription &outputFormat) |
{ |
AudioFileID infile; |
OSStatus err = AudioFileOpenURL(inputFileURL, kAudioFileReadPermission, 0, &infile); |
XThrowIfError (err, "AudioFileOpen"); |
// get the input file format |
GetFormatFromInputFile (infile, inputFormat); |
if (inputFormat.mFormatID != kAudioFormatLinearPCM && sampleRate > 0) { |
printf ("Can only specify sample rate with linear pcm input file\n"); |
UsageString(1); |
} |
// set up the output file format |
if (!format) { |
if (sampleRate > 0) { |
outputFormat = inputFormat; |
outputFormat.mSampleRate = sampleRate; |
} else { |
if (inputFormat.mFormatID != kAudioFormatLinearPCM) |
format = kAudioFormatLinearPCM; |
else |
format = kAudioFormatAppleIMA4; |
} |
} |
if (format) { |
if (format == kAudioFormatLinearPCM) { |
outputFormat.mFormatID = format; |
outputFormat.mSampleRate = inputFormat.mSampleRate; |
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; |
outputFormat.mBitsPerChannel = (bitDepth) ? bitDepth : 16; |
outputFormat.mBytesPerPacket = inputFormat.mChannelsPerFrame * (outputFormat.mBitsPerChannel / 8); |
outputFormat.mFramesPerPacket = 1; |
outputFormat.mBytesPerFrame = outputFormat.mBytesPerPacket; |
if (fileType == kAudioFileWAVEType) |
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
| kLinearPCMFormatFlagIsPacked; |
else |
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian |
| kLinearPCMFormatFlagIsSignedInteger |
| kLinearPCMFormatFlagIsPacked; |
} else { |
// need to set at least these fields for kAudioFormatProperty_FormatInfo |
outputFormat.mFormatID = format; |
outputFormat.mSampleRate = inputFormat.mSampleRate; |
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; |
// use AudioFormat API to fill out the rest. |
UInt32 size = sizeof(outputFormat); |
err = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat); |
XThrowIfError (err, "AudioFormatGetProperty kAudioFormatProperty_FormatInfo"); |
} |
} |
AudioFileClose (infile); |
} |
int main (int argc, char * const argv[]) |
{ |
#if TARGET_OS_WIN32 |
QTLicenseRef aacEncoderLicenseRef = nil; |
QTLicenseRef amrEncoderLicenseRef = nil; |
OSErr localerr; |
#endif |
int result = 0; |
CFURLRef inputFileURL = NULL; |
CFURLRef outputFileURL = NULL; |
#if TARGET_OS_WIN32 |
InitializeQTML(0L); |
{ |
OSErr localerr; |
const char *licenseDesc = "AAC Encode License Verification"; |
const char *amrLicenseDesc = "AMR Encode License Verification"; |
localerr = QTRequestLicensedTechnology("com.apple.quicktimeplayer","com.apple.aacencoder", |
(void *)licenseDesc,strlen(licenseDesc),&aacEncoderLicenseRef); |
localerr = QTRequestLicensedTechnology("com.apple.quicktimeplayer","1D07EB75-3D5E-4DA6-B749-D497C92B06D8", |
(void *)amrLicenseDesc,strlen(amrLicenseDesc),&amrEncoderLicenseRef); |
} |
#endif |
try { |
OSType format = 0; |
Float64 sampleRate = 0; |
AudioFileTypeID outputFileType = kAudioFileCAFType; |
UInt32 outputBitRate = 0; |
UInt32 outputBitDepth = 0; |
ParseArgs (argc, argv, format, sampleRate, outputFileType, inputFileURL, outputFileURL, outputBitDepth, outputBitRate); |
// printf ("args:%4.4s, sample rate:%.1f, outputFileType: %4.4s\n", (char*)&format, sampleRate, (char*)&outputFileType); |
CAStreamBasicDescription inputFormat; |
CAStreamBasicDescription outputFormat; |
ConstructOutputFormatFromArgs (inputFileURL, outputFileType, format, sampleRate, inputFormat, outputBitDepth, outputFormat); |
printf ("Source File format:\n\t"); inputFormat.Print(); |
printf ("Dest File format:\n\t"); outputFormat.Print(); |
result = ConvertFile (inputFileURL, inputFormat, outputFileURL, outputFileType, outputFormat, outputBitRate); |
CFStringRef path = CFURLCopyPath(outputFileURL); |
printf("done: "); fflush(stdout); CFShow(path); |
if (path) CFRelease(path); |
} catch (CAXException e) { |
char buf[256]; |
fprintf(stderr, "Error in: %s\nException thrown: %s\n", e.mOperation, e.FormatError(buf, sizeof(buf))); |
result = 1; |
} catch (...) { |
fprintf(stderr, "Unspecified exception\n"); |
result = 1; |
} |
if (inputFileURL) CFRelease(inputFileURL); |
if (outputFileURL) CFRelease(outputFileURL); |
#if TARGET_OS_WIN32 |
TerminateQTML(); |
if (aacEncoderLicenseRef) |
{ |
localerr = QTReleaseLicensedTechnology(aacEncoderLicenseRef); |
aacEncoderLicenseRef = nil; |
} |
if(amrEncoderLicenseRef) |
{ |
localerr = QTReleaseLicensedTechnology(amrEncoderLicenseRef); |
amrEncoderLicenseRef = nil; |
} |
#endif |
return result; |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-29