diff --git a/src/gpacmp4/mp4.c b/src/gpacmp4/mp4.c index c9be6273..121188a5 100644 --- a/src/gpacmp4/mp4.c +++ b/src/gpacmp4/mp4.c @@ -3,6 +3,7 @@ #include #include +#include #include "lib_ccx.h" #include "utility.h" #include "ccx_encoders_common.h" @@ -185,6 +186,87 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I return status; } +unsigned char * ccdp_extract_data(unsigned char * ccdp_atom_content, unsigned int len, unsigned int *cc_count) +{ + unsigned char *data = ccdp_atom_content; + + if (len < 4) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected size of cdp\n"); + return NULL; + } + + unsigned int cdp_id = (data[0] << 8) | data[1]; + if (cdp_id != 0x9669) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected header %hhX %hhX\n", data[0], data[1]); + return NULL; + } + + data += 2; + len -= 2; + + unsigned int cdp_data_count = data[0]; + unsigned int cdp_frame_rate = data[1] >> 4; //frequency could be calculated + if (cdp_data_count != len + 2) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected data length %u %u\n", cdp_data_count, len + 2); + return NULL; + } + + data += 2; + len -= 2; + + unsigned int cdp_flags = data[0]; + unsigned int cdp_counter = (data[1] << 8) | data[2]; + + data += 3; + len -= 3; + + unsigned int cdp_timecode_added = (cdp_flags & 0x80) >> 7; + unsigned int cdp_data_added = (cdp_flags & 0x40) >> 6; + + if (!cdp_data_added) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: packet without data\n"); + return NULL; + } + + if (cdp_timecode_added) + { + data += 4; + len -= 4; + } + + if (data[0] != CDP_DATA_SECTION) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: cdp_data_section byte not found\n"); + return NULL; + } + + *cc_count = (unsigned int) (data[1] & 0x1F); + + if (*cc_count != 10 && *cc_count != 20 && *cc_count != 25 && *cc_count != 30) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected cc_count %u\n", cc_count); + return NULL; + } + + data += 2; + len -= 2; + + if ((*cc_count) * 3 > len) + { + dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: not enough bytes left (%u) to carry %u*3 bytes\n", len, cc_count); + return NULL; + } + + (void)(cdp_counter); + (void)(cdp_frame_rate); + + return data; +} + /* Here is application algorithm described in some C-like pseudo code: main(){ @@ -231,17 +313,20 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx) (unsigned char) ((type>>16)%0x100),(unsigned char) ((type>>8)%0x100),(unsigned char) (type%0x100), (unsigned char) (subtype>>24%0x100), (unsigned char) ((subtype>>16)%0x100),(unsigned char) ((subtype>>8)%0x100),(unsigned char) (subtype%0x100)); - if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) - cc_track_count++; - if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264) + if ((type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) || + (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C708)) + cc_track_count++; + if (type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264) avc_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++) { const u32 type = gf_isom_get_media_type(f, i + 1); const u32 subtype = gf_isom_get_media_subtype(f, i + 1, 1); - + if ( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_XDVB) { if (cc_track_count && !ccx_options.mp4vidtrack) @@ -285,7 +370,8 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx) } - if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) + if ((type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) || + (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C708)) { if (avc_track_count && ccx_options.mp4vidtrack) continue; @@ -294,7 +380,7 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx) unsigned num_streams = gf_isom_get_sample_description_count (f,i+1); #endif unsigned num_samples = gf_isom_get_sample_count (f,i+1); - + u32 ProcessingStreamDescriptionIndex = 0; // Current track we are processing, 0 = we don't know yet u32 timescale = gf_isom_get_media_timescale(f,i+1); #ifdef MP$DEBUG @@ -306,7 +392,7 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx) #endif for (unsigned k = 0; k data + atomStart; unsigned int atomLength = RB32(data); - if(atomLength < 8 || atomLength > sample->dataLength) + if (atomLength < 8 || atomLength > sample->dataLength) { - mprint ("Invalid atom.\n"); + mprint ("Invalid atom length. Atom length: %u, should be: %u\n", atomLength, sample->dataLength); break; } - - data += 4; - if (!strncmp(data, "cdat", 4) || !strncmp(data, "cdt2", 4)) - { - int ret = 0; - int len = atomLength - 8; - data += 4; #ifdef MP4_DEBUG - dump(256, (unsigned char *)data, atomLength - 8, 0, 1); + dump(256, (unsigned char *)data, atomLength - 8, 0, 1); #endif - do + data += 4; + int is_ccdp = !strncmp(data, "ccdp", 4); + + if (!strncmp(data, "cdat", 4) || !strncmp(data, "cdt2", 4) || is_ccdp) + { + if (subtype == GF_ISOM_SUBTYPE_C708) { - ret = process608((unsigned char*)data, len, ctx->dec_ctx->context_cc608_field_1, &dec_sub); - len -= ret; - data += ret; - if(dec_sub.got_output) + if (!is_ccdp) { - encode_sub(enc_ctx, &dec_sub); - dec_sub.got_output = 0; + mprint("Your video file seems to be an interesting sample for us\n"); + mprint("We haven't met c708 subtitle not in a \'ccdp\' atom before\n"); + mprint("Please, report\n"); + break; } - } while (len > 0); + + unsigned int cc_count; + data += 4; + unsigned char *cc_data = ccdp_extract_data((unsigned char *) data, sample->dataLength - 8, &cc_count); + + if (!cc_data) + { + dbg_print(CCX_DMT_PARSE, "mp4-708: no cc data found in ccdp\n"); + break; + } + + do_cea708 = 1; + unsigned char temp[4]; + for (int cc_i = 0; cc_i < cc_count; cc_i++, cc_data += 3) + { + unsigned char cc_info = cc_data[0]; + unsigned char cc_valid = (unsigned char) ((cc_info & 4) >> 2); + unsigned char cc_type = (unsigned char) (cc_info & 3); + + if (cc_info == CDP_SVC_INFO_SECTION || cc_info == CDP_FOOTER_SECTION) + { + 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"); + continue; + } + + temp[0] = cc_valid; + temp[1] = cc_type; + temp[2] = cc_data[1]; + temp[3] = cc_data[2]; + + if (cc_type < 2) + { + dbg_print(CCX_DMT_PARSE, "mp4-708: atom skipped (cc_type < 2)\n"); + continue; + } + do_708(ctx->dec_ctx, (unsigned char *) temp, 4); + cb_708++; + } + atomStart = sample->dataLength; + } + else //subtype == GF_ISOM_SUBTYPE_C608 + { + if (is_ccdp) + { + mprint("Your video file seems to be an interesting sample for us\n"); + mprint("We haven't met c608 subtitle in a \'ccdp\' atom before\n"); + mprint("Please, report\n"); + break; + } + + int ret = 0; + int len = atomLength - 8; + data += 4; + + do { + ret = process608((unsigned char *) data, len, ctx->dec_ctx->context_cc608_field_1, + &dec_sub); + len -= ret; + data += ret; + if (dec_sub.got_output) { + encode_sub(enc_ctx, &dec_sub); + dec_sub.got_output = 0; + } + } while (len > 0); + } } atomStart += atomLength; - } // End of change