static int dts_importer_probe( importer_t *importer ) { dts_importer_t *dts_imp = create_dts_importer( importer ); if( !dts_imp ) return LSMASH_ERR_MEMORY_ALLOC; lsmash_bits_t *bits = dts_imp->info.bits; lsmash_bs_t *bs = bits->bs; bs->buffer.max_size = DTS_MAX_EXSS_SIZE; importer->info = dts_imp; int err = dts_importer_get_next_accessunit_internal( importer ); if( err < 0 ) goto fail; lsmash_audio_summary_t *summary = dts_create_summary( &dts_imp->info ); if( !summary ) { err = LSMASH_ERR_NAMELESS; goto fail; } if( importer->status != IMPORTER_EOF ) importer->status = IMPORTER_OK; dts_imp->au_number = 0; if( lsmash_add_entry( importer->summaries, summary ) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); err = LSMASH_ERR_MEMORY_ALLOC; goto fail; } return 0; fail: remove_dts_importer( dts_imp ); importer->info = NULL; return err; }
static lsmash_video_summary_t *vc1_create_summary( vc1_info_t *info, vc1_sequence_header_t *sequence, uint32_t max_au_length ) { if( !info->sequence.present || !info->entry_point.present ) return NULL; lsmash_video_summary_t *summary = (lsmash_video_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_VIDEO ); if( !summary ) return NULL; lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_VIDEO_VC_1, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); specific->data.unstructured = lsmash_create_vc1_specific_info( &info->dvc1_param, &specific->size ); if( !specific->data.unstructured || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); lsmash_destroy_codec_specific_data( specific ); return NULL; } summary->sample_type = ISOM_CODEC_TYPE_VC_1_VIDEO; summary->max_au_length = max_au_length; summary->timescale = sequence->framerate_numerator; summary->timebase = sequence->framerate_denominator; summary->vfr = !sequence->framerate_flag; summary->sample_per_field = 0; summary->width = sequence->disp_horiz_size; summary->height = sequence->disp_vert_size; summary->par_h = sequence->aspect_width; summary->par_v = sequence->aspect_height; summary->color.primaries_index = sequence->color_prim; summary->color.transfer_index = sequence->transfer_char; summary->color.matrix_index = sequence->matrix_coef; return summary; }
static int vc1_importer_probe( importer_t *importer ) { /* Find the first start code. */ vc1_importer_t *vc1_imp = create_vc1_importer( importer ); if( !vc1_imp ) return LSMASH_ERR_MEMORY_ALLOC; lsmash_bs_t *bs = importer->bs; uint64_t first_ebdu_head_pos = 0; int err; while( 1 ) { /* The first EBDU in decoding order of the stream shall have start code (0x000001). */ if( 0x000001 == lsmash_bs_show_be24( bs, first_ebdu_head_pos ) ) break; /* Invalid if encountered any value of non-zero before the first start code. */ if( lsmash_bs_show_byte( bs, first_ebdu_head_pos ) ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } ++first_ebdu_head_pos; } /* OK. It seems the stream has a sequence header of VC-1. */ importer->info = vc1_imp; vc1_info_t *info = &vc1_imp->info; lsmash_bs_read_seek( bs, first_ebdu_head_pos, SEEK_SET ); info->ebdu_head_pos = first_ebdu_head_pos; if( (err = vc1_analyze_whole_stream( importer )) < 0 ) goto fail; lsmash_video_summary_t *summary = vc1_create_summary( info, &vc1_imp->first_sequence, vc1_imp->max_au_length ); if( !summary ) { err = LSMASH_ERR_NAMELESS; goto fail; } if( lsmash_add_entry( importer->summaries, summary ) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); err = LSMASH_ERR_MEMORY_ALLOC; goto fail; } /* Go back to layer of the first EBDU. */ importer->status = IMPORTER_OK; lsmash_bs_read_seek( bs, first_ebdu_head_pos, SEEK_SET ); info->prev_bdu_type = 0xFF; /* 0xFF is a forbidden value. */ info->ebdu_head_pos = first_ebdu_head_pos; uint8_t *temp_access_unit = info->access_unit.data; uint8_t *temp_incomplete_access_unit = info->access_unit.incomplete_data; memset( &info->access_unit, 0, sizeof(vc1_access_unit_t) ); info->access_unit.data = temp_access_unit; info->access_unit.incomplete_data = temp_incomplete_access_unit; memset( &info->picture, 0, sizeof(vc1_picture_info_t) ); return 0; fail: remove_vc1_importer( vc1_imp ); importer->info = NULL; lsmash_remove_entries( importer->summaries, lsmash_cleanup_summary ); return err; }
static void remove_mp4_hnd( hnd_t handle ) { mp4_hnd_t *p_mp4 = handle; if( !p_mp4 ) return; lsmash_cleanup_summary( (lsmash_summary_t *)p_mp4->summary ); lsmash_close_file( &p_mp4->file_param ); lsmash_destroy_root( p_mp4->p_root ); free( p_mp4->p_sei_buffer ); free( p_mp4 ); }
static int wave_importer_probe( importer_t *importer ) { wave_importer_t *wave_imp = create_wave_importer( importer ); if( !wave_imp ) return LSMASH_ERR_MEMORY_ALLOC; int err = 0; uint32_t filesize; lsmash_bs_t *bs = importer->bs; if( lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'R', 'I', 'F', 'F' ) || ((filesize = lsmash_bs_get_le32( bs ) + 8) < WAVE_MIN_FILESIZE && filesize > 8) || lsmash_bs_get_be32( bs ) != LSMASH_4CC( 'W', 'A', 'V', 'E' ) ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } int fmt_chunk_present = 0; int data_chunk_present = 0; while( !bs->eob && !(fmt_chunk_present && data_chunk_present) ) { uint32_t ckID = lsmash_bs_get_be32( bs ); uint32_t ckSize = lsmash_bs_get_le32( bs ); lsmash_bs_reset_counter( bs ); switch( ckID ) { case LSMASH_4CC( 'f', 'm', 't', ' ' ) : if( ckSize < 16 ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } if( (err = wave_parse_fmt_chunk( wave_imp, bs )) < 0 ) goto fail; fmt_chunk_present = 1; break; case LSMASH_4CC( 'd', 'a', 't', 'a' ) : if( !fmt_chunk_present ) { /* The 'fmt ' chunk must be present before the 'data' chunk. */ err = LSMASH_ERR_INVALID_DATA; goto fail; } wave_imp->chunk.data_offset = lsmash_bs_get_stream_pos( bs ); wave_imp->chunk.length = ckSize; wave_imp->chunk.number = 1; wave_imp->chunk.file = importer->file; wave_imp->number_of_samples = ckSize / wave_imp->fmt.wfx.nBlockAlign; data_chunk_present = 1; break; default : break; } if( !data_chunk_present ) { /* Skip the rest of this chunk. * Note that ckData is word-aligned even if ckSize is an odd number. */ uint32_t skip_size = ckSize; if( skip_size & 1 ) skip_size++; if( skip_size > lsmash_bs_count( bs ) ) { skip_size -= lsmash_bs_count( bs ); lsmash_bs_read_seek( bs, skip_size, SEEK_CUR ); } } } if( !(fmt_chunk_present && data_chunk_present) ) { err = LSMASH_ERR_INVALID_DATA; goto fail; } /* Make fake movie. * Treat WAVE file format as if it's QuickTime file format. */ uint32_t track_ID; lsmash_movie_parameters_t movie_param = { 0 }; lsmash_track_parameters_t track_param = { 0 }; lsmash_media_parameters_t media_param = { 0 }; importer->file->qt_compatible = 1; if( (err = lsmash_importer_make_fake_movie( importer )) < 0 || (err = lsmash_importer_make_fake_track( importer, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, &track_ID )) < 0 || (err = lsmash_get_movie_parameters( importer->root, &movie_param )) < 0 || (err = lsmash_get_track_parameters( importer->root, track_ID, &track_param )) < 0 || (err = lsmash_get_media_parameters( importer->root, track_ID, &media_param )) < 0 ) goto fail; movie_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec; media_param.timescale = wave_imp->fmt.wfx.nSamplesPerSec; if( (err = lsmash_set_movie_parameters( importer->root, &movie_param )) < 0 || (err = lsmash_set_track_parameters( importer->root, track_ID, &track_param )) < 0 || (err = lsmash_set_media_parameters( importer->root, track_ID, &media_param )) < 0 ) goto fail; lsmash_audio_summary_t *summary = wave_create_summary( &wave_imp->fmt ); if( !summary || lsmash_add_sample_entry( importer->root, track_ID, summary ) != 1 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); err = LSMASH_ERR_NAMELESS; goto fail; } if( (err = lsmash_add_entry( importer->summaries, summary )) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); goto fail; } importer->info = wave_imp; importer->status = IMPORTER_OK; return 0; fail: lsmash_importer_break_fake_movie( importer ); remove_wave_importer( wave_imp ); importer->file->qt_compatible = 0; importer->info = NULL; return err; }
static lsmash_audio_summary_t *wave_create_summary( waveformat_extensible_t *fmt ) { lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); if( !summary ) return NULL; waveformat_extended_t *wfx = &fmt->wfx; summary->sample_type = QT_CODEC_TYPE_LPCM_AUDIO; summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; summary->frequency = wfx->nSamplesPerSec; summary->channels = wfx->nChannels; summary->sample_size = wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE ? fmt->Samples.wValidBitsPerSample : wfx->wBitsPerSample; summary->samples_in_frame = 1000; /* arbitrary */ summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; summary->bytes_per_frame = wfx->nBlockAlign * summary->samples_in_frame; summary->max_au_length = summary->bytes_per_frame; lsmash_codec_specific_t *cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_FORMAT_SPECIFIC_FLAGS, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); if( !cs ) goto fail; lsmash_qt_audio_format_specific_flags_t *lpcm = (lsmash_qt_audio_format_specific_flags_t *)cs->data.structured; if( (summary->sample_size & 7) == 0 ) lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_PACKED; else lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_ALIGNED_HIGH; if( summary->sample_size > 8 ) lpcm->format_flags |= QT_AUDIO_FORMAT_FLAG_SIGNED_INTEGER; if( lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) { lsmash_destroy_codec_specific_data( cs ); goto fail; } if( wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE || wfx->nChannels > 2 ) { cs = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_QT_AUDIO_CHANNEL_LAYOUT, LSMASH_CODEC_SPECIFIC_FORMAT_STRUCTURED ); if( !cs ) goto fail; lsmash_qt_audio_channel_layout_t *layout = (lsmash_qt_audio_channel_layout_t *)cs->data.structured; if( wfx->wFormatTag == WAVE_FORMAT_TYPE_ID_EXTENSIBLE ) { layout->channelLayoutTag = QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP; layout->channelBitmap = fmt->dwChannelMask; } else { layout->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN | wfx->nChannels; layout->channelBitmap = 0; } if( lsmash_add_entry( &summary->opaque->list, cs ) < 0 ) { lsmash_destroy_codec_specific_data( cs ); goto fail; } } return summary; fail: lsmash_cleanup_summary( (lsmash_summary_t *)summary ); return NULL; }
bool fcMP4Muxer::mux(const Params ¶ms) { lsmash_root_t *root; lsmash_file_parameters_t mp4_stream; lsmash_file_parameters_t h264_stream; lsmash_brand_type major_brand = ISOM_BRAND_TYPE_MP42; lsmash_brand_type compatible_brands[2] = { ISOM_BRAND_TYPE_MP42, ISOM_BRAND_TYPE_ISOM }; uint32_t fps_num = params.frame_rate; uint32_t fps_den = 1; // 出力 mp4 root = lsmash_create_root(); if (lsmash_open_file(params.out_mp4_path, 0, &mp4_stream) != 0) { return false; } mp4_stream.major_brand = major_brand; mp4_stream.brands = compatible_brands; mp4_stream.brand_count = sizeof(compatible_brands) / sizeof(compatible_brands[0]); mp4_stream.minor_version = 0; lsmash_set_file(root, &mp4_stream); lsmash_movie_parameters_t movie_param; lsmash_initialize_movie_parameters(&movie_param); lsmash_set_movie_parameters(root, &movie_param); int track_number = 1; MP4TrackData track_data[2]; int num_track_data = 0; if (params.in_h264_path) { importer_t *h264_importer = lsmash_importer_open(params.in_h264_path, "H.264"); if (h264_importer == nullptr) { return false; } int h264_track_id = lsmash_create_track(root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK); lsmash_track_parameters_t track_param; lsmash_initialize_track_parameters(&track_param); (int&)track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW; int sample_entry = 0; lsmash_summary_t *summary = lsmash_duplicate_summary(h264_importer, track_number); auto video_summary = (lsmash_video_summary_t*)summary; track_param.display_width = video_summary->width << 16; track_param.display_height = video_summary->height << 16; lsmash_set_track_parameters(root, h264_track_id, &track_param); sample_entry = lsmash_add_sample_entry(root, h264_track_id, summary); auto& td = track_data[num_track_data++]; td.importer = h264_importer; td.summary = summary; td.sample_entry = sample_entry; td.track_number = track_number; td.track_id = h264_track_id; td.timescale = video_summary->timescale; td.timebase = video_summary->timebase; lsmash_media_parameters_t media_param; lsmash_initialize_media_parameters(&media_param); media_param.timescale = td.timescale; lsmash_set_media_parameters(root, h264_track_id, &media_param); } if (params.in_aac_path) { importer_t *aac_importer = lsmash_importer_open(params.in_aac_path, "adts"); if (aac_importer == nullptr) { return false; } int aac_track_id = lsmash_create_track(root, ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK); lsmash_track_parameters_t track_param; lsmash_initialize_track_parameters(&track_param); (int&)track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW; int sample_entry = 0; lsmash_summary_t *summary = lsmash_duplicate_summary(aac_importer, track_number); auto audio_summary = (lsmash_audio_summary_t*)summary; lsmash_set_track_parameters(root, aac_track_id, &track_param); sample_entry = lsmash_add_sample_entry(root, aac_track_id, summary); auto& td = track_data[num_track_data++]; td.importer = aac_importer; td.summary = summary; td.sample_entry = sample_entry; td.track_number = track_number; td.track_id = aac_track_id; td.timescale = audio_summary->frequency; td.timebase = 1; lsmash_media_parameters_t media_param; lsmash_initialize_media_parameters(&media_param); media_param.timescale = td.timescale; lsmash_set_media_parameters(root, aac_track_id, &media_param); } double largest_dts = 0.0; uint32_t num_consecutive_sample_skip = 0; for (int ti = 0;;) { auto &td = track_data[ti]; if (!td.sample) { int ret = lsmash_importer_get_access_unit(td.importer, td.track_number, &td.sample); if (ret <= -1) // error { lsmash_delete_sample(td.sample); break; } else if (ret == 1) /* a change of stream's properties */ { lsmash_cleanup_summary(td.summary); td.summary = lsmash_duplicate_summary(td.importer, td.track_number); td.sample_entry = lsmash_add_sample_entry(root, td.track_id, td.summary); if (!td.sample_entry) { break; } } else if (ret == 2) /* EOF */ { lsmash_delete_sample(td.sample); break; } if (td.sample) { td.sample->index = td.sample_entry; td.sample->dts *= td.timebase; td.sample->cts *= td.timebase; td.dts = (double)td.sample->dts / td.timescale; } } if (td.sample) { if (td.dts <= largest_dts || num_consecutive_sample_skip == num_track_data) { uint64_t sample_size = td.sample->length; uint64_t sample_dts = td.sample->dts; uint64_t sample_cts = td.sample->cts; if (lsmash_append_sample(root, td.track_id, td.sample)) { return false; } td.prev_dts = sample_dts; td.sample = nullptr; largest_dts = std::max<double>(largest_dts, td.dts); num_consecutive_sample_skip = 0; } else { // skip ++num_consecutive_sample_skip; } } ti = (ti + 1) % num_track_data; } for (int i = 0; i < num_track_data; ++i) { lsmash_flush_pooled_samples(root, track_data[i].track_id, fps_den); lsmash_cleanup_summary(track_data[i].summary); lsmash_importer_close(track_data[i].importer); } lsmash_finish_movie(root, nullptr); lsmash_close_file(&mp4_stream); lsmash_destroy_root(root); return true; }
static lsmash_audio_summary_t *dts_create_summary( dts_info_t *info ) { lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO ); if( !summary ) return NULL; lsmash_dts_specific_parameters_t *param = &info->ddts_param; lsmash_codec_specific_t *specific = lsmash_create_codec_specific_data( LSMASH_CODEC_SPECIFIC_DATA_TYPE_ISOM_AUDIO_DTS, LSMASH_CODEC_SPECIFIC_FORMAT_UNSTRUCTURED ); specific->data.unstructured = lsmash_create_dts_specific_info( param, &specific->size ); if( !specific->data.unstructured || lsmash_add_entry( &summary->opaque->list, specific ) < 0 ) { lsmash_cleanup_summary( (lsmash_summary_t *)summary ); lsmash_destroy_codec_specific_data( specific ); return NULL; } /* The CODEC identifiers probably should not be the combination of 'mp4a' and * the objectTypeIndications for DTS audio since there is no public specification * which defines the encapsulation of the stream as the MPEG-4 Audio context yet. * In the world, there are muxers which is using such doubtful implementation. * The objectTypeIndications are registered at MP4RA, but this does not always * mean we can mux by using those objectTypeIndications. * If available, there shall be the specification which defines the existence of * DecoderSpecificInfo and its semantics, and what access unit consists of. */ summary->sample_type = lsmash_dts_get_codingname( param ); summary->aot = MP4A_AUDIO_OBJECT_TYPE_NULL; /* make no sense */ summary->sbr_mode = MP4A_AAC_SBR_NOT_SPECIFIED; /* make no sense */ switch( param->DTSSamplingFrequency ) { case 12000 : /* Invalid? (No reference in the spec) */ case 24000 : case 48000 : case 96000 : case 192000 : case 384000 : /* Invalid? (No reference in the spec) */ summary->frequency = 48000; break; case 22050 : case 44100 : case 88200 : case 176400 : case 352800 : /* Invalid? (No reference in the spec) */ summary->frequency = 44100; break; case 8000 : /* Invalid? (No reference in the spec) */ case 16000 : case 32000 : case 64000 : case 128000 : summary->frequency = 32000; break; default : summary->frequency = 0; break; } summary->samples_in_frame = (summary->frequency * info->frame_duration) / param->DTSSamplingFrequency; summary->max_au_length = DTS_MAX_CORE_SIZE + DTS_MAX_NUM_EXSS * DTS_MAX_EXSS_SIZE; summary->sample_size = param->pcmSampleDepth; summary->channels = dts_get_max_channel_count( info ); return summary; }