diff --git a/src/ccextractor.c b/src/ccextractor.c index f15f4a5a..6a580dfc 100644 --- a/src/ccextractor.c +++ b/src/ccextractor.c @@ -243,7 +243,14 @@ int main(int argc, char *argv[]) { fatal (EXIT_INCOMPATIBLE_PARAMETERS, "MP4 requires an actual file, it's not possible to read from a stream, including stdin.\n"); } - tmp = processmp4(ctx, &ctx->mp4_cfg, ctx->inputfile[ctx->current_file]); + if(ccx_options.extract_chapters) + { + tmp = dumpchapters(ctx, &ctx->mp4_cfg, ctx->inputfile[ctx->current_file]); + } + else + { + tmp = processmp4(ctx, &ctx->mp4_cfg, ctx->inputfile[ctx->current_file]); + } if (ccx_options.print_file_reports) print_file_report(ctx); if (!ret) ret = tmp; diff --git a/src/gpacmp4/mp4.c b/src/gpacmp4/mp4.c index 0dd596e6..506c7724 100644 --- a/src/gpacmp4/mp4.c +++ b/src/gpacmp4/mp4.c @@ -190,6 +190,39 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I return status; } +static char *format_duration(u64 dur, u32 timescale, char *szDur) +{ + u32 h, m, s, ms; + if ((dur==(u64) -1) || (dur==(u32) -1)) { + strcpy(szDur, "Unknown"); + return szDur; + } + dur = (u64) (( ((Double) (s64) dur)/timescale)*1000); + h = (u32) (dur / 3600000); + m = (u32) (dur/ 60000) - h*60; + s = (u32) (dur/1000) - h*3600 - m*60; + ms = (u32) (dur) - h*3600000 - m*60000 - s*1000; + if (h<=24) { + sprintf(szDur, "%02d:%02d:%02d.%03d", h, m, s, ms); + } else { + u32 d = (u32) (dur / 3600000 / 24); + h = (u32) (dur/3600000)-24*d; + if (d<=365) { + sprintf(szDur, "%d Days, %02d:%02d:%02d.%03d", d, h, m, s, ms); + } else { + u32 y=0; + while (d>365) { + y++; + d-=365; + if (y%4) d--; + } + sprintf(szDur, "%d Years %d Days, %02d:%02d:%02d.%03d", y, d, h, m, s, ms); + } + + } + return szDur; +} + unsigned char * ccdp_find_data(unsigned char * ccdp_atom_content, unsigned int len, unsigned int *cc_count) { unsigned char *data = ccdp_atom_content; @@ -286,7 +319,7 @@ unsigned char * ccdp_find_data(unsigned char * ccdp_atom_content, unsigned int l */ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) { - int caps = 0; + int mp4_ret = 0; GF_ISOFile* f; u32 i, j, track_count, avc_track_count, cc_track_count; struct cc_subtitle dec_sub; @@ -296,7 +329,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) dec_ctx = update_decoder_list(ctx); memset(&dec_sub,0,sizeof(dec_sub)); - mprint("opening \'%s\': ", file); + mprint("Opening \'%s\': ", file); #ifdef MP4_DEBUG gf_log_set_tool_level(GF_LOG_CONTAINER,GF_LOG_DEBUG); #endif @@ -330,7 +363,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) avc_track_count++; } - mprint("mp4: found %u tracks: %u avc and %u cc\n", track_count, avc_track_count, cc_track_count); + mprint("MP4: found %u tracks: %u avc and %u cc\n", track_count, avc_track_count, cc_track_count); for(i = 0; i < track_count; i++) { @@ -349,7 +382,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) } if(dec_sub.got_output) { - caps = 1; + mp4_ret = 1; encode_sub(enc_ctx, &dec_sub); dec_sub.got_output = 0; } @@ -377,7 +410,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) } if(dec_sub.got_output) { - caps = 1; + mp4_ret = 1; encode_sub(enc_ctx, &dec_sub); dec_sub.got_output = 0; } @@ -472,14 +505,14 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) if (cc_info == CDP_SECTION_SVC_INFO || cc_info == CDP_SECTION_FOOTER) { - dbg_print(CCX_DMT_PARSE, "mp4-708: premature end of sample (0x73 or 0x74)\n"); + dbg_print(CCX_DMT_PARSE, "MP4-708: premature end of sample (0x73 or 0x74)\n"); break; } if ((cc_info == 0xFA || cc_info == 0xFC || cc_info == 0xFD) && (cc_data[1] & 0x7F) == 0 && (cc_data[2] & 0x7F) == 0) { - dbg_print(CCX_DMT_PARSE, "mp4-708: skipped (zero cc data)\n"); + dbg_print(CCX_DMT_PARSE, "MP4-708: skipped (zero cc data)\n"); continue; } @@ -490,7 +523,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) if (cc_type < 2) { - dbg_print(CCX_DMT_PARSE, "mp4-708: atom skipped (cc_type < 2)\n"); + dbg_print(CCX_DMT_PARSE, "MP4-708: atom skipped (cc_type < 2)\n"); continue; } dec_ctx->dtvcc->encoder = (void *)enc_ctx; //WARN: otherwise cea-708 will not work @@ -522,7 +555,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) tdata += ret; cb_field1++; if (dec_sub.got_output) { - caps = 1; + mp4_ret = 1; encode_sub(enc_ctx, &dec_sub); dec_sub.got_output = 0; } @@ -550,7 +583,7 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) free(dec_ctx->xds_ctx); - mprint("\nclosing media: "); + mprint("\nClosing media: "); gf_isom_close(f); f = NULL; @@ -567,9 +600,66 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) if (cc_track_count) mprint ("Found %d CC track(s).\n", cc_track_count); else - mprint ("found no dedicated CC track(s).\n"); + mprint ("Found no dedicated CC track(s).\n"); ctx->freport.mp4_cc_track_cnt = cc_track_count; - return caps; + return mp4_ret; +} + +int dumpchapters (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file) +{ + int mp4_ret = 0; + GF_ISOFile* f; + mprint("Opening \'%s\': ", file); +#ifdef MP4_DEBUG + gf_log_set_tool_level(GF_LOG_CONTAINER,GF_LOG_DEBUG); +#endif + + if((f = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL)) == NULL) + { + mprint("failed to open\n"); + return 5; + } + + mprint("ok\n"); + + char szName[1024]; + FILE *t; + u32 i, count; + count = gf_isom_get_chapter_count(f, 0); + if(count>0) + { + if (file) + { + strcpy(szName, get_basename(file)); + strcat(szName, ".txt"); + + t = gf_fopen(szName, "wt"); + if (!t) return 5; + } + else { + t = stdout; + } + mp4_ret=1; + printf("Writing chapters into %s\n",szName); + } + else + { + mprint("No chapters information found!\n"); + } + + + + for (i=0; iauto_myth = 2; // 2=auto /* MP4 related stuff */ options->mp4vidtrack=0; // Process the video track even if a CC dedicated track exists. + options->extract_chapters=0; // By default don't extract chapters. /* General stuff */ options->usepicorder = 0; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams options->xmltv=0; // 1 = full output. 2 = live output. 3 = both diff --git a/src/lib_ccx/ccx_common_option.h b/src/lib_ccx/ccx_common_option.h index a8533902..156b4491 100644 --- a/src/lib_ccx/ccx_common_option.h +++ b/src/lib_ccx/ccx_common_option.h @@ -115,6 +115,7 @@ struct ccx_s_options // Options from user parameters int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto /* MP4 related stuff */ unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists. + int extract_chapters; // If 1, extracts chapters (if present), from MP4 files. /* General settings */ int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams int xmltv; // 1 = full output. 2 = live output. 3 = both diff --git a/src/lib_ccx/ccx_decoders_708_output.c b/src/lib_ccx/ccx_decoders_708_output.c index f887e85b..addfcd4a 100644 --- a/src/lib_ccx/ccx_decoders_708_output.c +++ b/src/lib_ccx/ccx_decoders_708_output.c @@ -279,7 +279,7 @@ void ccx_dtvcc_write_transcript(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_ if (strlen(buf)) write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf)); - _dtvcc_write_row(writer, tv, i, encoder, 0); + _dtvcc_write_row(writer, decoder, i, encoder, 0); write(encoder->dtvcc_writers[tv->service_number - 1].fd, encoder->encoded_crlf, encoder->encoded_crlf_length); } diff --git a/src/lib_ccx/ccx_mp4.h b/src/lib_ccx/ccx_mp4.h index 6e5fe887..84eb5a8e 100644 --- a/src/lib_ccx/ccx_mp4.h +++ b/src/lib_ccx/ccx_mp4.h @@ -4,4 +4,5 @@ int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file); +int dumpchapters(struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file); #endif diff --git a/src/lib_ccx/params.c b/src/lib_ccx/params.c index fd60f0ab..2b5a8d27 100644 --- a/src/lib_ccx/params.c +++ b/src/lib_ccx/params.c @@ -490,6 +490,9 @@ void print_usage (void) mprint (" --webvtt-create-css: Create a separate file for CSS instead of inline.\n"); mprint ("\n"); mprint ("Options that affect what kind of output will be produced:\n"); + mprint (" -chapters: (Experimental) Produces a chapter file from MP4 files.\n"); + mprint (" Note that this must only be used with MP4 files,\n"); + mprint (" for other files it will simply generate subtitles file.\n"); mprint (" -bom: Append a BOM (Byte Order Mark) to output files.\n"); mprint (" Note that most text processing tools in linux will not\n"); mprint (" like BOM.\n"); @@ -1199,6 +1202,11 @@ int parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]) } } #endif + + if (strcmp(argv[i], "-chapters") == 0){ + opt->extract_chapters= 1; + continue; + } if (strcmp (argv[i],"-bi")==0 || strcmp (argv[i],"--bufferinput")==0) @@ -2195,6 +2203,13 @@ int parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]) { fatal (EXIT_INCOMPATIBLE_PARAMETERS, "MP4 requires an actual file, it's not possible to read from a stream, including stdin.\n"); } + + if(opt->extract_chapters) + { + mprint("Request to extract chapters recieved.\n"); + mprint("Note that this must only be used with MP4 files,\n"); + mprint("for other files it will simply generate subtitles file.\n\n"); + } if(opt->gui_mode_reports) {