int main(int argc, char **argv) { /********************/ /* declarations */ /********************/ char *input, *output, tmpstr[GF_MAX_PATH]; GF_ISOFile *isom_file_in; GF_MediaImporter import; AdobeHDSCtx ctx; GF_Err e; u32 i; /*****************/ /* gpac init */ /*****************/ gf_sys_init(GF_MemTrackerNone); gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_WARNING); /***********************/ /* initialisations */ /***********************/ input = NULL; output = NULL; isom_file_in = NULL; memset(&import, 0, sizeof(GF_MediaImporter)); e = GF_OK; memset(&ctx, 0, sizeof(ctx)); ctx.curr_time = 0; ctx.segnum = 1; /*********************************************/ /* parse arguments and build HDS context */ /*********************************************/ if (GF_OK != parse_args(argc, argv, &input, &output, &ctx.curr_time, &ctx.segnum)) { usage(argv[0]); goto exit; } ctx.multirate_manifest = adobe_alloc_multirate_manifest(output); #if 0 /*'moov' conversion tests*/ { char metamoov64[GF_MAX_PATH]; u32 metamoov64_len; unsigned char metamoov[GF_MAX_PATH]; u32 metamoov_len=GF_MAX_PATH; FILE *f = gf_fopen("metamoov64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); metamoov64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(metamoov64, metamoov64_len, 1, f); metamoov_len = gf_base64_decode(metamoov64, metamoov64_len, metamoov, metamoov_len); gf_fclose(f); f = gf_fopen("metamoov", "wb"); fwrite(metamoov, metamoov_len, 1, f); gf_fclose(f); return 0; } #endif #if 0 /*'abst'conversion tests*/ { char bootstrap64[GF_MAX_PATH]; u32 bootstrap64_len; unsigned char bootstrap[GF_MAX_PATH]; u32 bootstrap_len=GF_MAX_PATH; GF_AdobeBootstrapInfoBox *abst = (GF_AdobeBootstrapInfoBox *)abst_New(); GF_BitStream *bs; #if 1 //64 FILE *f = gf_fopen("bootstrap64"/*input*/, "rt"); gf_fseek(f, 0, SEEK_END); bootstrap64_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap64, bootstrap64_len, 1, f); bootstrap_len = gf_base64_decode(bootstrap64, bootstrap64_len, bootstrap, bootstrap_len); #else //binary bootstrap FILE *f = gf_fopen("bootstrap.bin"/*input*/, "rb"); gf_fseek(f, 0, SEEK_END); bootstrap_len = (u32)gf_ftell(f); gf_fseek(f, 0, SEEK_SET); fread(bootstrap, bootstrap_len, 1, f); #endif bs = gf_bs_new(bootstrap+8, bootstrap_len-8, GF_BITSTREAM_READ); abst->size = bootstrap[2]*256+bootstrap[3]; assert(abst->size<GF_MAX_PATH); abst_Read((GF_Box*)abst, bs); gf_bs_del(bs); //then rewrite with just one 'afrt' memset(bootstrap, 0, bootstrap_len); bs = gf_bs_new(bootstrap, bootstrap_len, GF_BITSTREAM_WRITE); abst_Write((GF_Box*)abst, bs); bootstrap_len = (u32)gf_bs_get_position(bs); gf_bs_del(bs); gf_fclose(f); f = gf_fopen("bootstrap", "wt"); bootstrap64_len = gf_base64_encode(bootstrap, bootstrap_len, bootstrap64, GF_MAX_PATH); fwrite(bootstrap64, bootstrap64_len, 1, f); fprintf(f, "\n\n"); abst_dump((GF_Box*)abst, f); gf_fclose(f); abst_del((GF_Box*)abst); return 0; } #endif /*****************/ /* main loop */ /*****************/ import.trackID = 0; import.in_name = input; import.flags = GF_IMPORT_PROBE_ONLY; //create output or open when recovering from a saved state sprintf(tmpstr, "%s_import.mp4", input); isom_file_in = gf_isom_open(tmpstr, GF_ISOM_WRITE_EDIT, NULL); if (!isom_file_in) { fprintf(stderr, "Error opening output file %s: %s\n", tmpstr, gf_error_to_string(e)); assert(0); goto exit; } import.dest = isom_file_in; //probe input e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing input file %s: %s\n", input, gf_error_to_string(e)); assert(0); goto exit; } //import input data import.flags = 0; for (i=0; i<import.nb_tracks; i++) { import.trackID = import.tk_info[i].track_num; e = gf_media_import(&import); if (e) { fprintf(stderr, "Error while importing track number %u, input file %s: %s\n", import.trackID, input, gf_error_to_string(e)); assert(0); goto exit; } } //Adobe specific stuff e = adobize_segment(isom_file_in, &ctx); if (e) { fprintf(stderr, "Couldn't turn the ISOM fragmented file into an Adobe f4v segment: %s\n", gf_error_to_string(e)); assert(0); goto exit; } //interleave data and remove imported file //FIXME: set multiple fragments: sprintf(tmpstr, "%s_HD_100_Seg%u-Frag1", output, ctx.segnum); //FIXME: "HD", "100" and fragnum: pass as arg //e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0); e = gf_media_fragment_file(isom_file_in, tmpstr, 1.0+gf_isom_get_duration(isom_file_in)/gf_isom_get_timescale(isom_file_in)); if (e) { fprintf(stderr, "Error while fragmenting file to output %s: %s\n", output, gf_error_to_string(e)); assert(0); goto exit; } gf_isom_delete(isom_file_in); isom_file_in = NULL; e = adobe_gen_multirate_manifest(ctx.multirate_manifest, ctx.bootstrap, ctx.bootstrap_size); if (e) { fprintf(stderr, "Couldn't generate Adobe f4m manifest: %s\n", gf_error_to_string(e)); assert(0); goto exit; } exit: //delete intermediate mp4 file if (isom_file_in) gf_isom_delete(isom_file_in); if (ctx.multirate_manifest) adobe_free_multirate_manifest(ctx.multirate_manifest); if (ctx.bootstrap) { gf_free(ctx.bootstrap); //ctx.bootstrap = NULL; //ctx.bootstrap_size = 0; } gf_sys_close(); return !e ? 0 : 1; }
GF_Err adobize_segment(GF_ISOFile *isom_file, AdobeHDSCtx *ctx) { GF_Err e; GF_BitStream *bs; GF_AdobeFragRandomAccessBox *afra = (GF_AdobeFragRandomAccessBox*) afra_New(); GF_AfraEntry *ae = (GF_AfraEntry*) gf_calloc(1, sizeof(GF_AfraEntry)); GF_AdobeBootstrapInfoBox *abst = (GF_AdobeBootstrapInfoBox*) abst_New(); GF_AdobeSegmentRunTableBox *asrt = (GF_AdobeSegmentRunTableBox*) asrt_New(); GF_AdobeSegmentRunEntry *asre = (GF_AdobeSegmentRunEntry*) gf_calloc(1, sizeof(GF_AdobeSegmentRunEntry)); GF_AdobeFragmentRunTableBox *afrt = (GF_AdobeFragmentRunTableBox*) afrt_New(); GF_AdobeFragmentRunEntry *afre = (GF_AdobeFragmentRunEntry*) gf_calloc(1, sizeof(GF_AdobeFragmentRunEntry)); u64 init_seg_time = ctx->curr_time; u32 seg_duration = (u32)gf_isom_get_duration(isom_file); //update context ctx->curr_time += seg_duration; //Adobe specific boxes //Random Access afra->type = GF_4CC('a', 'f', 'r', 'a'); afra->version = 0; afra->flags = 0; afra->long_ids = 1; afra->long_offsets = 1; afra->global_entries = 0; afra->time_scale = gf_isom_get_timescale(isom_file); afra->entry_count = 1; ae->time = init_seg_time; ae->offset = 3999; gf_list_add(afra->local_access_entries, ae); afra->global_entries = 0; afra->global_entry_count = 0; e = gf_list_add(isom_file->TopBoxes, afra); if (e) { fprintf(stderr, "Impossible to write AFRA box: %s\n", gf_error_to_string(e)); assert(0); return e; } //Bootstrap Info abst->type = GF_4CC('a', 'b', 's', 't'); abst->version = 0; abst->flags = 0; abst->bootstrapinfo_version = 1; abst->profile = 0; abst->live = 1; abst->update = 0; abst->time_scale = gf_isom_get_timescale(isom_file); abst->current_media_time = init_seg_time+seg_duration; abst->smpte_time_code_offset = 0; abst->movie_identifier = NULL; abst->drm_data = NULL; abst->meta_data = NULL; abst->server_entry_count = 0; abst->quality_entry_count = 0; abst->segment_run_table_count = 1; { //Segment Run asrt->type = GF_4CC('a', 's', 'r', 't'); asrt->version = 0; asrt->flags = 0; asrt->segment_run_entry_count = 1; { asre->first_segment = ctx->segnum; asre->fragment_per_segment = 1; } e = gf_list_add(asrt->segment_run_entry_table, asre); if (e) { fprintf(stderr, "Impossible to write ASR Entry: %s\n", gf_error_to_string(e)); assert(0); return e; } } e = gf_list_add(abst->segment_run_table_entries, asrt); if (e) { fprintf(stderr, "Impossible to write ASRT box: %s\n", gf_error_to_string(e)); assert(0); return e; } abst->fragment_run_table_count = 1; { //Fragment Run afrt->type = GF_4CC('a', 'f', 'r', 't'); afrt->version = 0; afrt->flags = 0; afrt->timescale = gf_isom_get_timescale(isom_file); afrt->fragment_run_entry_count = 1; { afre->first_fragment = 1; afre->first_fragment_timestamp = 0; afre->fragment_duration = seg_duration; } e = gf_list_add(afrt->fragment_run_entry_table, afre); if (e) { fprintf(stderr, "Impossible to write AFR Entry: %s\n", gf_error_to_string(e)); assert(0); return e; } } e = gf_list_add(abst->fragment_run_table_entries, afrt); if (e) { fprintf(stderr, "Impossible to write AFRT box: %s\n", gf_error_to_string(e)); assert(0); return e; } e = gf_list_add(isom_file->TopBoxes, abst); if (e) { fprintf(stderr, "Impossible to write ABST box: %s\n", gf_error_to_string(e)); assert(0); return e; } e = abst_Size((GF_Box*)abst); if (e) { fprintf(stderr, "Impossible to compute ABST box size: %s\n", gf_error_to_string(e)); assert(0); return e; } ctx->bootstrap_size = (size_t)abst->size; ctx->bootstrap = gf_malloc(ctx->bootstrap_size); bs = gf_bs_new(ctx->bootstrap, ctx->bootstrap_size, GF_BITSTREAM_WRITE); e = abst_Write((GF_Box*)abst, bs); if (e) { fprintf(stderr, "Impossible to code the ABST box: %s\n", gf_error_to_string(e)); assert(0); gf_bs_del(bs); return e; } gf_bs_del(bs); //set brands as reversed engineered from f4v files /*e = gf_isom_reset_alt_brands(isom_file); if (e) { fprintf(stderr, "Warning: couldn't reset ISOM brands: %s\n", gf_error_to_string(e)); assert(0); }*/ gf_isom_set_brand_info(isom_file, GF_4CC('f','4','v',' '), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('i','s','o','m'), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('m','p','4','2'), 1); gf_isom_modify_alternate_brand(isom_file, GF_4CC('m','4','v',' '), 1); return GF_OK; }