1. Sven Weidauer
  2. cocoa-stuff

Source

cocoa-stuff / Quicktime / MetadataItem.m

/* Copyright (c) 2008 Sven Weidauer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 
 * Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 */


#import "MetadataItem.h"

#import "MetadataContainer.h"
#import "EndianSupport.h"

@implementation MetadataItem

- initWithContainer: (MetadataContainer *) newContainer item: (QTMetaDataItem) newItem;
{
	self = [super init];
	if (self) {
		container = newContainer;
		item = newItem;
	}
	return self;
}

- (BOOL) getPropertyInfoForClass: (QTPropertyClass) propClass ID: (QTPropertyID) propID  type: (QTPropertyValueType *) outType size: (NSUInteger *) outSize flags: (UInt32 *) outFlags;
{
	ByteCount size = 0;
	OSStatus retVal = QTMetaDataGetItemPropertyInfo( [container quickTimeMetadata], item, propClass, propID, outType, &size, outFlags );
	if (noErr == retVal) {
		if (outSize) *outSize = size;
		return YES;
	} else {
		return NO;
	}
}


- (NSData *) getPropertyDataForClass: (QTPropertyClass) propClass ID: (QTPropertyID) propID type: (QTPropertyValueType *) outType;
{
	NSData *result = nil;
	
	NSUInteger size;
	if ([self getPropertyInfoForClass: propClass ID: propID type: outType size: &size flags: 0]) {
		if (outType) {
			NSLog( @"outType: %d %s", *outType, CStringFrom4CC( *outType ) );
		}
		void *outBuffer = malloc( size );
		if (0 != outBuffer) {
			ByteCount outSize = 0;
			OSStatus retVal = QTMetaDataGetItemProperty( [container quickTimeMetadata], item, propClass, propID, size, outBuffer, &outSize );
			if (noErr == retVal) {
				result = [NSData dataWithBytesNoCopy: outBuffer length: outSize freeWhenDone: YES];
			} else {
				free( outBuffer );
				outBuffer = 0;
			}
		}
	}
	
	return result;
}

- (NSData *) valueData;
{
	NSData *result = nil;
	
	ByteCount inSize = 0;
	OSStatus retVal = QTMetaDataGetItemValue( [container quickTimeMetadata], item, 0, 0, &inSize );
	if (noErr == retVal) {
		void *outBuffer = malloc( inSize );
		if (0 != outBuffer) {
			ByteCount outSize = 0;
			retVal = QTMetaDataGetItemValue( [container quickTimeMetadata], item, outBuffer, inSize, &outSize );
			if (noErr == retVal) {
				result = [NSData dataWithBytesNoCopy: outBuffer length: outSize freeWhenDone: YES];
			} else {
				free( outBuffer );
				outBuffer = 0;
			}
		}
	}
	
	return result;
}


- (id) value;
{
	id result = nil;
	
	NSData *dataTypeData = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID:kQTMetaDataItemPropertyID_DataType type: 0];
	if (dataTypeData) {
		UInt32 dataType = *(const UInt32 *)[dataTypeData bytes];
		
		NSData *itemValueData = [self valueData];
		if (nil != itemValueData) {
			switch (dataType) {
				case kQTMetaDataTypeBinary: 
					result = itemValueData; 
					break;
				
				case kQTMetaDataTypeUTF8: 
					result = [[[NSString alloc] initWithData: itemValueData encoding: NSUTF8StringEncoding] autorelease]; 
					break;
				
				case kQTMetaDataTypeUTF16BE: 
					result = [[[NSString alloc] initWithData: itemValueData encoding: NSUTF16BigEndianStringEncoding] autorelease]; 
					break;
				
				case kQTMetaDataTypeMacEncodedText: 
					result = [[[NSString alloc] initWithData: itemValueData encoding: NSMacOSRomanStringEncoding] autorelease];
					break;
				
				case kQTMetaDataTypeBMPImage:
				case kQTMetaDataTypeJPEGImage:
				case kQTMetaDataTypePNGImage:
					result = [[[NSImage alloc] initWithData: itemValueData] autorelease];
					break;
			
				case kQTMetaDataTypeSignedIntegerBE:
//					result = [NSNumber numberWithLongLong: SignedIntegerFromBEData( itemValueData )];
					result = [NSNumber numberWithFloat: FixedToFloat( SignedIntegerFromBEData( itemValueData ) )];
					break;
				
				case kQTMetaDataTypeUnsignedIntegerBE:
					result = [NSNumber numberWithUnsignedLongLong: UnsignedIntegerFromBEData( itemValueData )];
					break;
				
				case kQTMetaDataTypeFloat32BE:
				case kQTMetaDataTypeFloat64BE:
					result = [NSNumber numberWithDouble: DoubleFromBEFloatData( itemValueData )];
					break;
			
				default:
					NSLog( @"unknown data type %d %s", dataType, CStringFrom4CC( dataType ) );
					result = itemValueData;
					break;
			}
		}
	}
	
	return result;
}


- (id) key;
{
	NSData *keyProperty = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID: kQTMetaDataItemPropertyID_Key type: 0];
	NSData *keyFormat = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID: kQTMetaDataItemPropertyID_KeyFormat type: 0];

	id keyValue = nil;
			
	if (keyProperty && keyFormat) {
		OSType keyFormatOSType = *(const OSType *)[keyFormat bytes];
		
		switch (keyFormatOSType) {
			case kQTMetaDataKeyFormatiTunesLongForm:
			case kQTMetaDataKeyFormatQuickTime:
				keyValue = [[[NSString alloc] initWithData: keyProperty encoding: NSUTF8StringEncoding] autorelease];
				break;
				
			case kQTMetaDataKeyFormatiTunesShortForm:
			case kQTMetaDataKeyFormatUserData:
				keyValue = [NSNumber numberWithUnsignedLong: *(const UInt32 *)[keyProperty bytes]];
				break;
				
			default:
				NSLog( @"unknown key format type %d %s", keyFormatOSType, CStringFrom4CC( keyFormatOSType ) );
				break;
		}
	}

	if ([keyValue isKindOfClass: [NSNumber class]]) {
		keyValue = [NSString stringWithCString: CStringFrom4CC( [keyValue unsignedLongValue] ) encoding: NSASCIIStringEncoding];
	}
	
	return keyValue;

}


- (NSString *) locale;
{
	NSData *localeData = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID: kQTMetaDataItemPropertyID_Locale type: 0];
	if (localeData) return [[[NSString alloc] initWithData: localeData encoding: NSUTF8StringEncoding] autorelease];
	else return nil;
}

- (NSString *) dataType;
{
	NSData *dataTypeData = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID:kQTMetaDataItemPropertyID_DataType type: 0];
	if (dataTypeData) {
		UInt32 dataType = *(const UInt32 *)[dataTypeData bytes];
		switch (dataType) {
				case kQTMetaDataTypeBinary: return @"Binary";
				case kQTMetaDataTypeUTF8:  return @"UTF8 String";
				case kQTMetaDataTypeUTF16BE: return @"UTF16 String";
				case kQTMetaDataTypeMacEncodedText: return @"Mac String";
				case kQTMetaDataTypeBMPImage:return @"BMP Image";
				case kQTMetaDataTypeJPEGImage: return @"JPEG Image";
				case kQTMetaDataTypePNGImage: return @"PNG Image";
				case kQTMetaDataTypeSignedIntegerBE: return @"Signed Integer";
				case kQTMetaDataTypeUnsignedIntegerBE: return @"Unsigned Integer";
				case kQTMetaDataTypeFloat32BE: return @"32 Bit Float";
				case kQTMetaDataTypeFloat64BE: return @"64 Bit Float";
		}
	}
	NSLog( @"unknown data type" );
	return nil;
}


- (NSString *) storageFormat;
{
	NSData *storageFormatData = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID:kQTMetaDataItemPropertyID_StorageFormat type: 0];
	if (storageFormatData) {
		QTMetaDataStorageFormat format = *(const QTMetaDataStorageFormat *)[storageFormatData bytes];
		switch (format) {
			case kQTMetaDataStorageFormatiTunes: return @"iTunes";
			case kQTMetaDataStorageFormatQuickTime: return @"QuickTime";
			case kQTMetaDataStorageFormatUserData: return @"UserData";
		}
	}
	NSLog( @"unknown storage format" );
	return nil;
}

- (NSString *) keyFormat;
{
	NSData *keyFormatData = [self getPropertyDataForClass: kPropertyClass_MetaDataItem ID: kQTMetaDataItemPropertyID_KeyFormat type: 0];
	if (keyFormatData) {
		OSType format = *(const OSType *)[keyFormatData bytes];
		switch (format) {
			case kQTMetaDataKeyFormatiTunesLongForm: return @"iTunes long";
			case kQTMetaDataKeyFormatiTunesShortForm: return @"iTunes short";
			case kQTMetaDataKeyFormatQuickTime: return @"QuickTime";
			case kQTMetaDataKeyFormatUserData: return @"UserData";
			case kQTMetaDataKeyFormatCommon: return @"Common";
		}
	}
	return @"Unknown";
}

- (BOOL) setValueData: (NSData *) data type: (UInt32) dataType;
{
	BOOL result = NO;
	
	if (nil != data) {
		OSStatus retVal = QTMetaDataSetItem( [container quickTimeMetadata], item, (UInt8 *)[data bytes], [data length], dataType );
		if (noErr == retVal) result = YES;
	}
	
	return result;
}

- (BOOL) setValue: (id) newValue;
{
	NSData *data = nil;
	UInt32 dataType = 0;
	
	if ([newValue isKindOfClass: [NSString class]]) {
		data = [newValue dataUsingEncoding: NSUTF8StringEncoding];
		dataType = kQTMetaDataTypeUTF8;
	} else if ([newValue isKindOfClass: [NSImage class]]) {
		data = [NSBitmapImageRep representationOfImageRepsInArray: [newValue representations] usingType:NSPNGFileType properties: nil];
		dataType = kQTMetaDataTypePNGImage;
	} else if ([newValue isKindOfClass: [NSData class]]) {
		data = newValue;
		dataType = kQTMetaDataTypeBinary;
	}
	
	return [self setValueData: data type: dataType];
}

- (BOOL) setLocale: (NSString *) newLocale;
{
	NSData *localeData = [newLocale dataUsingEncoding: NSASCIIStringEncoding allowLossyConversion: YES];
	
	BOOL result = NO;
	if (nil != localeData) {
		OSStatus retVal = QTMetaDataSetItemProperty( [container quickTimeMetadata], item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_Locale, [localeData length], [localeData bytes] );
		if (noErr == retVal) result = YES;
	}
	
	return result;
}

- (BOOL) setUInt8Value: (UInt8) newValue;
{
	return [self setValueData: BEDataForUnsignedInteger8( newValue ) type: kQTMetaDataTypeUnsignedIntegerBE];
}

- (BOOL) setUInt16Value: (UInt16) newValue;
{
	return [self setValueData: BEDataForUnsignedInteger16( newValue ) type: kQTMetaDataTypeUnsignedIntegerBE];
}

- (BOOL) setUInt32Value: (UInt32) newValue;
{
	return [self setValueData: BEDataForUnsignedInteger32( newValue ) type: kQTMetaDataTypeUnsignedIntegerBE];
}

- (BOOL) setUInt64Value: (UInt64) newValue;
{
	return [self setValueData: BEDataForUnsignedInteger64( newValue ) type: kQTMetaDataTypeUnsignedIntegerBE];
}

- (BOOL) setSInt8Value: (SInt8) newValue;
{
	return [self setValueData: BEDataForSignedInteger8( newValue ) type: kQTMetaDataTypeSignedIntegerBE];
}

- (BOOL) setSInt16Value: (SInt16) newValue;
{
	return [self setValueData: BEDataForSignedInteger16( newValue ) type: kQTMetaDataTypeSignedIntegerBE];
}

- (BOOL) setSInt32Value: (SInt32) newValue;
{
	return [self setValueData: BEDataForSignedInteger32( newValue ) type: kQTMetaDataTypeSignedIntegerBE];
}

- (BOOL) setSInt64Value: (SInt64) newValue;
{
	return [self setValueData: BEDataForSignedInteger64( newValue ) type: kQTMetaDataTypeSignedIntegerBE];
}

- (BOOL) setFloatValue: (float) newValue;
{
	return [self setValueData: BEDataForFloat( newValue ) type: kQTMetaDataTypeFloat32BE];
}

- (BOOL) setDoubleValue: (double) newValue;
{
	return [self setValueData: BEDataForDouble( newValue ) type: kQTMetaDataTypeFloat64BE];
}


- (QTMetaDataItem) quickTimeMetadataItem;
{
	return item;
}

@end