void LSMASHVideoSource::get_video_track( const char *source, uint32_t track_number, int threads, IScriptEnvironment *env )
{
    uint32_t number_of_tracks = open_file( source, env );
    if( track_number && track_number > number_of_tracks )
        env->ThrowError( "LSMASHVideoSource: the number of tracks equals %I32u.", number_of_tracks );
    /* L-SMASH */
    uint32_t i;
    lsmash_media_parameters_t media_param;
    if( track_number == 0 )
    {
        /* Get the first video track. */
        for( i = 1; i <= number_of_tracks; i++ )
        {
            vdh.track_ID = lsmash_get_track_ID( vdh.root, i );
            if( vdh.track_ID == 0 )
                env->ThrowError( "LSMASHVideoSource: failed to find video track." );
            lsmash_initialize_media_parameters( &media_param );
            if( lsmash_get_media_parameters( vdh.root, vdh.track_ID, &media_param ) )
                env->ThrowError( "LSMASHVideoSource: failed to get media parameters." );
            if( media_param.handler_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
                break;
        }
        if( i > number_of_tracks )
            env->ThrowError( "LSMASHVideoSource: failed to find video track." );
    }
    else
    {
        /* Get the desired video track. */
        vdh.track_ID = lsmash_get_track_ID( vdh.root, track_number );
        if( vdh.track_ID == 0 )
            env->ThrowError( "LSMASHVideoSource: failed to find video track." );
        lsmash_initialize_media_parameters( &media_param );
        if( lsmash_get_media_parameters( vdh.root, vdh.track_ID, &media_param ) )
            env->ThrowError( "LSMASHVideoSource: failed to get media parameters." );
        if( media_param.handler_type != ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
            env->ThrowError( "LSMASHVideoSource: the track you specified is not a video track." );
    }
    if( lsmash_construct_timeline( vdh.root, vdh.track_ID ) )
        env->ThrowError( "LSMASHVideoSource: failed to get construct timeline." );
    if( get_summaries( vdh.root, vdh.track_ID, &vdh.config ) )
        env->ThrowError( "LSMASHVideoSource: failed to get summaries." );
    vi.num_frames = lsmash_get_sample_count_in_media_timeline( vdh.root, vdh.track_ID );
    setup_timestamp_info( &vdh, &vi, media_param.timescale, env );
    /* libavformat */
    for( i = 0; i < format_ctx->nb_streams && format_ctx->streams[i]->codec->codec_type != AVMEDIA_TYPE_VIDEO; i++ );
    if( i == format_ctx->nb_streams )
        env->ThrowError( "LSMASHVideoSource: failed to find stream by libavformat." );
    /* libavcodec */
    AVStream *stream = format_ctx->streams[i];
    AVCodecContext *ctx = stream->codec;
    vdh.config.ctx = ctx;
    AVCodec *codec = avcodec_find_decoder( ctx->codec_id );
    if( !codec )
        env->ThrowError( "LSMASHVideoSource: failed to find %s decoder.", codec->name );
    ctx->thread_count = threads;
    if( avcodec_open2( ctx, codec, NULL ) < 0 )
        env->ThrowError( "LSMASHVideoSource: failed to avcodec_open2." );
}
static uint32_t libavsmash_video_fetch_media_timescale
(
    libavsmash_video_decode_handler_t *vdhp
)
{
    if( !vdhp )
        return 0;
    lsmash_media_parameters_t media_param;
    lsmash_initialize_media_parameters( &media_param );
    if( lsmash_get_media_parameters( vdhp->root, vdhp->track_id, &media_param ) < 0 )
        return 0;
    vdhp->media_timescale = media_param.timescale;
    return vdhp->media_timescale;
}
int main( int argc, char *argv[] )
{
    if ( argc < 2 )
    {
        display_help();
        return -1;
    }
    else if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
    {
        display_help();
        return 0;
    }
    else if( !strcasecmp( argv[1], "-v" ) || !strcasecmp( argv[1], "--version" ) )
    {
        display_version();
        return 0;
    }
    int dump_box = 1;
    int chapter = 0;
    char *filename;
    lsmash_get_mainargs( &argc, &argv );
    if( argc > 2 )
    {
        if( !strcasecmp( argv[1], "--box" ) )
            DO_NOTHING;
        else if( !strcasecmp( argv[1], "--chapter" ) )
            chapter = 1;
        else if( !strcasecmp( argv[1], "--timestamp" ) )
            dump_box = 0;
        else
        {
            display_help();
            return -1;
        }
        filename = argv[2];
    }
    else
    {
        filename = argv[1];
    }
    /* Open the input file. */
    lsmash_root_t *root = lsmash_create_root();
    if( !root )
    {
        fprintf( stderr, "Failed to create a ROOT.\n" );
        return -1;
    }
    lsmash_file_parameters_t file_param = { 0 };
    if( lsmash_open_file( filename, 1, &file_param ) < 0 )
        return BOXDUMPER_ERR( "Failed to open an input file.\n" );
    if( dump_box )
        file_param.mode |= LSMASH_FILE_MODE_DUMP;
    lsmash_file_t *file = lsmash_set_file( root, &file_param );
    if( !file )
        return BOXDUMPER_ERR( "Failed to add a file into a ROOT.\n" );
    if( lsmash_read_file( file, &file_param ) < 0 )
        return BOXDUMPER_ERR( "Failed to read a file\n" );
    /* Dump the input file. */
    if( chapter )
    {
        if( lsmash_print_chapter_list( root ) )
            return BOXDUMPER_ERR( "Failed to extract chapter.\n" );
    }
    else if( dump_box )
    {
        if( lsmash_print_movie( root, "-" ) )
            return BOXDUMPER_ERR( "Failed to dump box structure.\n" );
    }
    else
    {
        lsmash_movie_parameters_t movie_param;
        lsmash_initialize_movie_parameters( &movie_param );
        lsmash_get_movie_parameters( root, &movie_param );
        uint32_t num_tracks = movie_param.number_of_tracks;
        for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ )
        {
            uint32_t track_ID = lsmash_get_track_ID( root, track_number );
            if( !track_ID )
                return BOXDUMPER_ERR( "Failed to get track_ID.\n" );
            lsmash_media_parameters_t media_param;
            lsmash_initialize_media_parameters( &media_param );
            if( lsmash_get_media_parameters( root, track_ID, &media_param ) )
                return BOXDUMPER_ERR( "Failed to get media parameters.\n" );
            if( lsmash_construct_timeline( root, track_ID ) )
                return BOXDUMPER_ERR( "Failed to construct timeline.\n" );
            uint32_t timeline_shift;
            if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) )
                return BOXDUMPER_ERR( "Failed to get timestamps.\n" );
            lsmash_media_ts_list_t ts_list;
            if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) )
                return BOXDUMPER_ERR( "Failed to get timestamps.\n" );
            fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID );
            fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale );
            lsmash_media_ts_t *ts_array = ts_list.timestamp;
            if( !ts_array )
            {
                fprintf( stdout, "\n" );
                continue;
            }
            for( uint32_t i = 0; i < ts_list.sample_count; i++ )
                fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift );
            lsmash_free( ts_array );
            fprintf( stdout, "\n" );
        }
    }
    lsmash_destroy_root( root );
    return 0;
}
Exemple #4
0
static int set_param( hnd_t handle, x264_param_t *p_param )
{
    mp4_hnd_t *p_mp4 = handle;
    uint64_t i_media_timescale;

    p_mp4->i_delay_frames = p_param->i_bframe ? (p_param->i_bframe_pyramid ? 2 : 1) : 0;
    p_mp4->i_dts_compress_multiplier = p_mp4->b_dts_compress * p_mp4->i_delay_frames + 1;

    i_media_timescale = (uint64_t)p_param->i_timebase_den * p_mp4->i_dts_compress_multiplier;
    p_mp4->i_time_inc = (uint64_t)p_param->i_timebase_num * p_mp4->i_dts_compress_multiplier;
    MP4_FAIL_IF_ERR( i_media_timescale > UINT32_MAX, "MP4 media timescale %"PRIu64" exceeds maximum\n", i_media_timescale );

    /* Select brands. */
    lsmash_brand_type brands[6] = { 0 };
    uint32_t brand_count = 0;
    brands[brand_count++] = ISOM_BRAND_TYPE_MP42;
    brands[brand_count++] = ISOM_BRAND_TYPE_MP41;
    brands[brand_count++] = ISOM_BRAND_TYPE_ISOM;
    if( p_mp4->b_use_recovery )
    {
        brands[brand_count++] = ISOM_BRAND_TYPE_AVC1;   /* sdtp, sgpd, sbgp and visual roll recovery grouping */
        if( p_param->b_open_gop )
            brands[brand_count++] = ISOM_BRAND_TYPE_ISO6;   /* cslg and visual random access grouping */
    }

    /* Set file */
    lsmash_file_parameters_t *file_param = &p_mp4->file_param;
    file_param->major_brand   = brands[0];
    file_param->brands        = brands;
    file_param->brand_count   = brand_count;
    file_param->minor_version = 0;
    MP4_FAIL_IF_ERR( !lsmash_set_file( p_mp4->p_root, file_param ), "failed to add an output file into a ROOT.\n" );

    /* Set movie parameters. */
    lsmash_movie_parameters_t movie_param;
    lsmash_initialize_movie_parameters( &movie_param );
    MP4_FAIL_IF_ERR( lsmash_set_movie_parameters( p_mp4->p_root, &movie_param ),
                     "failed to set movie parameters.\n" );
    p_mp4->i_movie_timescale = lsmash_get_movie_timescale( p_mp4->p_root );
    MP4_FAIL_IF_ERR( !p_mp4->i_movie_timescale, "movie timescale is broken.\n" );

    /* Create a video track. */
    p_mp4->i_track = lsmash_create_track( p_mp4->p_root, ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK );
    MP4_FAIL_IF_ERR( !p_mp4->i_track, "failed to create a video track.\n" );

    p_mp4->summary->width = p_param->i_width;
    p_mp4->summary->height = p_param->i_height;
    uint32_t i_display_width = p_param->i_width << 16;
    uint32_t i_display_height = p_param->i_height << 16;
    if( p_param->vui.i_sar_width && p_param->vui.i_sar_height )
    {
        double sar = (double)p_param->vui.i_sar_width / p_param->vui.i_sar_height;
        if( sar > 1.0 )
            i_display_width *= sar;
        else
            i_display_height /= sar;
        p_mp4->summary->par_h = p_param->vui.i_sar_width;
        p_mp4->summary->par_v = p_param->vui.i_sar_height;
    }
    p_mp4->summary->color.primaries_index = p_param->vui.i_colorprim;
    p_mp4->summary->color.transfer_index  = p_param->vui.i_transfer;
    p_mp4->summary->color.matrix_index    = p_param->vui.i_colmatrix >= 0 ? p_param->vui.i_colmatrix : ISOM_MATRIX_INDEX_UNSPECIFIED;
    p_mp4->summary->color.full_range      = p_param->vui.b_fullrange >= 0 ? p_param->vui.b_fullrange : 0;

    /* Set video track parameters. */
    lsmash_track_parameters_t track_param;
    lsmash_initialize_track_parameters( &track_param );
    lsmash_track_mode track_mode = ISOM_TRACK_ENABLED | ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
    track_param.mode = track_mode;
    track_param.display_width = i_display_width;
    track_param.display_height = i_display_height;
    MP4_FAIL_IF_ERR( lsmash_set_track_parameters( p_mp4->p_root, p_mp4->i_track, &track_param ),
                     "failed to set track parameters for video.\n" );

    /* Set video media parameters. */
    lsmash_media_parameters_t media_param;
    lsmash_initialize_media_parameters( &media_param );
    media_param.timescale = i_media_timescale;
    media_param.media_handler_name = "L-SMASH Video Media Handler";
    if( p_mp4->b_use_recovery )
    {
        media_param.roll_grouping = p_param->b_intra_refresh;
        media_param.rap_grouping = p_param->b_open_gop;
    }
    MP4_FAIL_IF_ERR( lsmash_set_media_parameters( p_mp4->p_root, p_mp4->i_track, &media_param ),
                     "failed to set media parameters for video.\n" );
    p_mp4->i_video_timescale = lsmash_get_media_timescale( p_mp4->p_root, p_mp4->i_track );
    MP4_FAIL_IF_ERR( !p_mp4->i_video_timescale, "media timescale for video is broken.\n" );

    return 0;
}
void LSMASHAudioSource::get_audio_track( const char *source, uint32_t track_number, bool skip_priming, IScriptEnvironment *env )
{
    uint32_t number_of_tracks = open_file( source, env );
    if( track_number && track_number > number_of_tracks )
        env->ThrowError( "LSMASHAudioSource: the number of tracks equals %I32u.", number_of_tracks );
    /* L-SMASH */
    uint32_t i;
    lsmash_media_parameters_t media_param;
    if( track_number == 0 )
    {
        /* Get the first audio track. */
        for( i = 1; i <= number_of_tracks; i++ )
        {
            adh.track_ID = lsmash_get_track_ID( adh.root, i );
            if( adh.track_ID == 0 )
                env->ThrowError( "LSMASHAudioSource: failed to find audio track." );
            lsmash_initialize_media_parameters( &media_param );
            if( lsmash_get_media_parameters( adh.root, adh.track_ID, &media_param ) )
                env->ThrowError( "LSMASHAudioSource: failed to get media parameters." );
            if( media_param.handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
                break;
        }
        if( i > number_of_tracks )
            env->ThrowError( "LSMASHAudioSource: failed to find audio track." );
    }
    else
    {
        /* Get the desired audio track. */
        adh.track_ID = lsmash_get_track_ID( adh.root, track_number );
        if( adh.track_ID == 0 )
            env->ThrowError( "LSMASHAudioSource: failed to find audio track." );
        lsmash_initialize_media_parameters( &media_param );
        if( lsmash_get_media_parameters( adh.root, adh.track_ID, &media_param ) )
            env->ThrowError( "LSMASHAudioSource: failed to get media parameters." );
        if( media_param.handler_type != ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
            env->ThrowError( "LSMASHAudioSource: the track you specified is not an audio track." );
    }
    if( lsmash_construct_timeline( adh.root, adh.track_ID ) )
        env->ThrowError( "LSMASHAudioSource: failed to get construct timeline." );
    if( get_summaries( adh.root, adh.track_ID, &adh.config ) )
        env->ThrowError( "LSMASHAudioSource: failed to get summaries." );
    adh.frame_count = lsmash_get_sample_count_in_media_timeline( adh.root, adh.track_ID );
    vi.num_audio_samples = lsmash_get_media_duration_from_media_timeline( adh.root, adh.track_ID );
    if( skip_priming )
    {
        uint32_t itunes_metadata_count = lsmash_count_itunes_metadata( adh.root );
        for( i = 1; i <= itunes_metadata_count; i++ )
        {
            lsmash_itunes_metadata_t metadata;
            if( lsmash_get_itunes_metadata( adh.root, i, &metadata ) < 0 )
                continue;
            if( metadata.item != ITUNES_METADATA_ITEM_CUSTOM
             || (metadata.type != ITUNES_METADATA_TYPE_STRING && metadata.type != ITUNES_METADATA_TYPE_BINARY)
             || !metadata.meaning || !metadata.name
             || memcmp( "com.apple.iTunes", metadata.meaning, strlen( metadata.meaning ) )
             || memcmp( "iTunSMPB", metadata.name, strlen( metadata.name ) ) )
            {
                lsmash_cleanup_itunes_metadata( &metadata );
                continue;
            }
            char *value = NULL;
            if( metadata.type == ITUNES_METADATA_TYPE_STRING )
            {
                int length = strlen( metadata.value.string );
                if( length >= 116 )
                    value = duplicate_as_string( metadata.value.string, length );
            }
            else    /* metadata.type == ITUNES_METADATA_TYPE_BINARY */
            {
                if( metadata.value.binary.size >= 116 )
                    value = duplicate_as_string( metadata.value.binary.data, metadata.value.binary.size );
            }
            lsmash_cleanup_itunes_metadata( &metadata );
            if( !value )
                continue;
            uint32_t dummy[9];
            uint32_t priming_samples;
            uint32_t padding;
            uint64_t duration;
            if( 12 != sscanf( value, " %I32x %I32x %I32x %I64x %I32x %I32x %I32x %I32x %I32x %I32x %I32x %I32x",
                              &dummy[0], &priming_samples, &padding, &duration, &dummy[1], &dummy[2],
                              &dummy[3], &dummy[4], &dummy[5], &dummy[6], &dummy[7], &dummy[8] ) )
            {
                delete [] value;
                continue;
            }
            delete [] value;
            adh.implicit_preroll     = 1;
            aoh.skip_decoded_samples = priming_samples;
            vi.num_audio_samples = duration + priming_samples;
            break;
        }
        if( aoh.skip_decoded_samples == 0 )
        {
            uint32_t ctd_shift;
            if( lsmash_get_composition_to_decode_shift_from_media_timeline( adh.root, adh.track_ID, &ctd_shift ) )
                env->ThrowError( "LSMASHAudioSource: failed to get the timeline shift." );
            aoh.skip_decoded_samples = ctd_shift + get_start_time( adh.root, adh.track_ID );
        }
    }
    /* libavformat */
    for( i = 0; i < format_ctx->nb_streams && format_ctx->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO; i++ );
    if( i == format_ctx->nb_streams )
        env->ThrowError( "LSMASHAudioSource: failed to find stream by libavformat." );
    /* libavcodec */
    AVStream *stream = format_ctx->streams[i];
    AVCodecContext *ctx = stream->codec;
    adh.config.ctx = ctx;
    AVCodec *codec = avcodec_find_decoder( ctx->codec_id );
    if( !codec )
        env->ThrowError( "LSMASHAudioSource: failed to find %s decoder.", codec->name );
    ctx->thread_count = 0;
    if( avcodec_open2( ctx, codec, NULL ) < 0 )
        env->ThrowError( "LSMASHAudioSource: failed to avcodec_open2." );
}
bool fcMP4Muxer::mux(const Params &params)
{
    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;
}
Exemple #7
0
int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name )
{
    if( isom_check_initializer_present( root ) < 0 )
        goto error_message;
    lsmash_file_t *file = root->file;
    if( !file
     || !file->moov
     || !file->moov->mvhd )
        goto error_message;
    if( file->forbid_tref || (!file->qt_compatible && !file->itunes_movie) )
    {
        lsmash_log( NULL, LSMASH_LOG_ERROR, "reference chapter is not available for this file.\n" );
        goto error_message;
    }
    FILE *chapter = NULL;       /* shut up 'uninitialized' warning */
    /* Create a Track Reference Box. */
    isom_trak_t *trak = isom_get_trak( file, track_ID );
    if( !trak )
    {
        lsmash_log( NULL, LSMASH_LOG_ERROR, "the specified track ID to apply the chapter doesn't exist.\n" );
        goto error_message;
    }
    if( !trak->tref && !isom_add_tref( trak ) )
        goto error_message;
    /* Create a track_ID for a new chapter track. */
    uint32_t *id = (uint32_t *)lsmash_malloc( sizeof(uint32_t) );
    if( !id )
        goto error_message;
    uint32_t chapter_track_ID = *id = file->moov->mvhd->next_track_ID;
    /* Create a Track Reference Type Box. */
    isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP );
    if( !chap )
    {
        lsmash_free( id );
        goto error_message;
    }
    chap->ref_count = 1;
    chap->track_ID  = id;
    /* Create a reference chapter track. */
    if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) )
        goto error_message;
    /* Set track parameters. */
    lsmash_track_parameters_t track_param;
    lsmash_initialize_track_parameters( &track_param );
    track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
    if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) < 0 )
        goto fail;
    /* Set media parameters. */
    uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID );
    if( !media_timescale )
        goto fail;
    lsmash_media_parameters_t media_param;
    lsmash_initialize_media_parameters( &media_param );
    media_param.timescale    = media_timescale;
    media_param.ISO_language = file->max_3gpp_version >= 6 || file->itunes_movie ? ISOM_LANGUAGE_CODE_UNDEFINED : 0;
    media_param.MAC_language = 0;
    if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) < 0 )
        goto fail;
    /* Create a sample description. */
    lsmash_codec_type_t sample_type = file->max_3gpp_version >= 6 || file->itunes_movie
                                    ? ISOM_CODEC_TYPE_TX3G_TEXT
                                    : QT_CODEC_TYPE_TEXT_TEXT;
    lsmash_summary_t summary = { .sample_type = sample_type, .data_ref_index = 1 };
    uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, &summary );
    if( !sample_entry )
        goto fail;
    /* Check each line format. */
    fn_get_chapter_data fnc = isom_check_chap_line( file_name );
    if( !fnc )
        goto fail;
    /* Open chapter format file. */
    chapter = lsmash_fopen( file_name, "rb" );
    if( !chapter )
    {
        lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name );
        goto fail;
    }
    /* Parse the file and write text samples. */
    isom_chapter_entry_t data;
    while( !fnc( chapter, &data ) )
    {
        /* set start_time */
        data.start_time = data.start_time * 1e-9 * media_timescale + 0.5;
        /* write a text sample here */
        int is_qt_text = lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TEXT_TEXT );
        uint16_t name_length = strlen( data.chapter_name );
        lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * is_qt_text );
        if( !sample )
        {
            lsmash_free( data.chapter_name );
            goto fail;
        }
        sample->data[0] = (name_length >> 8) & 0xff;
        sample->data[1] =  name_length       & 0xff;
        memcpy( sample->data + 2, data.chapter_name, name_length );
        if( is_qt_text )
        {
            /* QuickTime Player requires Text Encoding Attribute Box ('encd') if media language is ISO language codes : undefined.
             * Also this box can avoid garbling if the QuickTime text sample is encoded by Unicode characters.
             * Note: 3GPP Timed Text supports only UTF-8 or UTF-16, so this box isn't needed. */
            static const uint8_t encd[12] =
                {
                    0x00, 0x00, 0x00, 0x0C,     /* size: 12 */
                    0x65, 0x6E, 0x63, 0x64,     /* type: 'encd' */
                    0x00, 0x00, 0x01, 0x00      /* Unicode Encoding */
                };
            memcpy( sample->data + 2 + name_length, encd, 12 );
        }
        sample->dts           = data.start_time;
        sample->cts           = data.start_time;
        sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
        sample->index         = sample_entry;
        if( lsmash_append_sample( root, chapter_track_ID, sample ) < 0 )
        {
            lsmash_free( data.chapter_name );
            goto fail;
        }
        lsmash_freep( &data.chapter_name );
    }
    if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) < 0 )
        goto fail;
    isom_trak_t *chapter_trak = isom_get_trak( file, chapter_track_ID );
    if( !chapter_trak )
        goto fail;
    fclose( chapter );
    chapter_trak->is_chapter       = 1;
    chapter_trak->related_track_ID = track_ID;
    return 0;
fail:
    if( chapter )
        fclose( chapter );
    /* Remove chapter track reference. */
    if( trak->tref->ref_list.tail )
        isom_remove_box_by_itself( trak->tref->ref_list.tail->data );
    if( trak->tref->ref_list.entry_count == 0 )
        isom_remove_box_by_itself( trak->tref );
    /* Remove the reference chapter track attached at tail of the list. */
    if( file->moov->trak_list.tail )
        isom_remove_box_by_itself( file->moov->trak_list.tail->data );
error_message:
    lsmash_log( NULL, LSMASH_LOG_ERROR, "failed to set reference chapter.\n" );
    return LSMASH_ERR_NAMELESS;
}

uint32_t lsmash_count_tyrant_chapter( lsmash_root_t *root )
{
    if( isom_check_initializer_present( root ) < 0
     && root->file->initializer->moov
     && root->file->initializer->moov->udta
     && root->file->initializer->moov->udta->chpl
     && root->file->initializer->moov->udta->chpl->list )
        return root->file->initializer->moov->udta->chpl->list->entry_count;
    return 0;
}

char *lsmash_get_tyrant_chapter( lsmash_root_t *root, uint32_t index, double *timestamp )
{
    if( isom_check_initializer_present( root ) < 0 )
        return NULL;
    lsmash_file_t *file = root->file->initializer;
    if( !file->moov
     || !file->moov->mvhd
     || !file->moov->udta
     || !file->moov->udta->chpl )
        return NULL;
    isom_chpl_t *chpl = file->moov->udta->chpl;
    isom_chpl_entry_t *data = (isom_chpl_entry_t *)lsmash_get_entry_data( chpl->list, index );
    if( !data )
        return NULL;
    double timescale = chpl->version ? 10000000.0 : file->moov->mvhd->timescale;
    *timestamp = data->start_time / timescale;
    if( !memcmp( data->chapter_name, UTF8_BOM, UTF8_BOM_LENGTH ) )
        return data->chapter_name + UTF8_BOM_LENGTH;
    return data->chapter_name;
}


int lsmash_print_chapter_list( lsmash_root_t *root )
{
    if( isom_check_initializer_present( root ) < 0
     || !(root->file->initializer->flags & LSMASH_FILE_MODE_READ) )
        return LSMASH_ERR_FUNCTION_PARAM;
    lsmash_file_t *file = root->file->initializer;
    if( file->moov
     && file->moov->udta
     && file->moov->udta->chpl )
    {
        isom_chpl_t *chpl = file->moov->udta->chpl;
        uint32_t timescale;
        if( !chpl->version )
        {
            if( !file->moov
             && !file->moov->mvhd )
                return LSMASH_ERR_NAMELESS;
            timescale = file->moov->mvhd->timescale;
        }
        else
            timescale = 10000000;
        uint32_t i = 1;
        for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
        {
            isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
            int64_t start_time = data->start_time / timescale;
            int hh =  start_time / 3600;
            int mm = (start_time /   60) % 60;
            int ss =  start_time         % 60;
            int ms = ((data->start_time / (double)timescale) - hh * 3600 - mm * 60 - ss) * 1e3 + 0.5;
            if( !memcmp( data->chapter_name, UTF8_BOM, UTF8_BOM_LENGTH ) )    /* detect BOM */
            {
                data->chapter_name += UTF8_BOM_LENGTH;
#ifdef _WIN32
                if( i == 1 )
                    printf( UTF8_BOM );    /* add BOM on Windows */
#endif
            }
            printf( "CHAPTER%02"PRIu32"=%02d:%02d:%02d.%03d\n", i, hh, mm, ss, ms );
            printf( "CHAPTER%02"PRIu32"NAME=%s\n", i++, data->chapter_name );
        }
        return 0;
    }
    lsmash_log( NULL, LSMASH_LOG_ERROR, "this file doesn't have a chapter list.\n" );
    return LSMASH_ERR_NAMELESS;
}
Exemple #8
0
int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name )
{
    if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list )
        goto error_message;
    if( !root->qt_compatible && !root->itunes_movie )
    {
        lsmash_log( LSMASH_LOG_ERROR, "reference chapter is not available for this file.\n" );
        goto error_message;
    }
    FILE *chapter = NULL;       /* shut up 'uninitialized' warning */
    /* Create a Track Reference Box. */
    isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
    if( !trak )
    {
        lsmash_log( LSMASH_LOG_ERROR, "the specified track ID to apply the chapter doesn't exist.\n" );
        goto error_message;
    }
    if( isom_add_tref( trak ) )
        goto error_message;
    /* Create a track_ID for a new chapter track. */
    uint32_t *id = (uint32_t *)malloc( sizeof(uint32_t) );
    if( !id )
        goto error_message;
    uint32_t chapter_track_ID = *id = root->moov->mvhd->next_track_ID;
    /* Create a Track Reference Type Box. */
    isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP, 1, id );
    if( !chap )
        goto error_message;      /* no need to free id */
    /* Create a reference chapter track. */
    if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) )
        goto error_message;
    /* Set track parameters. */
    lsmash_track_parameters_t track_param;
    lsmash_initialize_track_parameters( &track_param );
    track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
    if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) )
        goto fail;
    /* Set media parameters. */
    uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID );
    if( !media_timescale )
        goto fail;
    lsmash_media_parameters_t media_param;
    lsmash_initialize_media_parameters( &media_param );
    media_param.timescale = media_timescale;
    media_param.ISO_language = root->max_3gpp_version >= 6 || root->itunes_movie ? ISOM_LANGUAGE_CODE_UNDEFINED : 0;
    media_param.MAC_language = 0;
    if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) )
        goto fail;
    /* Create a sample description. */
    lsmash_codec_type_t sample_type = root->max_3gpp_version >= 6 || root->itunes_movie
                                    ? ISOM_CODEC_TYPE_TX3G_TEXT
                                    : QT_CODEC_TYPE_TEXT_TEXT;
    lsmash_summary_t summary = { .sample_type = sample_type };
    uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, &summary );
    if( !sample_entry )
        goto fail;
    /* Check each line format. */
    fn_get_chapter_data fnc = isom_check_chap_line( file_name );
    if( !fnc )
        goto fail;
    /* Open chapter format file. */
    chapter = fopen( file_name, "rb" );
    if( !chapter )
    {
        lsmash_log( LSMASH_LOG_ERROR, "failed to open the chapter file \"%s\".\n", file_name );
        goto fail;
    }
    /* Parse the file and write text samples. */
    isom_chapter_entry_t data;
    while( !fnc( chapter, &data ) )
    {
        /* set start_time */
        data.start_time = data.start_time * 1e-9 * media_timescale + 0.5;
        /* write a text sample here */
        int is_qt_text = lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_TEXT_TEXT );
        uint16_t name_length = strlen( data.chapter_name );
        lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * is_qt_text );
        if( !sample )
        {
            free( data.chapter_name );
            goto fail;
        }
        sample->data[0] = (name_length >> 8) & 0xff;
        sample->data[1] =  name_length       & 0xff;
        memcpy( sample->data + 2, data.chapter_name, name_length );
        if( is_qt_text )
        {
            /* QuickTime Player requires Text Encoding Attribute Box ('encd') if media language is ISO language codes : undefined.
             * Also this box can avoid garbling if the QuickTime text sample is encoded by Unicode characters.
             * Note: 3GPP Timed Text supports only UTF-8 or UTF-16, so this box isn't needed. */
            static const uint8_t encd[12] =
                {
                    0x00, 0x00, 0x00, 0x0C,     /* size: 12 */
                    0x65, 0x6E, 0x63, 0x64,     /* type: 'encd' */
                    0x00, 0x00, 0x01, 0x00      /* Unicode Encoding */
                };
            memcpy( sample->data + 2 + name_length, encd, 12 );
        }
        sample->dts = sample->cts = data.start_time;
        sample->prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
        sample->index = sample_entry;
        if( lsmash_append_sample( root, chapter_track_ID, sample ) )
        {
            free( data.chapter_name );
            goto fail;
        }
        free( data.chapter_name );
        data.chapter_name = NULL;
    }
    if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) )
        goto fail;
    isom_trak_entry_t *chapter_trak = isom_get_trak( root, chapter_track_ID );
    if( !chapter_trak )
        goto fail;
    fclose( chapter );
    chapter_trak->is_chapter = 1;
    chapter_trak->related_track_ID = track_ID;
    return 0;
fail:
    if( chapter )
        fclose( chapter );
    /* Remove chapter track reference. */
    lsmash_remove_entry_direct( trak->tref->ref_list, trak->tref->ref_list->tail, isom_remove_track_reference_type );
    if( trak->tref->ref_list->entry_count == 0 )
        isom_remove_tref( trak->tref );
    /* Remove the reference chapter track attached at tail of the list. */
    lsmash_remove_entry_direct( root->moov->trak_list, root->moov->trak_list->tail, isom_remove_trak );
error_message:
    lsmash_log( LSMASH_LOG_ERROR, "failed to set reference chapter.\n" );
    return -1;
}
int main( int argc, char *argv[] )
{
    if( argc < 2 )
        return print_help( -1 );
    int dump_box = 1;
    int chapter = 0;
    char *filename;
    if( argc > 2 )
    {
        if( !strcasecmp( argv[1], "--box" ) )
            DO_NOTHING;
        else if( !strcasecmp( argv[1], "--chapter" ) )
            chapter = 1;
        else if( !strcasecmp( argv[1], "--timestamp" ) )
            dump_box = 0;
        else
            return print_help( -1 );
        filename = argv[2];
    }
    else
    {
        if( !strcasecmp( argv[1], "-h" ) || !strcasecmp( argv[1], "--help" ) )
            return print_help( 0 );
        filename = argv[1];
    }
#ifdef _WIN32
    _setmode( _fileno(stdin), _O_BINARY );
#endif
    lsmash_file_mode mode = LSMASH_FILE_MODE_READ;
    if( dump_box )
        mode |= LSMASH_FILE_MODE_DUMP;
    lsmash_root_t *root = lsmash_open_movie( filename, mode );
    if( !root )
    {
        fprintf( stderr, "Failed to open input file.\n" );
        return -1;
    }
    if( chapter )
    {
        if( lsmash_print_chapter_list( root ) )
            return BOXDUMPER_ERR( "Failed to extract chapter.\n" );
    }
    else if( dump_box )
    {
        if( lsmash_print_movie( root, "-" ) )
            return BOXDUMPER_ERR( "Failed to dump box structure.\n" );
    }
    else
    {
        lsmash_movie_parameters_t movie_param;
        lsmash_initialize_movie_parameters( &movie_param );
        lsmash_get_movie_parameters( root, &movie_param );
        uint32_t num_tracks = movie_param.number_of_tracks;
        for( uint32_t track_number = 1; track_number <= num_tracks; track_number++ )
        {
            uint32_t track_ID = lsmash_get_track_ID( root, track_number );
            if( !track_ID )
                return BOXDUMPER_ERR( "Failed to get track_ID.\n" );
            lsmash_media_parameters_t media_param;
            lsmash_initialize_media_parameters( &media_param );
            if( lsmash_get_media_parameters( root, track_ID, &media_param ) )
                return BOXDUMPER_ERR( "Failed to get media parameters.\n" );
            if( lsmash_construct_timeline( root, track_ID ) )
                return BOXDUMPER_ERR( "Failed to construct timeline.\n" );
            uint32_t timeline_shift;
            if( lsmash_get_composition_to_decode_shift_from_media_timeline( root, track_ID, &timeline_shift ) )
                return BOXDUMPER_ERR( "Failed to get timestamps.\n" );
            lsmash_media_ts_list_t ts_list;
            if( lsmash_get_media_timestamps( root, track_ID, &ts_list ) )
                return BOXDUMPER_ERR( "Failed to get timestamps.\n" );
            fprintf( stdout, "track_ID: %"PRIu32"\n", track_ID );
            fprintf( stdout, "Media timescale: %"PRIu32"\n", media_param.timescale );
            lsmash_media_ts_t *ts_array = ts_list.timestamp;
            if( !ts_array )
            {
                fprintf( stdout, "\n" );
                continue;
            }
            for( uint32_t i = 0; i < ts_list.sample_count; i++ )
                fprintf( stdout, "DTS = %"PRIu64", CTS = %"PRIu64"\n", ts_array[i].dts, ts_array[i].cts + timeline_shift );
            free( ts_array );
            fprintf( stdout, "\n" );
        }
    }
    lsmash_destroy_root( root );
    return 0;
}