/*---------------------------------------------------------------------- | Mp4ParserOutput_SetSampleDescription +---------------------------------------------------------------------*/ static BLT_Result Mp4ParserOutput_SetSampleDescription(Mp4ParserOutput* self, unsigned int indx) { // if we had a decrypter before, release it now delete self->sample_decrypter; self->sample_decrypter = NULL; // check that the audio track is of the right type AP4_SampleDescription* sample_desc = self->track->GetSampleDescription(indx); if (sample_desc == NULL) { ATX_LOG_FINE("no sample description for track"); return BLT_ERROR_INVALID_MEDIA_FORMAT; } // handle encrypted tracks BLT_Result result = Mp4ParserOutput_ProcessCryptoInfo(self, sample_desc); if (BLT_FAILED(result)) return result; // update the generic part of the stream info BLT_StreamInfo stream_info; stream_info.id = self->track->GetId(); stream_info.duration = self->track->GetDurationMs(); stream_info.mask = BLT_STREAM_INFO_MASK_ID | BLT_STREAM_INFO_MASK_DURATION; // deal with audio details, if this is an audio track AP4_AudioSampleDescription* audio_desc = dynamic_cast<AP4_AudioSampleDescription*>(sample_desc); if (audio_desc) { ATX_LOG_FINE("sample description is audio"); stream_info.type = BLT_STREAM_TYPE_AUDIO; stream_info.channel_count = audio_desc->GetChannelCount(); stream_info.sample_rate = audio_desc->GetSampleRate(); stream_info.mask |= BLT_STREAM_INFO_MASK_TYPE | BLT_STREAM_INFO_MASK_CHANNEL_COUNT | BLT_STREAM_INFO_MASK_SAMPLE_RATE; } else if (self == &self->parser->audio_output) { ATX_LOG_FINE("expected audio sample description, but did not get one"); return BLT_ERROR_INVALID_MEDIA_FORMAT; } AP4_VideoSampleDescription* video_desc = dynamic_cast<AP4_VideoSampleDescription*>(sample_desc); if (video_desc) { ATX_LOG_FINE("sample description is video"); stream_info.type = BLT_STREAM_TYPE_VIDEO; stream_info.width = video_desc->GetWidth(); stream_info.height = video_desc->GetHeight(); stream_info.mask |= BLT_STREAM_INFO_MASK_TYPE | BLT_STREAM_INFO_MASK_WIDTH | BLT_STREAM_INFO_MASK_HEIGHT; } else if (self == &self->parser->video_output) { ATX_LOG_FINE("expected video sample descriton, but did not get one"); return BLT_ERROR_INVALID_MEDIA_FORMAT; } AP4_MpegSampleDescription* mpeg_desc = NULL; if (sample_desc->GetType() == AP4_SampleDescription::TYPE_MPEG) { ATX_LOG_FINE("sample description is of type MPEG"); mpeg_desc = dynamic_cast<AP4_MpegSampleDescription*>(sample_desc); } if (mpeg_desc) { stream_info.data_type = mpeg_desc->GetObjectTypeString(mpeg_desc->GetObjectTypeId()); stream_info.average_bitrate = mpeg_desc->GetAvgBitrate(); stream_info.nominal_bitrate = mpeg_desc->GetAvgBitrate(); stream_info.mask |= BLT_STREAM_INFO_MASK_AVERAGE_BITRATE | BLT_STREAM_INFO_MASK_NOMINAL_BITRATE | BLT_STREAM_INFO_MASK_DATA_TYPE; } // setup the output media type AP4_DataBuffer decoder_info; BLT_MediaTypeId media_type_id = BLT_MEDIA_TYPE_ID_NONE; AP4_UI32 format_or_object_type_id = 0; if (mpeg_desc) { decoder_info.SetData(mpeg_desc->GetDecoderInfo().GetData(), mpeg_desc->GetDecoderInfo().GetDataSize()); media_type_id = self->mp4_es_type_id; format_or_object_type_id = mpeg_desc->GetObjectTypeId(); } else { // here we have to be format-specific for the decoder info stream_info.data_type = AP4_GetFormatName(sample_desc->GetFormat()); stream_info.mask |= BLT_STREAM_INFO_MASK_DATA_TYPE; format_or_object_type_id = sample_desc->GetFormat(); if (sample_desc->GetFormat() == AP4_SAMPLE_FORMAT_AVC1) { // look for an 'avcC' atom AP4_AvccAtom* avcc = static_cast<AP4_AvccAtom*>(sample_desc->GetDetails().GetChild(AP4_ATOM_TYPE_AVCC)); if (avcc) { // pass the avcc payload as the decoder info decoder_info.SetData(avcc->GetRawBytes().GetData(), avcc->GetRawBytes().GetDataSize()); } } else if (sample_desc->GetFormat() == AP4_SAMPLE_FORMAT_ALAC) { // look for an 'alac' atom (either top-level or inside a 'wave') AP4_Atom* alac = sample_desc->GetDetails().GetChild(AP4_SAMPLE_FORMAT_ALAC); if (alac == NULL) { AP4_ContainerAtom* wave = dynamic_cast<AP4_ContainerAtom*>(sample_desc->GetDetails().GetChild(AP4_ATOM_TYPE_WAVE)); if (wave) { alac = wave->GetChild(AP4_SAMPLE_FORMAT_ALAC); } } if (alac) { // pass the alac payload as the decoder info AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream((AP4_Size)alac->GetSize()); alac->WriteFields(*mbs); decoder_info.SetData(mbs->GetData(), mbs->GetDataSize()); mbs->Release(); } } media_type_id = self->iso_base_es_type_id; } BLT_Mp4MediaType* media_type = NULL; unsigned int struct_size = decoder_info.GetDataSize()?decoder_info.GetDataSize()-1:0; if (audio_desc) { struct_size += sizeof(BLT_Mp4AudioMediaType); BLT_Mp4AudioMediaType* audio_type = (BLT_Mp4AudioMediaType*)ATX_AllocateZeroMemory(struct_size);; audio_type->base.stream_type = BLT_MP4_STREAM_TYPE_AUDIO; audio_type->channel_count = audio_desc->GetChannelCount(); audio_type->sample_rate = audio_desc->GetSampleRate(); audio_type->decoder_info_length = decoder_info.GetDataSize(); if (decoder_info.GetDataSize()) { ATX_CopyMemory(&audio_type->decoder_info[0], decoder_info.GetData(), decoder_info.GetDataSize()); } media_type = &audio_type->base; } else { struct_size += sizeof(BLT_Mp4VideoMediaType); BLT_Mp4VideoMediaType* video_type = (BLT_Mp4VideoMediaType*)ATX_AllocateZeroMemory(struct_size); video_type->base.stream_type = BLT_MP4_STREAM_TYPE_VIDEO; video_type->width = video_desc->GetWidth(); video_type->height = video_desc->GetHeight(); video_type->decoder_info_length = decoder_info.GetDataSize(); if (decoder_info.GetDataSize()) { ATX_CopyMemory(&video_type->decoder_info[0], decoder_info.GetData(), decoder_info.GetDataSize()); } media_type = &video_type->base; } media_type->base.id = media_type_id; media_type->base.extension_size = struct_size-sizeof(BLT_MediaType); media_type->format_or_object_type_id = format_or_object_type_id; self->media_type = &media_type->base; self->sample_description_index = indx; // final update to the stream info BLT_Stream_SetInfo(ATX_BASE(self->parser, BLT_BaseMediaNode).context, &stream_info); // enable the track in the linear reader if we have one if (self->parser->input.reader) { self->parser->input.reader->EnableTrack(self->track->GetId()); } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpDecryptingProcessor:CreateTrackHandler +---------------------------------------------------------------------*/ AP4_Processor::TrackHandler* AP4_MarlinIpmpDecryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak) { // look for this track in the list of entries AP4_MarlinIpmpParser::SinfEntry* sinf_entry = NULL; for (AP4_List<AP4_MarlinIpmpParser::SinfEntry>::Item* sinf_entry_item = m_SinfEntries.FirstItem(); sinf_entry_item; sinf_entry_item = sinf_entry_item->GetNext()) { sinf_entry = sinf_entry_item->GetData(); if (sinf_entry->m_TrackId == trak->GetId()) { break; // match } else { sinf_entry = NULL; // no match } } if (sinf_entry == NULL) return NULL; // no matching entry AP4_ContainerAtom* sinf = sinf_entry->m_Sinf; // check the scheme bool use_group_key; AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, sinf->GetChild(AP4_ATOM_TYPE_SCHM)); if (schm == NULL) return NULL; // no schm if (schm->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC && schm->GetSchemeVersion() == 0x0100) { use_group_key = false; } else if (schm->GetSchemeType() == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACGK && schm->GetSchemeVersion() == 0x0100) { use_group_key = true; } else { // unsupported scheme return NULL; } // find the key const AP4_DataBuffer* key = NULL; AP4_DataBuffer unwrapped_key; if (use_group_key) { const AP4_DataBuffer* group_key = m_KeyMap.GetKey(0); if (group_key == NULL) return NULL; // no group key AP4_ContainerAtom* schi = AP4_DYNAMIC_CAST(AP4_ContainerAtom, sinf->GetChild(AP4_ATOM_TYPE_SCHI)); if (schi == NULL) return NULL; // no schi AP4_Atom* gkey = schi->GetChild(AP4_ATOM_TYPE_GKEY); if (gkey == NULL) return NULL; // no gkey AP4_MemoryByteStream* gkey_data = new AP4_MemoryByteStream(); gkey->WriteFields(*gkey_data); AP4_AesKeyUnwrap(group_key->GetData(), gkey_data->GetData(), gkey_data->GetDataSize(), unwrapped_key); key = &unwrapped_key; gkey_data->Release(); } else { key = m_KeyMap.GetKey(sinf_entry->m_TrackId); } if (key == NULL) return NULL; // create the decrypter AP4_MarlinIpmpTrackDecrypter* decrypter = NULL; AP4_Result result = AP4_MarlinIpmpTrackDecrypter::Create(*m_BlockCipherFactory, key->GetData(), key->GetDataSize(), decrypter); if (AP4_FAILED(result)) return NULL; return decrypter; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc < 4) { PrintUsageAndExit(); } // parse arguments const char* atom_path = NULL; const char* input_filename = NULL; const char* output_filename = NULL; bool payload_only = false; char* arg; while ((arg = *++argv)) { if (!strcmp(arg, "--payload-only")) { payload_only = true; } else if (atom_path == NULL) { atom_path = arg; } else if (input_filename == NULL) { input_filename = arg; } else if (output_filename == NULL) { output_filename = arg; } else { fprintf(stderr, "ERROR: invalid command line argument (%s)\n", arg); return 1; } } // check arguments if (atom_path == NULL) { fprintf(stderr, "ERROR: missing atom path\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 the input stream AP4_Result result; 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; } // parse the atoms AP4_AtomParent top_level; AP4_Atom* atom; AP4_AtomFactory& atom_factory = AP4_DefaultAtomFactory::Instance; while (atom_factory.CreateAtomFromStream(*input, atom) == AP4_SUCCESS) { top_level.AddChild(atom); } // release the input input->Release(); // find the atom atom = top_level.FindChild(atom_path); if (atom == NULL) { fprintf(stderr, "ERROR: atom '%s' not found\n", atom_path); 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; } // write the atom if (payload_only) { atom->WriteFields(*output); } else { atom->Write(*output); } // cleanup output->Release(); return 0; }