Commits

Ivan Vučica committed 994ce5b

Added metadata support.

  • Participants
  • Parent commits 293e4c5

Comments (0)

Files changed (2)

File MakeRefMovieCLI/makerefmovie.m

 #import <Foundation/Foundation.h>
 
 #define ENDIANESS_FLIP_32(x) (\
-    ((((x) & 0xFF) << 24) & 0xFF000000) | \
-    ((((x) & 0xFF00) <<  8) & 0xFF0000) | \
-    ((((x) & 0xFF0000) >>  8) & 0xFF00) | \
-    ((((x) & 0xFF000000) >> 24) & 0xFF) \
-    )
+((((x) & 0xFF) << 24) & 0xFF000000) | \
+((((x) & 0xFF00) <<  8) & 0xFF0000) | \
+((((x) & 0xFF0000) >>  8) & 0xFF00) | \
+((((x) & 0xFF000000) >> 24) & 0xFF) \
+)
+#define ENDIANESS_FLIP_16(x) (\
+((((x) & 0xFF) << 8) & 0xFF000) | \
+((((x) & 0xFF00) >>  8) & 0xFF)\
+)
 
 #pragma pack(0)
 struct ReferenceMovieDataRefRecord {
 
 @interface Atom : NSObject
 {
-	NSString * _name;
+    @public
+    NSData * _name;
 	NSMutableData * data;
 }
-@property (nonatomic, retain) NSString * name;
+@property (nonatomic, retain) NSData * name;
 @end
 @interface MovieAtom : Atom 
 {
 	if(!self)
 		return nil;
 
-    self.name = name;
+    self.name = [NSData dataWithBytes:name.UTF8String length:4];
 	data = [NSMutableData new];
 
 	return self;
 {
     uint32_t num = ENDIANESS_FLIP_32(data.length + /* size */ 4 + 4 /* name */);
 	NSMutableData * returnData = [NSMutableData dataWithBytes:&num length:4];
-    [returnData appendBytes:[_name UTF8String] length:4];
+    [returnData appendData:self.name];
     [returnData appendData:data];
     return returnData;
 }
 }
 @end
 
+//////////////////////
+// MARK: - Meta
 
+@interface MetadataAtom : Atom 
+{
+}
+@end
+@implementation MetadataAtom
+-(id)init
+{
+    self = [super initWithName:@"meta"];
+    if(!self)
+        return nil;
+    
+    [data appendBytes:(char[]){0,0,0,0} length:4]; // ???
+    
+    return self;
+}
+@end
+
+#pragma pack(1)
+struct MetadataHandlerRefRecord {
+    char version;
+    char flags[3];
+    uint32_t predefined;
+    uint32_t handler_type;
+    uint32_t reserved[3];
+    //char name[1];
+};
+#pragma pack()
+typedef struct MetadataHandlerRefRecord MetadataHandlerRefRecord;
+
+@interface MetadataHandlerAtom : Atom 
+{
+    MetadataHandlerRefRecord record;
+}
+@end
+@implementation MetadataHandlerAtom
+-(id)initWithHandler:(uint32_t)handler
+{
+    self = [super initWithName:@"hdlr"];
+    if(!self)
+        return nil;
+    
+    // version, flags and predefined are already set to 0
+    record.handler_type = ENDIANESS_FLIP_32(handler);
+    // reserved is already set to 0
+    // name will be appended later
+    
+    [data appendBytes:&record length:1+3+4+4+(4*3)];
+    
+    char name[] = "";
+    [data appendBytes:name length:sizeof(name)-1];
+    return self;
+}
+@end
+
+
+#pragma pack(1)
+struct MetadataItemKeysRefRecord {
+    char version;
+    char flags[3];
+    uint32_t entry_count;
+    // repeat entry_count times:
+    //uint32_t key_size; // key_size = sizeof(key_size) + sizeof(key_namespace) + sizeof(key_value)
+    //uint32_t key_namespace; // 'mdta'
+    //char keyvalue[key_size-8];
+};
+#pragma pack()
+typedef struct MetadataItemKeysRefRecord MetadataItemKeysRefRecord;
+@interface MetadataItemKeysAtom : Atom 
+{
+    MetadataItemKeysRefRecord record;
+}
+@end
+@implementation MetadataItemKeysAtom
+-(id)initWithKeyNames:(NSArray*)names
+{
+    self = [super initWithName:@"keys"];
+    if(!self)
+        return nil;
+    
+    // version and flags are already set to 0
+    record.entry_count = ENDIANESS_FLIP_32(names.count);
+    [data appendBytes:&record length:sizeof(record)];
+    
+    for(NSString * key in names)
+    {
+        uint32_t key_size = ENDIANESS_FLIP_32(4 + 4 + key.length);
+        uint32_t key_namespace = ENDIANESS_FLIP_32('mdta');
+        [data appendBytes:&key_size length:sizeof(key_size)];
+        [data appendBytes:&key_namespace length:sizeof(key_namespace)];
+        [data appendBytes:key.UTF8String length:key.length];
+    }
+    
+    return self;
+}
+@end
+
+
+@interface MetadataItemListAtom : Atom 
+{
+}
+@end
+@implementation MetadataItemListAtom
+-(id)init
+{
+    self = [super initWithName:@"ilst"];
+    if(!self)
+        return nil;
+    
+    return self;
+}
+@end
+
+
+@interface MetadataItemAtom : Atom 
+{
+}
+@end
+@implementation MetadataItemAtom
+
+// in case of keyindex-based metadata
+-(id)initWithKeyIndex:(uint32_t)keyIndex /* 1-based, not 0-based */
+{
+    self = [super init];
+    if(!self)
+        return nil;
+    
+    keyIndex = ENDIANESS_FLIP_32(keyIndex); 
+    self.name = [NSData dataWithBytes:&keyIndex length:4];
+    data = [NSMutableData new];
+    
+    return self;
+}
+
+
+-(id)initWithMiniName:(NSString*)miniName unknownPrefixBeforeValue:(uint16_t)prefix value:(NSData*)value
+{
+    self = [self initWithMiniName:miniName];
+    if(!self)
+        return nil;
+    
+    uint32_t adjustedPrefix = ENDIANESS_FLIP_16(prefix) << 16; // We need this: XXYY 0000
+    [data appendBytes:&adjustedPrefix length:4];
+    [data appendData:value];
+    
+    return self;
+}
+
+// in case of name 0xA9 . X .  Y . Z-based metadata
+-(id)initWithMiniName:(NSString*)miniName
+{
+    self = [super init];
+    if(!self)
+        return nil;
+    
+    
+    char nameChars[4] = 
+    { 
+        0xA9,
+        [miniName characterAtIndex:0],
+        [miniName characterAtIndex:1],
+        [miniName characterAtIndex:2]
+    };
+    
+    NSData * newName = [NSData dataWithBytes:nameChars length:4];
+    
+    self.name = newName;
+    data = [NSMutableData new];
+    
+    return self;
+}
+
+@end
+
+enum MetadataDataAtomType
+{
+    MetadataDataAtomTypeString = 0x00000001,
+    MetadataDataAtomTypeUInt32 = 0x00000015
+};
+typedef enum MetadataDataAtomType MetadataDataAtomType;
+
+enum MetadataDataAtomLocale
+{
+    MetadataDataAtomLocaleDefault = 0
+};
+typedef enum MetadataDataAtomLocale MetadataDataAtomLocale;
+
+@interface MetadataDataAtom : Atom 
+{
+}
+@end
+@implementation MetadataDataAtom
+-(id)initWithType:(MetadataDataAtomType)type locale:(MetadataDataAtomLocale)locale value:(NSData*)value
+{
+    self = [super initWithName:@"data"];
+    if(!self)
+        return nil;
+    
+    type = ENDIANESS_FLIP_32(type);
+    locale = ENDIANESS_FLIP_32(locale);
+    
+    [data appendBytes:&type length:sizeof(type)];
+    [data appendBytes:&locale length:sizeof(locale)];
+    [data appendData:value];
+    
+    return self;
+}
+@end
+
+
+@interface UserdataAtom : Atom 
+{
+}
+@end
+@implementation UserdataAtom
+-(id)init
+{
+    self = [super initWithName:@"udta"];
+    if(!self)
+        return nil;
+    
+    return self;
+}
+@end
 
 
 void usage();
 
 void usage()
 {
-	printf("  usage: makerefmovie referencedfile.extension outputfile.mov\n");	
+	printf("  usage: makerefmovie referencedfile.extension outputfile.mov [metadata]\n");
+	printf("   with metadata:\n");
+    printf("   -nam <string>: name\n");
+    printf("   -art <string>: artist\n");
+    printf("   -alb <string>: album\n");
+    printf("   -day <string>: release day\n");
+    printf("   -gen <string>: genre\n");
+    printf("   -tvsh <string>: tv show\n");
+    printf("   -tvnn <string>: tv network\n");
+    printf("   -tvsn <integer>: tv season number\n");
+    printf("   -tves <integer>: tv episode number\n");
+    printf("   -stik <integer>: media kind\n");
+    printf("     1: music, 2: audio book, 6: music video, 9: movie,\n");
+    printf("     10: tv show, 11: booklet, 14: ringtone\n");
 }
 
 int main(int argc, char ** argv)
 {
-	if(argc != 3)
+	if(argc < 3)
 	{
 		usage();
 		return 1;
 	}
     NSAutoreleasePool * pool = [NSAutoreleasePool new];
     MovieAtom * moov = [[MovieAtom new] autorelease];
+    
+    
+    
+    
     ReferenceMovieAtom * rmra = [[ReferenceMovieAtom new] autorelease];
     ReferenceMovieDescriptorAtom * rmda = [[ReferenceMovieDescriptorAtom new] autorelease];
     ReferenceMovieDataReferenceAtom * rmrf = [[[ReferenceMovieDataReferenceAtom alloc] initWithPath:[NSString stringWithUTF8String:argv[1]]] autorelease];
     [rmra appendAtom:rmda];
     [moov appendAtom:rmra];
     
-    [moov writeToFile:[NSString stringWithUTF8String:argv[2]]];
+    ///////////
+    if(argc > 3)
+    {
+        
+        UserdataAtom * udta = nil;
+        MetadataAtom * meta = nil;
+        
+        udta = [[UserdataAtom new] autorelease];
+        /*
+        NSData *namdata = [NSData dataWithBytes:"Jerry" length:5];
+        MetadataItemAtom * nam = [[[MetadataItemAtom alloc] initWithMiniName:@"nam" unknownPrefixBeforeValue:1 value:namdata] autorelease];
+        [udta appendAtom:nam];
+        */
+        meta = [[MetadataAtom new] autorelease];
+        
+        
+#if 0
+        MetadataHandlerAtom * hdlr = [[[MetadataHandlerAtom alloc] initWithHandler:'mdta'] autorelease];
+        [meta appendAtom:hdlr];
+
+        NSMutableArray * keysArray = [NSMutableArray array];
+        int argi;
+        for(argi = 3; argi < argc; argi++)
+        {
+            if(!strcmp(argv[argi], "-nam"))
+            {
+                [keysArray addObject:@"com.apple.quicktime.displayname"];
+                argi++;
+            }
+        }
+        MetadataItemKeysAtom * keys = [[[MetadataItemKeysAtom alloc] initWithKeyNames:keysArray] autorelease];
+        [meta appendAtom:keys];
+        
+        //
+
+        MetadataItemListAtom * ilst = [[MetadataItemListAtom new] autorelease];
+        for(argi = 3; argi < argc; argi++)
+        {
+            int keyIndex = argi - 3 + 1; // 1 based instead of 0 based
+            if(!strcmp(argv[argi], "-nam"))
+            {
+                argi++;
+                
+                MetadataItemAtom * nam = [[[MetadataItemAtom alloc] initWithKeyIndex:keyIndex] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [nam appendAtom:data];
+                [ilst appendAtom:nam];
+
+            }
+        }
+        
+#else
+        MetadataHandlerAtom * hdlr = [[[MetadataHandlerAtom alloc] initWithHandler:'mdir'] autorelease];
+        [meta appendAtom:hdlr];
+
+        MetadataItemListAtom * ilst = [[MetadataItemListAtom new] autorelease];
+        int argi;
+        for(argi = 3; argi < argc; argi++)
+        {
+            if(!strcmp(argv[argi], "-nam"))
+            {
+                argi++;
+                
+                MetadataItemAtom * nam = [[[MetadataItemAtom alloc] initWithMiniName:@"nam"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [nam appendAtom:data];
+                [ilst appendAtom:nam];
+            }
+            if(!strcmp(argv[argi], "-art"))
+            {
+                argi++;
+                
+                MetadataItemAtom * art = [[[MetadataItemAtom alloc] initWithMiniName:@"ART"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [art appendAtom:data];
+                [ilst appendAtom:art];
+            }
+            if(!strcmp(argv[argi], "-alb"))
+            {
+                argi++;
+                
+                MetadataItemAtom * alb = [[[MetadataItemAtom alloc] initWithMiniName:@"alb"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [alb appendAtom:data];
+                [ilst appendAtom:alb];
+            }
+            if(!strcmp(argv[argi], "-day"))
+            {
+                argi++;
+                
+                MetadataItemAtom * day = [[[MetadataItemAtom alloc] initWithMiniName:@"day"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [day appendAtom:data];
+                [ilst appendAtom:day];
+            }
+            if(!strcmp(argv[argi], "-gen"))
+            {
+                argi++;
+                
+                MetadataItemAtom * gen = [[[MetadataItemAtom alloc] initWithMiniName:@"gen"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [gen appendAtom:data];
+                [ilst appendAtom:gen];
+            }
+            if(!strcmp(argv[argi], "-tvsh"))
+            {
+                argi++;
+                
+                MetadataItemAtom * tvsh = [[[MetadataItemAtom alloc] initWithName:@"tvsh"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [tvsh appendAtom:data];
+                [ilst appendAtom:tvsh];
+            }
+            if(!strcmp(argv[argi], "-tvnn"))
+            {
+                argi++;
+                
+                MetadataItemAtom * tvnn = [[[MetadataItemAtom alloc] initWithName:@"tvnn"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [tvnn appendAtom:data];
+                [ilst appendAtom:tvnn];
+            }
+            if(!strcmp(argv[argi], "-tvsn"))
+            {
+                argi++;
+                
+                MetadataItemAtom * tvsn = [[[MetadataItemAtom alloc] initWithName:@"tvsn"] autorelease];
+                NSData * value = [NSData dataWithBytes:&(uint32_t){ENDIANESS_FLIP_32(atoi(argv[argi]))} length:4];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeUInt32 locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [tvsn appendAtom:data];
+                [ilst appendAtom:tvsn];
+            }
+            if(!strcmp(argv[argi], "-tves"))
+            {
+                argi++;
+                
+                MetadataItemAtom * tves = [[[MetadataItemAtom alloc] initWithName:@"tves"] autorelease];
+                NSData * value = [NSData dataWithBytes:&(uint32_t){ENDIANESS_FLIP_32(atoi(argv[argi]))} length:4];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeUInt32 locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [tves appendAtom:data];
+                [ilst appendAtom:tves];
+            }
+            if(!strcmp(argv[argi], "-stik"))
+            {
+                argi++;
+                
+                MetadataItemAtom * stik = [[[MetadataItemAtom alloc] initWithName:@"stik"] autorelease];
+                NSData * value = [NSData dataWithBytes:&(uint32_t){ENDIANESS_FLIP_32(atoi(argv[argi]))} length:4];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeUInt32 locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [stik appendAtom:data];
+                [ilst appendAtom:stik];
+            }
+        }
+
+#endif
+        
+        [meta appendAtom:ilst];
+        [udta appendAtom:meta];
+        [moov appendAtom:udta];
+
+    }    
+    
+    
+    
+    /*
+    if(argc > 3)
+    {
+        UserdataAtom * udta = [[UserdataAtom new] autorelease];
+        int argi;
+        for(argi = 3; argi < argc; argi++)
+        {
+            if(!strcmp(argv[argi], "-nam"))
+            {
+                argi++;
+                
+                MetadataItemAtom * nam = [[[MetadataItemAtom alloc] initWithMiniName:@"nam"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [nam appendAtom:data];
+                [udta appendAtom:nam];
+                
+            }
+        }
+        [moov appendAtom:udta];
+    }
+     */
+    /*
+    if(argc > 3)
+    {
+        MetadataAtom * meta = [[MetadataAtom new] autorelease];
+        MetadataHandlerAtom * hdlr = [[[MetadataHandlerAtom alloc] initWithHandler:'mdir'] autorelease];
+        [meta appendAtom:hdlr];
+        
+        MetadataItemListAtom * ilst = [[MetadataItemListAtom new] autorelease];
+        int argi;
+        for(argi = 3; argi < argc; argi++)
+        {
+            if(!strcmp(argv[argi], "-nam"))
+            {
+                argi++;
+
+                MetadataItemAtom * nam = [[[MetadataItemAtom alloc] initWithMiniName:@"nam"] autorelease];
+                NSData * value = [NSData dataWithBytes:argv[argi] length:strlen(argv[argi])];
+                MetadataDataAtom * data = [[[MetadataDataAtom alloc] initWithType:MetadataDataAtomTypeString locale:MetadataDataAtomLocaleDefault value:value] autorelease];
+                
+                [nam appendAtom:data];
+                [ilst appendAtom:nam];
+                
+            }
+        }
+        [meta appendAtom:ilst];
+        
+        [moov appendAtom:meta];
+    }    
+    */
+    
+    //[moov writeToFile:[NSString stringWithUTF8String:argv[2]]];
+    
+    
+    Atom * a = [[[Atom alloc] initWithName:@"ftyp"] autorelease];
+    [a->data appendBytes:(char[]){'3','g','2','a'} length:4]; // major brand
+    [a->data appendBytes:(char[]){0,0,0,0} length:4]; // version
+    //[a writeToFile:[NSString stringWithUTF8String:argv[2]]];
+    
+    
+    NSMutableData * outData = [NSMutableData data];
+    //[outData appendData:[a resultData]];
+    [outData appendData:[moov resultData]];
+    [outData writeToFile:[NSString stringWithUTF8String:argv[2]] atomically:YES];
     
     [pool release];
     
 Based on information published on:
 <https://developer.apple.com/quicktime/icefloe/dispatch015.html>
 
+Metadata
+--------
+
+You can also add some metadata to the produced reference files. After
+first two arguments, just add additional options.
+
+* -nam <string>: name
+* -art <string>: artist
+* -alb <string>: album
+* -day <string>: release day
+* -gen <string>: genre
+* -tvsh <string>: tv show
+* -tvnn <string>: tv network
+* -tvsn <integer>: tv season number
+* -tves <integer>: tv episode number
+* -stik <integer>: media kind
+
+Available media kinds:
+
+    1: music, 2: audio book, 6: music video, 9: movie,
+    10: tv show, 11: booklet, 14: ringtone
+
 License
 -------