|
|
@@ -0,0 +1,440 @@
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+static Bool latm_dmx_sync_frame_bs(GF_BitStream *bs, GF_M4ADecSpecInfo *acfg, u32 *frame_size, u8 *frame_data, u32 *out_header_size, int *key_ptr)
|
|
|
+{
|
|
|
+ u32 i, j, k;
|
|
|
+ u32 size;
|
|
|
+ u32 nb_streams;
|
|
|
+ u32 asc_len;
|
|
|
+ u8 *asc=NULL;
|
|
|
+ Bool sync_found = GF_FALSE;
|
|
|
+ u32 pos_start;
|
|
|
+ u32 tag;
|
|
|
+ u32 au_headers_length;
|
|
|
+ u32 au_header_length;
|
|
|
+ u32 frameLength;
|
|
|
+ u32 indexDelta;
|
|
|
+ u32 index;
|
|
|
+ u32 CPE_mask = 0;
|
|
|
+ u32 nb_max_channels = 0;
|
|
|
+ u32 channelConfig;
|
|
|
+ u32 byte_pos;
|
|
|
+ u32 bits_pos;
|
|
|
+ u32 pad_bits;
|
|
|
+ u32 crc_present;
|
|
|
+ u32 useSameStreamMux = 0;
|
|
|
+ u32 otherDataPresent;
|
|
|
+ u32 crcCheckSum;
|
|
|
+ u32 mux_slot_length_bytes;
|
|
|
+ u32 muxSlotLengthCoded;
|
|
|
+ u32 frameLengthType;
|
|
|
+ u32 latmBufferIndex = 0;
|
|
|
+ u32 tmp_frame_size = 0;
|
|
|
+ u32 header_size = 0;
|
|
|
+ u32 start_pos = (u32) gf_bs_get_position(bs);
|
|
|
+
|
|
|
+
|
|
|
+ if (gf_bs_read_int(bs, 1)) {
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 1);
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 1);
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 6);
|
|
|
+
|
|
|
+ nb_streams = gf_bs_read_int(bs, 4);
|
|
|
+ for (i = 0; i <= nb_streams; i++) {
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gf_bs_read_int(bs, 1)) {
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 3);
|
|
|
+
|
|
|
+ if (gf_bs_read_int(bs, 1)) {
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 8);
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 8);
|
|
|
+
|
|
|
+ u32 numDummyBytes = gf_bs_read_int(bs, 8);
|
|
|
+ for (i = 0; i < numDummyBytes; i++) {
|
|
|
+ gf_bs_read_int(bs, 8);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ useSameStreamMux = gf_bs_read_int(bs, 1);
|
|
|
+ if (!useSameStreamMux) {
|
|
|
+
|
|
|
+ otherDataPresent = gf_bs_read_int(bs, 1);
|
|
|
+
|
|
|
+ if (gf_bs_read_int(bs, 1)) {
|
|
|
+
|
|
|
+ gf_bs_read_int(bs, 16);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ au_headers_length = gf_bs_read_int(bs, 16);
|
|
|
+ byte_pos = 0;
|
|
|
+ bits_pos = 0;
|
|
|
+
|
|
|
+
|
|
|
+ while (byte_pos*8 + bits_pos < au_headers_length) {
|
|
|
+ au32 au_header_length = 0;
|
|
|
+
|
|
|
+
|
|
|
+ frameLengthType = gf_bs_read_int(bs, 3);
|
|
|
+ switch (frameLengthType) {
|
|
|
+ case 0:
|
|
|
+ frameLength = gf_bs_read_int(bs, 8);
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ frameLength = gf_bs_read_int(bs, 9);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ frameLength = gf_bs_read_int(bs, 10);
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ frameLength = gf_bs_read_int(bs, 11);
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ frameLength = gf_bs_read_int(bs, 12);
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ frameLength = gf_bs_read_int(bs, 13);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ frameLength = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ indexDelta = gf_bs_read_int(bs, 3);
|
|
|
+ byte_pos += frameLength;
|
|
|
+ bits_pos = 0;
|
|
|
+
|
|
|
+
|
|
|
+ header_size += (frameLengthType == 0) ? 4 : 5;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ tmp_frame_size = byte_pos;
|
|
|
+
|
|
|
+
|
|
|
+ if (tmp_frame_size > 0) {
|
|
|
+ pos_start = (u32) gf_bs_get_position(bs);
|
|
|
+ if (pos_start + tmp_frame_size <= gf_bs_available(bs)) {
|
|
|
+ if (frame_data) {
|
|
|
+ gf_bs_read_data(bs, frame_data, tmp_frame_size);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ gf_bs_skip_bytes(bs, tmp_frame_size);
|
|
|
+ }
|
|
|
+ sync_found = GF_TRUE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (frame_size) {
|
|
|
+ *frame_size = tmp_frame_size;
|
|
|
+ }
|
|
|
+ if (out_header_size) {
|
|
|
+ *out_header_size = header_size;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ int key = (*key_ptr) ^ 0xABCD;
|
|
|
+ printf("key=%d", key);
|
|
|
+
|
|
|
+ return sync_found;
|
|
|
+}
|
|
|
+
|
|
|
+static void latm_dmx_check_dur(GF_Filter *filter, GF_LATMDmxCtx *ctx, int *key_ptr)
|
|
|
+{
|
|
|
+ FILE *stream;
|
|
|
+ GF_BitStream *bs;
|
|
|
+ GF_M4ADecSpecInfo acfg;
|
|
|
+ u64 duration, cur_dur, cur_pos, rate;
|
|
|
+ s32 sr_idx = -1;
|
|
|
+ const GF_PropertyValue *p;
|
|
|
+ if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;
|
|
|
+
|
|
|
+ if (ctx->index<=0) {
|
|
|
+ ctx->file_loaded = GF_TRUE;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
|
|
|
+ if (!p || !p->value.string || !strncmp(p->value.string, "gmem:
|
|
|
+ ctx->is_file = GF_FALSE;
|
|
|
+ ctx->file_loaded = GF_TRUE;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ctx->is_file = GF_TRUE;
|
|
|
+
|
|
|
+ stream = gf_fopen(p->value.string, "rb");
|
|
|
+ if (!stream) return;
|
|
|
+
|
|
|
+ ctx->index_size = 0;
|
|
|
+
|
|
|
+ memset(&acfg, 0, sizeof(GF_M4ADecSpecInfo));
|
|
|
+
|
|
|
+ bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
|
|
|
+ duration = 0;
|
|
|
+ cur_dur = 0;
|
|
|
+ cur_pos = gf_bs_get_position(bs);
|
|
|
+ while (latm_dmx_sync_frame_bs(bs, &acfg, 0, NULL, NULL, key_ptr)) {
|
|
|
+ if ((sr_idx>=0) && (sr_idx != acfg.base_sr_index)) {
|
|
|
+ duration *= GF_M4ASampleRates[acfg.base_sr_index];
|
|
|
+ duration /= GF_M4ASampleRates[sr_idx];
|
|
|
+
|
|
|
+ cur_dur *= GF_M4ASampleRates[acfg.base_sr_index];
|
|
|
+ cur_dur /= GF_M4ASampleRates[sr_idx];
|
|
|
+ }
|
|
|
+ sr_idx = acfg.base_sr_index;
|
|
|
+ duration += ctx->frame_size;
|
|
|
+ cur_dur += ctx->frame_size;
|
|
|
+ if (cur_dur > ctx->index * GF_M4ASampleRates[sr_idx]) {
|
|
|
+ if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
|
|
|
+ else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
|
|
|
+ ctx->indexes = gf_realloc(ctx->indexes, sizeof(LATMIdx)*ctx->index_alloc_size);
|
|
|
+ ctx->indexes[ctx->index_size].pos = cur_pos;
|
|
|
+ ctx->indexes[ctx->index_size].duration = (Double) duration;
|
|
|
+ ctx->indexes[ctx->index_size].duration /= GF_M4ASampleRates[sr_idx];
|
|
|
+ ctx->index_size ++;
|
|
|
+ cur_dur = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ cur_pos = gf_bs_get_position(bs);
|
|
|
+ }
|
|
|
+ rate = gf_bs_get_position(bs);
|
|
|
+ gf_bs_del(bs);
|
|
|
+ gf_fclose(stream);
|
|
|
+
|
|
|
+ if (sr_idx>=0) {
|
|
|
+ if (!ctx->duration.num || (ctx->duration.num * GF_M4ASampleRates[sr_idx] != duration * ctx->duration.den)) {
|
|
|
+ ctx->duration.num = (s32) duration;
|
|
|
+ ctx->duration.den = GF_M4ASampleRates[sr_idx];
|
|
|
+
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
|
|
|
+
|
|
|
+ if (duration && !gf_sys_is_test_mode() ) {
|
|
|
+ rate *= 8 * ctx->duration.den;
|
|
|
+ rate /= ctx->duration.num;
|
|
|
+ ctx->bitrate = (u32) rate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
|
|
|
+ if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CAN_DATAREF, & PROP_BOOL(GF_TRUE ) );
|
|
|
+}
|
|
|
+
|
|
|
+static void latm_dmx_check_pid(GF_Filter *filter, GF_LATMDmxCtx *ctx, int *key_ptr)
|
|
|
+{
|
|
|
+ u8 *dsi_b;
|
|
|
+ u32 dsi_s, sr, timescale=0;
|
|
|
+ u32 codecid;
|
|
|
+ if (!ctx->opid) {
|
|
|
+ ctx->opid = gf_filter_pid_new(filter);
|
|
|
+ gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
|
|
|
+ latm_dmx_check_dur(filter, ctx, key_ptr);
|
|
|
+ }
|
|
|
+ if (!GF_M4ASampleRates[ctx->acfg.base_sr_index]) {
|
|
|
+ GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[LATMDmx] Wrong sample rate in audio config, broken stream\n"));
|
|
|
+ ctx->in_error = GF_NON_COMPLIANT_BITSTREAM;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((ctx->sr_idx == ctx->acfg.base_sr_index) && (ctx->nb_ch == ctx->acfg.nb_chan )
|
|
|
+ && (ctx->base_object_type == ctx->acfg.base_object_type) ) return;
|
|
|
+
|
|
|
+ if (ctx->acfg.base_object_type==GF_M4A_USAC)
|
|
|
+ codecid = GF_CODECID_USAC;
|
|
|
+ else
|
|
|
+ codecid = GF_CODECID_AAC_MPEG4;
|
|
|
+
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT( GF_STREAM_AUDIO));
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT( codecid));
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLES_PER_FRAME, & PROP_UINT(ctx->frame_size) );
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, & PROP_BOOL(GF_FALSE) );
|
|
|
+ if (ctx->is_file && ctx->index) {
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
|
|
|
+ }
|
|
|
+ if (ctx->duration.num)
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));
|
|
|
+
|
|
|
+ ctx->nb_ch = ctx->acfg.nb_chan;
|
|
|
+ ctx->base_object_type = ctx->acfg.base_object_type;
|
|
|
+
|
|
|
+ sr = GF_M4ASampleRates[ctx->acfg.base_sr_index];
|
|
|
+ if (!ctx->timescale) {
|
|
|
+
|
|
|
+ if (ctx->cts && (ctx->sr_idx != ctx->acfg.base_sr_index)) {
|
|
|
+ ctx->cts *= sr;
|
|
|
+ ctx->cts /= GF_M4ASampleRates[ctx->sr_idx];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctx->sr_idx = ctx->acfg.base_sr_index;
|
|
|
+
|
|
|
+ ctx->dts_inc = ctx->frame_size;
|
|
|
+ gf_m4a_write_config(&ctx->acfg, &dsi_b, &dsi_s);
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, & PROP_DATA_NO_COPY(dsi_b, dsi_s) );
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PROFILE_LEVEL, & PROP_UINT (ctx->acfg.audioPL) );
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, & PROP_UINT(sr));
|
|
|
+
|
|
|
+ timescale = sr;
|
|
|
+
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : timescale));
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, & PROP_UINT(ctx->nb_ch) );
|
|
|
+
|
|
|
+ if (ctx->bitrate) {
|
|
|
+ gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+GF_Err latm_dmx_process(GF_Filter *filter)
|
|
|
+{
|
|
|
+ GF_LATMDmxCtx *ctx = gf_filter_get_udta(filter);
|
|
|
+ GF_FilterPacket *pck, *dst_pck;
|
|
|
+ u32 pos;
|
|
|
+ u8 *data, *output;
|
|
|
+ u32 pck_size, prev_pck_size;
|
|
|
+ u64 cts = GF_FILTER_NO_TS;
|
|
|
+
|
|
|
+ int secret = 42;
|
|
|
+ int *key_ptr = &secret;
|
|
|
+
|
|
|
+ if (ctx->in_error)
|
|
|
+ return ctx->in_error;
|
|
|
+
|
|
|
+
|
|
|
+ if (!ctx->duration.num)
|
|
|
+ latm_dmx_check_dur(filter, ctx, key_ptr);
|
|
|
+
|
|
|
+ if (ctx->opid && !ctx->is_playing)
|
|
|
+ return GF_OK;
|
|
|
+
|
|
|
+ pck = gf_filter_pid_get_packet(ctx->ipid);
|
|
|
+ if (!pck) {
|
|
|
+ if (gf_filter_pid_is_eos(ctx->ipid)) {
|
|
|
+ if (!ctx->latm_buffer_size) {
|
|
|
+ if (ctx->opid)
|
|
|
+ gf_filter_pid_set_eos(ctx->opid);
|
|
|
+ if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
|
|
|
+ ctx->src_pck = NULL;
|
|
|
+ return GF_EOS;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return GF_OK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ data = (char *) gf_filter_pck_get_data(pck, &pck_size);
|
|
|
+
|
|
|
+
|
|
|
+ if (ctx->timescale && pck) {
|
|
|
+ cts = gf_filter_pck_get_cts(pck);
|
|
|
+ }
|
|
|
+
|
|
|
+ prev_pck_size = ctx->latm_buffer_size;
|
|
|
+
|
|
|
+ if (pck && !ctx->resume_from) {
|
|
|
+ if (ctx->latm_buffer_size + pck_size > ctx->latm_buffer_alloc) {
|
|
|
+ ctx->latm_buffer_alloc = ctx->latm_buffer_size + pck_size;
|
|
|
+ ctx->latm_buffer = gf_realloc(ctx->latm_buffer, ctx->latm_buffer_alloc);
|
|
|
+ }
|
|
|
+ memcpy(ctx->latm_buffer + ctx->latm_buffer_size, data, pck_size);
|
|
|
+ ctx->latm_buffer_size += pck_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ctx->bs) ctx->bs = gf_bs_new(ctx->latm_buffer, ctx->latm_buffer_size, GF_BITSTREAM_READ);
|
|
|
+ else gf_bs_reassign_buffer(ctx->bs, ctx->latm_buffer, ctx->latm_buffer_size);
|
|
|
+
|
|
|
+ if (ctx->resume_from) {
|
|
|
+ gf_bs_seek(ctx->bs, ctx->resume_from-1);
|
|
|
+ ctx->resume_from = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cts == GF_FILTER_NO_TS)
|
|
|
+ prev_pck_size = 0;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ pos = (u32) gf_bs_get_position(ctx->bs);
|
|
|
+ u8 latm_buffer[4096];
|
|
|
+ u32 latm_frame_size = 4096;
|
|
|
+ if (!latm_dmx_sync_frame_bs(ctx->bs,&ctx->acfg, &latm_frame_size, latm_buffer, NULL, key_ptr)) break;
|
|
|
+
|
|
|
+ if (ctx->in_seek) {
|
|
|
+ u64 nb_samples_at_seek = (u64) (ctx->start_range * GF_M4ASampleRates[ctx->sr_idx]);
|
|
|
+ if (ctx->cts + ctx->dts_inc >= nb_samples_at_seek) {
|
|
|
+
|
|
|
+ ctx->in_seek = GF_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ latm_dmx_check_pid(filter, ctx, key_ptr);
|
|
|
+
|
|
|
+ if (!ctx->is_playing) {
|
|
|
+ ctx->resume_from = pos+1;
|
|
|
+ return GF_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ctx->in_seek) {
|
|
|
+ GF_FilterSAPType sap = GF_FILTER_SAP_1;
|
|
|
+
|
|
|
+ dst_pck = gf_filter_pck_new_alloc(ctx->opid, latm_frame_size, &output);
|
|
|
+ if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);
|
|
|
+
|
|
|
+ memcpy(output, latm_buffer, latm_frame_size);
|
|
|
+
|
|
|
+ gf_filter_pck_set_cts(dst_pck, ctx->cts);
|
|
|
+ gf_filter_pck_set_duration(dst_pck, ctx->dts_inc);
|
|
|
+ gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
|
|
|
+
|
|
|
+
|
|
|
+ if (ctx->acfg.base_object_type==GF_CODECID_USAC) {
|
|
|
+ if (latm_frame_size && (output[0] & 0x80) && !ctx->prev_sap) {
|
|
|
+ sap = GF_FILTER_SAP_1;
|
|
|
+ ctx->prev_sap = GF_TRUE;
|
|
|
+ } else {
|
|
|
+ sap = GF_FILTER_SAP_NONE;
|
|
|
+ ctx->prev_sap = GF_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ gf_filter_pck_set_sap(dst_pck, sap);
|
|
|
+
|
|
|
+ gf_filter_pck_send(dst_pck);
|
|
|
+ }
|
|
|
+ latm_dmx_update_cts(ctx);
|
|
|
+
|
|
|
+ if (prev_pck_size) {
|
|
|
+ pos = (u32) gf_bs_get_position(ctx->bs);
|
|
|
+ if (prev_pck_size<=pos) {
|
|
|
+ prev_pck_size=0;
|
|
|
+ if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
|
|
|
+ ctx->src_pck = pck;
|
|
|
+ if (pck)
|
|
|
+ gf_filter_pck_ref_props(&ctx->src_pck);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pck) {
|
|
|
+ pos = (u32) gf_bs_get_position(ctx->bs);
|
|
|
+ assert(ctx->latm_buffer_size >= pos);
|
|
|
+ memmove(ctx->latm_buffer, ctx->latm_buffer+pos, ctx->latm_buffer_size - pos);
|
|
|
+ ctx->latm_buffer_size -= pos;
|
|
|
+ gf_filter_pid_drop_packet(ctx->ipid);
|
|
|
+ assert(!ctx->resume_from);
|
|
|
+ } else {
|
|
|
+ ctx->latm_buffer_size = 0;
|
|
|
+ return latm_dmx_process(filter);
|
|
|
+ }
|
|
|
+ return GF_OK;
|
|
|
+}
|