Exemple #1
0
/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    if (argc == 1) PrintUsageAndExit();

    // parse options
    const char*              kms_uri = NULL;
    enum Method              method  = METHOD_NONE;
    const char*              input_filename = NULL;
    const char*              output_filename = NULL;
    const char*              fragments_info_filename = NULL;
    AP4_ProtectionKeyMap     key_map;
    AP4_TrackPropertyMap     property_map;
    bool                     show_progress = false;
    bool                     strict = false;
    AP4_Array<AP4_PsshAtom*> pssh_atoms;
    AP4_DataBuffer           kids;
    unsigned int             kid_count = 0;
    AP4_Result               result;
    
    // parse the command line arguments
    char* arg;
    while ((arg = *++argv)) {
        if (!strcmp(arg, "--method")) {
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --method option\n");
                return 1;
            }
            if (!strcmp(arg, "OMA-PDCF-CBC")) {
                method = METHOD_OMA_PDCF_CBC;
            } else if (!strcmp(arg, "OMA-PDCF-CTR")) {
                method = METHOD_OMA_PDCF_CTR;
            } else if (!strcmp(arg, "MARLIN-IPMP-ACBC")) {
                method = METHOD_MARLIN_IPMP_ACBC;
            } else if (!strcmp(arg, "MARLIN-IPMP-ACGK")) {
                method = METHOD_MARLIN_IPMP_ACGK;
            } else if (!strcmp(arg, "PIFF-CBC")) {
                method = METHOD_PIFF_CBC;
            } else if (!strcmp(arg, "PIFF-CTR")) {
                method = METHOD_PIFF_CTR;
            } else if (!strcmp(arg, "MPEG-CENC")) {
                method = METHOD_MPEG_CENC;
            } else if (!strcmp(arg, "ISMA-IAEC")) {
                method = METHOD_ISMA_AES;
            } else {
                fprintf(stderr, "ERROR: invalid value for --method argument\n");
                return 1;
            }
        } else if (!strcmp(arg, "--fragments-info")) {
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --fragments-info option\n");
                return 1;
            }
            fragments_info_filename = arg;
        } else if (!strcmp(arg, "--pssh") || !strcmp(arg, "--pssh-v1")) {
            bool v1 = (strcmp(arg, "--pssh-v1") == 0);
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --pssh\n");
                return 1;
            }
            if (AP4_StringLength(arg) < 32+1 || arg[32] != ':') {
                fprintf(stderr, "ERROR: invalid argument syntax for --pssh\n");
                return 1;
            }
            unsigned char system_id[16];
            arg[32] = '\0';
            result = AP4_ParseHex(arg, system_id, 16);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "ERROR: invalid argument syntax for --pssh\n");
                return 1;
            }
            const char* pssh_filename = arg+33;
            
            // load the pssh payload
            AP4_DataBuffer pssh_payload;
            if (pssh_filename[0]) {
                AP4_ByteStream* pssh_input = NULL;
                result = AP4_FileByteStream::Create(pssh_filename, AP4_FileByteStream::STREAM_MODE_READ, pssh_input);
                if (AP4_FAILED(result)) {
                    fprintf(stderr, "ERROR: cannot open pssh payload file (%d)\n", result);
                    return 1;
                }
                AP4_LargeSize pssh_payload_size = 0;
                pssh_input->GetSize(pssh_payload_size);
                pssh_payload.SetDataSize((AP4_Size)pssh_payload_size);
                result = pssh_input->Read(pssh_payload.UseData(), (AP4_Size)pssh_payload_size);
                if (AP4_FAILED(result)) {
                    fprintf(stderr, "ERROR: cannot read pssh payload from file (%d)\n", result);
                    return 1;
                }
            }
            AP4_PsshAtom* pssh;
            if (v1) {
                if (kid_count) {
                    pssh = new AP4_PsshAtom(system_id, kids.GetData(), kid_count);
                } else {
                    pssh = new AP4_PsshAtom(system_id);
                }
            } else {
                pssh = new AP4_PsshAtom(system_id);
            }
            if (pssh_payload.GetDataSize()) {
                pssh->SetData(pssh_payload.GetData(), pssh_payload.GetDataSize());
            }
            pssh_atoms.Append(pssh);
        } else if (!strcmp(arg, "--kms-uri")) {
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --kms-uri option\n");
                return 1;
            }
            if (method != METHOD_ISMA_AES) {
                fprintf(stderr, "ERROR: --kms-uri only applies to method ISMA-IAEC\n");
                return 1;
            }
            kms_uri = arg;
        } else if (!strcmp(arg, "--show-progress")) {
            show_progress = true;
        } else if (!strcmp(arg, "--strict")) {
            strict = true;
        } else if (!strcmp(arg, "--key")) {
            if (method == METHOD_NONE) {
                fprintf(stderr, "ERROR: --method argument must appear before --key\n");
                return 1;
            }
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --key option\n");
                return 1;
            }
            char* track_ascii = NULL;
            char* key_ascii = NULL;
            char* iv_ascii = NULL;
            if (AP4_FAILED(AP4_SplitArgs(arg, track_ascii, key_ascii, iv_ascii))) {
                fprintf(stderr, "ERROR: invalid argument for --key option\n");
                return 1;
            }
            unsigned int track = strtoul(track_ascii, NULL, 10);

            
            // parse the key value
            unsigned char key[16];
            AP4_SetMemory(key, 0, sizeof(key));
            if (AP4_CompareStrings(key_ascii, "random") == 0) {
                result = AP4_System_GenerateRandomBytes(key, 16);
                if (AP4_FAILED(result)) {
                    fprintf(stderr, "ERROR: failed to generate random key (%d)\n", result);
                    return 1;
                }
                char key_hex[32+1];
                key_hex[32] = '\0';
                AP4_FormatHex(key, 16, key_hex);
                printf("KEY.%d=%s\n", track, key_hex);
            } else {
                if (AP4_ParseHex(key_ascii, key, 16)) {
                    fprintf(stderr, "ERROR: invalid hex format for key\n");
                    return 1;
                }
            }
            
            // parse the iv
            unsigned char iv[16];
            AP4_SetMemory(iv, 0, sizeof(iv));
            if (AP4_CompareStrings(iv_ascii, "random") == 0) {
                result = AP4_System_GenerateRandomBytes(iv, 16);
                if (AP4_FAILED(result)) {
                    fprintf(stderr, "ERROR: failed to generate random key (%d)\n", result);
                    return 1;
                }
                iv[0] &= 0x7F; // always set the MSB to 0 so we don't have wraparounds
            } else {
                unsigned int iv_size = (unsigned int)AP4_StringLength(iv_ascii)/2;
                if (AP4_ParseHex(iv_ascii, iv, iv_size)) {
                    fprintf(stderr, "ERROR: invalid hex format for iv\n");
                    return 1;
                }
            }
            switch (method) {
                case METHOD_OMA_PDCF_CTR:
                case METHOD_ISMA_AES:
                case METHOD_PIFF_CTR:
                case METHOD_MPEG_CENC:
                    // truncate the IV
                    AP4_SetMemory(&iv[8], 0, 8);
                    break;
                    
                default:
                    break;
            }
            
            // check that the key is not already there
            if (key_map.GetKey(track)) {
                fprintf(stderr, "ERROR: key already set for track %d\n", track);
                return 1;
            }
            
            // set the key in the map
            key_map.SetKey(track, key, 16, iv, 16);
        } else if (!strcmp(arg, "--property")) {
            char* track_ascii = NULL;
            char* name = NULL;
            char* value = NULL;
            if (method != METHOD_OMA_PDCF_CBC     && 
                method != METHOD_OMA_PDCF_CTR     &&
                method != METHOD_MARLIN_IPMP_ACBC &&
                method != METHOD_MARLIN_IPMP_ACGK &&
                method != METHOD_PIFF_CBC         &&
                method != METHOD_PIFF_CTR         &&
                method != METHOD_MPEG_CENC) {
                fprintf(stderr, "ERROR: this method does not use properties\n");
                return 1;
            }
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --property option\n");
                return 1;
            }
            if (AP4_FAILED(AP4_SplitArgs(arg, track_ascii, name, value))) {
                fprintf(stderr, "ERROR: invalid argument for --property option\n");
                return 1;
            }
            unsigned int track = strtoul(track_ascii, NULL, 10);

            // check that the property is not already set
            if (property_map.GetProperty(track, name)) {
                fprintf(stderr, "ERROR: property %s already set for track %d\n",
                                name, track);
                return 1;
            }
            // set the property in the map
            property_map.SetProperty(track, name, value);
            
            // special treatment for KID properties
            if (!strcmp(name, "KID")) {
                if (AP4_StringLength(value) != 32) {
                    fprintf(stderr, "ERROR: invalid size for KID property (must be 32 hex chars)\n");
                    return 1;
                }
                AP4_UI08 kid[16];
                if (AP4_FAILED(AP4_ParseHex(value, kid, 16))) {
                    fprintf(stderr, "ERROR: invalid syntax for KID property (must be 32 hex chars)\n");
                    return 1;
                }
                
                // check if we already have this KID
                bool kid_already_present = false;
                for (unsigned int i=0; i<kid_count; i++) {
                    if (AP4_CompareMemory(kids.GetData()+(i*16), kid, 16) == 0) {
                        kid_already_present = true;
                        break;
                    }
                }
                if (!kid_already_present) {
                    ++kid_count;
                    kids.AppendData(kid, 16);
                }
            }
        } else if (!strcmp(arg, "--global-option")) {
            arg = *++argv;
            char* name = NULL;
            char* value = NULL;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --global-option option\n");
                return 1;
            }
            if (AP4_FAILED(AP4_SplitArgs(arg, name, value))) {
                fprintf(stderr, "ERROR: invalid argument for --global-option option\n");
                return 1;
            }
            AP4_GlobalOptions::SetString(name, value);
        } else if (input_filename == NULL) {
            input_filename = arg;
        } else if (output_filename == NULL) {
            output_filename = arg;
        } else {
            fprintf(stderr, "ERROR: unexpected argument (%s)\n", arg);
            return 1;
        }
    }

    // check the arguments
    if (method == METHOD_NONE) {
        fprintf(stderr, "ERROR: missing --method argument\n");
        return 1;
    }
    if (input_filename == NULL) {
        fprintf(stderr, "ERROR: missing input filename\n");
        return 1;
    }
    if (output_filename == NULL) {
        fprintf(stderr, "ERROR: missing output filename\n");
        return 1;
    }

    // create an encrypting processor
    AP4_Processor* processor = NULL;
    if (method == METHOD_ISMA_AES) {
        if (kms_uri == NULL) {
            fprintf(stderr, "ERROR: method ISMA-IAEC requires --kms-uri\n");
            return 1;
        }
        AP4_IsmaEncryptingProcessor* isma_processor = new AP4_IsmaEncryptingProcessor(kms_uri);
        isma_processor->GetKeyMap().SetKeys(key_map);
        processor = isma_processor;
    } else if (method == METHOD_MARLIN_IPMP_ACBC ||
               method == METHOD_MARLIN_IPMP_ACGK) {
        bool use_group_key = (method == METHOD_MARLIN_IPMP_ACGK);
        if (use_group_key) {
            // check that the group key is set
            if (key_map.GetKey(0) == NULL) {
                fprintf(stderr, "ERROR: method MARLIN-IPMP-ACGK requires a group key\n");
                return 1;
            }
        }
        AP4_MarlinIpmpEncryptingProcessor* marlin_processor = 
            new AP4_MarlinIpmpEncryptingProcessor(use_group_key);
        marlin_processor->GetKeyMap().SetKeys(key_map);
        marlin_processor->GetPropertyMap().SetProperties(property_map);
        processor = marlin_processor;
    } else if (method == METHOD_OMA_PDCF_CTR ||
               method == METHOD_OMA_PDCF_CBC) {
        AP4_OmaDcfEncryptingProcessor* oma_processor = 
            new AP4_OmaDcfEncryptingProcessor(method == METHOD_OMA_PDCF_CTR?
                                              AP4_OMA_DCF_CIPHER_MODE_CTR :
                                              AP4_OMA_DCF_CIPHER_MODE_CBC);
        oma_processor->GetKeyMap().SetKeys(key_map);
        oma_processor->GetPropertyMap().SetProperties(property_map);
        processor = oma_processor;
    } else if (method == METHOD_PIFF_CTR ||
               method == METHOD_PIFF_CBC ||
               method == METHOD_MPEG_CENC) {
        AP4_CencVariant variant = AP4_CENC_VARIANT_MPEG;
        switch (method) {
            case METHOD_PIFF_CBC:
                variant = AP4_CENC_VARIANT_PIFF_CBC;
                break;
                
            case METHOD_PIFF_CTR:
                variant = AP4_CENC_VARIANT_PIFF_CTR;
                break;
                
            case METHOD_MPEG_CENC:
                variant = AP4_CENC_VARIANT_MPEG;
                break;
                
            default:
                break;
        }
        AP4_CencEncryptingProcessor* cenc_processor = new AP4_CencEncryptingProcessor(variant);
        cenc_processor->GetKeyMap().SetKeys(key_map);
        cenc_processor->GetPropertyMap().SetProperties(property_map);
        for (unsigned int i=0; i<pssh_atoms.ItemCount(); i++) {
            cenc_processor->GetPsshAtoms().Append(pssh_atoms[i]);
        }
        processor = cenc_processor;
    }
    
    // create the input stream
    AP4_ByteStream* input = NULL;
    result = AP4_FileByteStream::Create(input_filename, AP4_FileByteStream::STREAM_MODE_READ, input);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open input file (%s)\n", input_filename);
        return 1;
    }

    // create the output stream
    AP4_ByteStream* output = NULL;
    result = AP4_FileByteStream::Create(output_filename, AP4_FileByteStream::STREAM_MODE_WRITE, output);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open output file (%s)\n", output_filename);
        return 1;
    }

    // create the fragments info stream if needed
    AP4_ByteStream* fragments_info = NULL;
    if (fragments_info_filename) {
        result = AP4_FileByteStream::Create(fragments_info_filename, AP4_FileByteStream::STREAM_MODE_READ, fragments_info);
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: cannot open fragments info file (%s)\n", fragments_info_filename);
            return 1;
        }
    }
    
    // process/decrypt the file
    ProgressListener listener;
    if (fragments_info) {
        bool check = CheckWarning(*fragments_info, key_map, method);
        if (strict && check) return 1;
        result = processor->Process(*input, *output, *fragments_info, show_progress?&listener:NULL);
    } else {
        bool check = CheckWarning(*input, key_map, method);
        if (strict && check) return 1;
        result = processor->Process(*input, *output, show_progress?&listener:NULL);
    }
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: failed to process the file (%d)\n", result);
    }

    // cleanup
    delete processor;
    input->Release();
    output->Release();
    if (fragments_info) fragments_info->Release();
    for (unsigned int i=0; i<pssh_atoms.ItemCount(); i++) {
        delete pssh_atoms[i];
    }
    
    return 0;
}
Exemple #2
0
/*----------------------------------------------------------------------
|   AP4_AtomFactory::CreateAtomFromStream
+---------------------------------------------------------------------*/
AP4_Result
AP4_AtomFactory::CreateAtomFromStream(AP4_ByteStream& stream, 
                                      AP4_UI32        type,
                                      AP4_UI32        size_32,
                                      AP4_UI64        size_64,
                                      AP4_Atom*&      atom)
{
    bool atom_is_large = (size_32 == 1);
    bool force_64 = (size_32==1 && ((size_64>>32) == 0));
    
    // create the atom
    if (GetContext() == AP4_ATOM_TYPE_STSD) {
        // sample entry
        if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
        switch (type) {
          case AP4_ATOM_TYPE_MP4A:
            atom = new AP4_Mp4aSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4V:
            atom = new AP4_Mp4vSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4S:
            atom = new AP4_Mp4sSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCA:
            atom = new AP4_EncaSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCV:
            atom = new AP4_EncvSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMS:
            atom = new AP4_DrmsSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMI:
            atom = new AP4_DrmiSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_AVC1:
          case AP4_ATOM_TYPE_AVC2:
          case AP4_ATOM_TYPE_AVC3:
          case AP4_ATOM_TYPE_AVC4:
            atom = new AP4_AvcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_HEV1:
          case AP4_ATOM_TYPE_HVC1:
            atom = new AP4_HevcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ALAC:
          case AP4_ATOM_TYPE_AC_3:
          case AP4_ATOM_TYPE_EC_3:
          case AP4_ATOM_TYPE_DTSC:
          case AP4_ATOM_TYPE_DTSH:
          case AP4_ATOM_TYPE_DTSL:
          case AP4_ATOM_TYPE_DTSE:
            atom = new AP4_AudioSampleEntry(type, size_32, stream, *this);
            break;
            
          case AP4_ATOM_TYPE_RTP_:
            atom = new AP4_RtpHintSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STPP:
            atom = new AP4_SubtitleSampleEntry(type, size_32, stream, *this);
            break;

          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            // no custom handler, create a generic entry
            if (atom == NULL) {
                atom = new AP4_UnknownSampleEntry(type, (AP4_UI32)size_64, stream);
            }

            break;
          }
        }
    } else {
        // regular atom
        switch (type) {
          case AP4_ATOM_TYPE_MOOV:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MoovAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MVHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MvhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MEHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MehdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRAK:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrakAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_TREX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrexAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HDLR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HdlrAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TKHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TkhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRUN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrunAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFRA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfraAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfroAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MDHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MdhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StsdAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STSC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StscAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STCO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StcoAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CO64:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Co64Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StszAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STZ2:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Stz2Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CTTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_CttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StssAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IODS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IodsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ESDS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_EsdsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AvccAtom::Create(size_32, stream);
            break;
            
          case AP4_ATOM_TYPE_HVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HvccAtom::Create(size_32, stream);
            break;

    #if !defined(AP4_CONFIG_MINI_BUILD)
          case AP4_ATOM_TYPE_UUID: {
              AP4_UI08 uuid[16];
              AP4_Result result = stream.Read(uuid, 16);
              if (AP4_FAILED(result)) return result;
              
              if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_TRACK_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffTrackEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_SAMPLE_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffSampleEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else {
                  atom = new AP4_UnknownUuidAtom(size_64, uuid, stream);
              }
              break;
          }
            
          case AP4_ATOM_TYPE_8ID_:
            atom = new AP4_NullTerminatedStringAtom(type, size_64, stream);
            break;

          case AP4_ATOM_TYPE_8BDL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_8bdlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_DREF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_DrefAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_URL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_UrlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ELST:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ElstAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_VMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_VmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_NMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_NmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SthdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_FRMA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FrmaAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SCHM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SchmAtom::Create(size_32, &m_ContextStack, stream);
            break;

          case AP4_ATOM_TYPE_FTYP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FtypAtom::Create(size_32, stream);
            break;
                        
          case AP4_ATOM_TYPE_TIMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TimsAtom::Create(size_32, stream);
            break;
     
          case AP4_ATOM_TYPE_SDP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SdpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IKMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IkmsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISFM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsfmAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISLT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsltAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ODHE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdheAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_OHDR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OhdrAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ODDA:
            atom = AP4_OddaAtom::Create(size_64, stream);
            break;

          case AP4_ATOM_TYPE_ODAF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdafAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_GRPI:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_GrpiAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IPRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IproAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_RTP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_RtpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFDT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfdtAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaizAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaioAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PDIN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PdinAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_BLOC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_BlocAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AINF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AinfAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PSSH:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PsshAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SIDX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SidxAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SBGP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SbgpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SGPD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SgpdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MKID:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_MARL) {
                atom = AP4_MkidAtom::Create(size_32, stream);
            }
            break;

          case AP4_ATOM_TYPE_DEC3:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_EC_3) {
                atom = AP4_Dec3Atom::Create(size_32, stream);
            }
            break;

          case AP4_ATOM_TYPE_XML:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_XmlAtom::Create(size_32, stream);
            break;

          // track ref types
          case AP4_ATOM_TYPE_HINT:
          case AP4_ATOM_TYPE_CDSC:
          case AP4_ATOM_TYPE_SYNC:
          case AP4_ATOM_TYPE_MPOD:
          case AP4_ATOM_TYPE_DPND:
          case AP4_ATOM_TYPE_IPIR:
          case AP4_ATOM_TYPE_ALIS:
          case AP4_ATOM_TYPE_CHAP:
            if (GetContext() == AP4_ATOM_TYPE_TREF) {
                if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
                atom = AP4_TrefTypeAtom::Create(type, size_32, stream);
            }
            break;

    #endif // AP4_CONFIG_MINI_BUILD

          // container atoms
          case AP4_ATOM_TYPE_MOOF:
          case AP4_ATOM_TYPE_MVEX:
          case AP4_ATOM_TYPE_TRAF:
          case AP4_ATOM_TYPE_TREF:
          case AP4_ATOM_TYPE_MFRA:
          case AP4_ATOM_TYPE_HNTI:
          case AP4_ATOM_TYPE_STBL:
          case AP4_ATOM_TYPE_MDIA:
          case AP4_ATOM_TYPE_DINF:
          case AP4_ATOM_TYPE_MINF:
          case AP4_ATOM_TYPE_SCHI:
          case AP4_ATOM_TYPE_SINF:
          case AP4_ATOM_TYPE_UDTA:
          case AP4_ATOM_TYPE_ILST:
          case AP4_ATOM_TYPE_EDTS: 
          case AP4_ATOM_TYPE_MDRI:
          case AP4_ATOM_TYPE_WAVE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            break;

          // containers, only at the top
          case AP4_ATOM_TYPE_MARL:
            if (GetContext() == 0) {
                atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            }
            break;
            
          // full container atoms
          case AP4_ATOM_TYPE_META:
          case AP4_ATOM_TYPE_ODRM:
          case AP4_ATOM_TYPE_ODKM:
            atom = AP4_ContainerAtom::Create(type, size_64, true, force_64, stream, *this);
            break;

          case AP4_ATOM_TYPE_FREE:
          case AP4_ATOM_TYPE_WIDE:
          case AP4_ATOM_TYPE_MDAT:
            // generic atoms
            break;
            
          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            break;
          }
        }
    }
    
    return AP4_SUCCESS;
}