static void ActivateSets( decoder_t *p_dec, const h264_sequence_parameter_set_t *p_sps, const h264_picture_parameter_set_t *p_pps ) { decoder_sys_t *p_sys = p_dec->p_sys; p_sys->p_active_pps = p_pps; p_sys->p_active_sps = p_sps; if( p_sps ) { p_dec->fmt_out.i_profile = p_sps->i_profile; p_dec->fmt_out.i_level = p_sps->i_level; (void) h264_get_picture_size( p_sps, &p_dec->fmt_out.video.i_width, &p_dec->fmt_out.video.i_height, &p_dec->fmt_out.video.i_visible_width, &p_dec->fmt_out.video.i_visible_height ); if( p_sps->vui.i_sar_num != 0 && p_sps->vui.i_sar_den != 0 ) { p_dec->fmt_out.video.i_sar_num = p_sps->vui.i_sar_num; p_dec->fmt_out.video.i_sar_den = p_sps->vui.i_sar_den; } if( p_sps->vui.b_valid ) { if( !p_dec->fmt_in.video.i_frame_rate_base && p_sps->vui.i_num_units_in_tick > 0 && p_sps->vui.i_time_scale > 1 ) { const unsigned i_rate_base = p_sps->vui.i_num_units_in_tick; const unsigned i_rate = p_sps->vui.i_time_scale >> 1; /* num_clock_ts == 2 */ if( i_rate_base != p_dec->fmt_out.video.i_frame_rate_base || i_rate != p_dec->fmt_out.video.i_frame_rate ) { p_dec->fmt_out.video.i_frame_rate_base = i_rate_base; p_dec->fmt_out.video.i_frame_rate = i_rate; date_Change( &p_sys->dts, p_sps->vui.i_time_scale, p_sps->vui.i_num_units_in_tick ); } } if( p_dec->fmt_in.video.primaries == COLOR_PRIMARIES_UNDEF ) h264_get_colorimetry( p_sps, &p_dec->fmt_out.video.primaries, &p_dec->fmt_out.video.transfer, &p_dec->fmt_out.video.space, &p_dec->fmt_out.video.b_color_range_full ); } } }
int transcode_audio_process( sout_stream_t *p_stream, sout_stream_id_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; block_t *p_block, *p_audio_buf; *out = NULL; if( unlikely( in == NULL ) ) { block_t *p_block; do { p_block = id->p_encoder->pf_encode_audio(id->p_encoder, NULL ); block_ChainAppend( out, p_block ); } while( p_block ); return VLC_SUCCESS; } while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder, &in )) ) { if( unlikely( !id->p_encoder->p_module ) ) { /* Complete destination format */ id->p_encoder->fmt_out.i_codec = p_sys->i_acodec; id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ? p_sys->i_sample_rate : id->p_decoder->fmt_out.audio.i_rate; id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate; id->p_encoder->fmt_out.audio.i_bitspersample = id->p_decoder->fmt_out.audio.i_bitspersample; id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ? p_sys->i_channels : id->p_decoder->fmt_out.audio.i_channels; /* Sanity check for audio channels */ id->p_encoder->fmt_out.audio.i_channels = __MIN( id->p_encoder->fmt_out.audio.i_channels, id->p_decoder->fmt_out.audio.i_channels ); id->p_encoder->fmt_out.audio.i_original_channels = id->p_decoder->fmt_in.audio.i_physical_channels; id->p_encoder->fmt_out.audio.i_physical_channels = pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels]; if( transcode_audio_initialize_encoder( id, p_stream ) ) { msg_Err( p_stream, "cannot create audio chain" ); return VLC_EGENERIC; } if( unlikely( transcode_audio_initialize_filters( p_stream, id, p_sys, &id->p_decoder->fmt_out.audio ) != VLC_SUCCESS ) ) return VLC_EGENERIC; date_Init( &id->interpolated_pts, id->p_decoder->fmt_out.audio.i_rate, 1 ); } /* Check if audio format has changed, and filters need reinit */ if( unlikely( ( id->p_decoder->fmt_out.audio.i_rate != p_sys->fmt_audio.i_rate ) || ( id->p_decoder->fmt_out.audio.i_physical_channels != p_sys->fmt_audio.i_physical_channels ) ) ) { msg_Info( p_stream, "Audio changed, trying to reinitialize filters" ); if( id->p_af_chain != NULL ) aout_FiltersDelete( (vlc_object_t *)NULL, id->p_af_chain ); /* decoders don't set audio.i_format, but audio filters use it */ id->p_decoder->fmt_out.audio.i_format = id->p_decoder->fmt_out.i_codec; aout_FormatPrepare( &id->p_decoder->fmt_out.audio ); if( transcode_audio_initialize_filters( p_stream, id, p_sys, &id->p_decoder->fmt_out.audio ) != VLC_SUCCESS ) return VLC_EGENERIC; /* Set interpolated_pts to run with new samplerate */ date_Change( &id->interpolated_pts, p_sys->fmt_audio.i_rate, 1 ); } if( p_sys->b_master_sync ) { mtime_t i_pts = date_Get( &id->interpolated_pts ); mtime_t i_drift = 0; if( likely( p_audio_buf->i_pts != VLC_TS_INVALID ) ) i_drift = p_audio_buf->i_pts - i_pts; if ( unlikely(i_drift > MASTER_SYNC_MAX_DRIFT || i_drift < -MASTER_SYNC_MAX_DRIFT) ) { msg_Dbg( p_stream, "audio drift is too high (%"PRId64"), resetting master sync", i_drift ); date_Set( &id->interpolated_pts, p_audio_buf->i_pts ); i_pts = date_Get( &id->interpolated_pts ); if( likely(p_audio_buf->i_pts != VLC_TS_INVALID ) ) i_drift = p_audio_buf->i_pts - i_pts; } p_sys->i_master_drift = i_drift; date_Increment( &id->interpolated_pts, p_audio_buf->i_nb_samples ); } p_audio_buf->i_dts = p_audio_buf->i_pts; /* Run filter chain */ p_audio_buf = aout_FiltersPlay( id->p_af_chain, p_audio_buf, INPUT_RATE_DEFAULT ); if( !p_audio_buf ) abort(); p_audio_buf->i_dts = p_audio_buf->i_pts; p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf ); block_ChainAppend( out, p_block ); block_Release( p_audio_buf ); } return VLC_SUCCESS; }
/*** * Packet (Data Unit) inspection, learns parameters from sequence * headers, sets up flags, drops unwanted data units, sets * encapsulation unit termination policy */ static int dirac_InspectDataUnit( decoder_t *p_dec, block_t **pp_block, block_t *p_eu ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block = *pp_block; uint8_t u_parse_code = p_block->p_buffer[4]; if( dirac_isEOS( u_parse_code ) ) { if( p_sys->b_seen_eos ) { /* remove duplicate EOS packets */ block_Release( p_block ); *pp_block = NULL; return DIRAC_DU_IN_EU; } /* p_block is an EOS packet */ p_eu->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; /* for the moment, let this end an encapsulation unit */ /* seeing an eos packet requires a flush of the packetizer * this is detected by the caller of this function */ p_sys->b_seen_seq_hdr = false; p_sys->b_seen_eos = true; return DIRAC_DU_ENDS_EU; #if 0 /* let anything down streem know too */ /* Actually, this is a bad idea: - It sets the discontinuity for every dirac EOS packet which doesnt imply a time discontinuity. - When the synchronizer detects a real discontinuity, it should copy the flags through. p_eu->i_flags |= BLOCK_FLAG_DISCONTINUITY; */ #endif } p_sys->b_seen_eos = false; if( dirac_isPicture( u_parse_code ) ) { /* timestamps apply to pictures only */ p_eu->i_dts = p_sys->i_eu_dts; p_eu->i_pts = p_sys->i_eu_pts; p_sys->i_eu_dts = p_sys->i_eu_pts = VLC_TS_INVALID; if( !p_sys->b_seen_seq_hdr ) { /* can't timestamp in this case, discard later * so that the timestamps aren't lost */ p_eu->i_flags |= DIRAC_DISCARD; } /* p_block is a picture -- it ends the 'encapsulation unit' */ if( dirac_numRefs( u_parse_code ) ) { /* if this picture is not an I frame, ensure that the * random access point flags are not set */ p_eu->i_flags &= ~BLOCK_FLAG_TYPE_I; } dirac_block_encap_t *p_dbe = dirac_GetBlockEncap( p_block ); if( p_dbe && p_block->i_buffer > 13+4 ) { /* record the picture number to save the time gen functions * from having to inspect the data for it */ p_dbe->u_picture_number = GetDWBE( p_block->p_buffer + 13 ); } return DIRAC_DU_ENDS_EU; } if( dirac_isSeqHdr( u_parse_code ) ) { if( !dirac_UnpackSeqHdr( &p_sys->seq_hdr, p_block ) ) { /* couldn't parse the sequence header, just ignore it */ return DIRAC_DU_IN_EU; } p_sys->b_seen_seq_hdr = true; /* a sequence header followed by an I frame is a random * access point; assume that this is the case */ p_eu->i_flags |= BLOCK_FLAG_TYPE_I; es_format_t *p_es = &p_dec->fmt_out; p_es->video.i_width = p_sys->seq_hdr.u_width; p_es->video.i_height = p_sys->seq_hdr.u_height; vlc_ureduce( &p_es->video.i_frame_rate, &p_es->video.i_frame_rate_base , p_sys->seq_hdr.u_fps_num, p_sys->seq_hdr.u_fps_den, 0 ); /* when field coding, dts needs to be incremented in terms of field periods */ unsigned u_pics_per_sec = p_sys->seq_hdr.u_fps_num; if( p_sys->seq_hdr.u_picture_coding_mode == DIRAC_FIELD_CODING ) { u_pics_per_sec *= 2; } date_Change( &p_sys->dts, u_pics_per_sec, p_sys->seq_hdr.u_fps_den ); /* TODO: set p_sys->reorder_buf.u_size_max */ p_sys->i_pts_offset = p_sys->reorder_buf.u_size_max * 1000000 * p_es->video.i_frame_rate_base / p_es->video.i_frame_rate + 1; /* stash a copy of the seqhdr * - required for ogg muxing * - useful for error checking * - it isn't allowed to change until an eos */ free( p_es->p_extra ); p_es->p_extra = calloc( 1, p_block->i_buffer + 13 ); if( !p_es->p_extra ) { p_es->i_extra = 0; return DIRAC_DU_IN_EU; } p_es->i_extra = p_block->i_buffer; memcpy( p_es->p_extra, p_block->p_buffer, p_block->i_buffer ); /* append EOS as per Ogg guidelines */ p_block = dirac_EmitEOS( p_dec, p_block->i_buffer ); if( p_block ) { memcpy( (uint8_t*)p_es->p_extra + p_es->i_extra, p_block->p_buffer, 13 ); p_es->i_extra += 13; } return DIRAC_DU_IN_EU; } /* doesn't end an encapsulation unit */ return DIRAC_DU_IN_EU; }
/** * Non-MIDI Meta events handler */ static int HandleMeta (demux_t *p_demux, mtrk_t *tr) { stream_t *s = p_demux->s; demux_sys_t *p_sys = p_demux->p_sys; uint8_t *payload; uint8_t type; int32_t length; int ret = 0; if (stream_Read (s, &type, 1) != 1) return -1; length = ReadVarInt (s); if (length < 0) return -1; payload = malloc (length + 1); if ((payload == NULL) || (stream_Read (s, payload, length) != length)) { free (payload); return -1; } payload[length] = '\0'; switch (type) { case 0x00: /* Sequence Number */ break; case 0x01: /* Text (comment) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Text : %s", (char *)payload); break; case 0x02: /* Copyright */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Copyright : %s", (char *)payload); break; case 0x03: /* Track name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Track name: %s", (char *)payload); break; case 0x04: /* Instrument name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Instrument: %s", (char *)payload); break; case 0x05: /* Lyric (one syllable) */ /*EnsureUTF8 ((char *)payload);*/ break; case 0x06: /* Marker text */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Marker : %s", (char *)payload); case 0x07: /* Cue point (WAVE filename) */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Cue point : %s", (char *)payload); break; case 0x08: /* Program/Patch name */ EnsureUTF8 ((char *)payload); msg_Info (p_demux, "Patch name: %s", (char *)payload); break; case 0x09: /* MIDI port name */ EnsureUTF8 ((char *)payload); msg_Dbg (p_demux, "MIDI port : %s", (char *)payload); break; case 0x2F: /* End of track */ if (tr->end != stream_Tell (s)) { msg_Err (p_demux, "misplaced end of track"); ret = -1; } break; case 0x51: /* Tempo */ if (length == 3) { uint32_t uspqn = (payload[0] << 16) | (payload[1] << 8) | payload[2]; unsigned tempo = 60 * 1000000 / (uspqn ? uspqn : 1); msg_Dbg (p_demux, "tempo: %uus/qn -> %u BPM", (unsigned)uspqn, tempo); if (tempo < TEMPO_MIN) { msg_Warn (p_demux, "tempo too slow -> %u BPM", TEMPO_MIN); tempo = TEMPO_MIN; } else if (tempo > TEMPO_MAX) { msg_Warn (p_demux, "tempo too fast -> %u BPM", TEMPO_MAX); tempo = TEMPO_MAX; } date_Change (&p_sys->pts, p_sys->ppqn * tempo, 60); } else ret = -1; break; case 0x54: /* SMPTE offset */ if (length == 5) msg_Warn (p_demux, "SMPTE offset not implemented"); else ret = -1; break; case 0x58: /* Time signature */ if (length == 4) ; else ret = -1; break; case 0x59: /* Key signature */ if (length == 2) ; else ret = -1; break; case 0x7f: /* Proprietary event */ msg_Dbg (p_demux, "ignored proprietary SMF Meta Event (%d bytes)", length); break; default: msg_Warn (p_demux, "unknown SMF Meta Event type 0x%02X (%d bytes)", type, length); } free (payload); return ret; }
/***************************************************************************** * Open: probe the packetizer and return score * When opening after demux, the packetizer is only loaded AFTER the decoder * That means that what you set in fmt_out is ignored by the decoder in this special case *****************************************************************************/ static int Open( vlc_object_t *p_this ) { decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys; int i; const bool b_avc = (p_dec->fmt_in.i_original_fourcc == VLC_FOURCC( 'a', 'v', 'c', '1' )); if( p_dec->fmt_in.i_codec != VLC_CODEC_H264 ) return VLC_EGENERIC; if( b_avc && p_dec->fmt_in.i_extra < 7 ) return VLC_EGENERIC; /* Allocate the memory needed to store the decoder's structure */ if( ( p_dec->p_sys = p_sys = malloc( sizeof(decoder_sys_t) ) ) == NULL ) { return VLC_ENOMEM; } p_sys->p_ccs = cc_storage_new(); if( unlikely(!p_sys->p_ccs) ) { free( p_dec->p_sys ); return VLC_ENOMEM; } packetizer_Init( &p_sys->packetizer, p_h264_startcode, sizeof(p_h264_startcode), startcode_FindAnnexB, p_h264_startcode, 1, 5, PacketizeReset, PacketizeParse, PacketizeValidate, p_dec ); p_sys->b_slice = false; p_sys->p_frame = NULL; p_sys->pp_frame_last = &p_sys->p_frame; p_sys->p_sei = NULL; p_sys->pp_sei_last = &p_sys->p_sei; p_sys->b_new_sps = false; p_sys->b_new_pps = false; for( i = 0; i <= H264_SPS_ID_MAX; i++ ) { p_sys->sps[i].p_sps = NULL; p_sys->sps[i].p_block = NULL; } p_sys->p_active_sps = NULL; for( i = 0; i <= H264_PPS_ID_MAX; i++ ) { p_sys->pps[i].p_pps = NULL; p_sys->pps[i].p_block = NULL; } p_sys->p_active_pps = NULL; p_sys->i_recovery_frame_cnt = UINT_MAX; h264_slice_init( &p_sys->slice ); p_sys->i_next_block_flags = 0; p_sys->b_recovered = false; p_sys->i_recoveryfnum = UINT_MAX; p_sys->i_frame_dts = VLC_TS_INVALID; p_sys->i_frame_pts = VLC_TS_INVALID; p_sys->i_dpb_output_delay = 0; /* POC */ h264_poc_context_init( &p_sys->pocctx ); p_sys->prevdatedpoc.pts = VLC_TS_INVALID; date_Init( &p_sys->dts, 30000 * 2, 1001 ); date_Set( &p_sys->dts, VLC_TS_INVALID ); /* Setup properties */ es_format_Copy( &p_dec->fmt_out, &p_dec->fmt_in ); p_dec->fmt_out.i_codec = VLC_CODEC_H264; p_dec->fmt_out.b_packetized = true; if( p_dec->fmt_in.video.i_frame_rate_base && p_dec->fmt_in.video.i_frame_rate && p_dec->fmt_in.video.i_frame_rate <= UINT_MAX / 2 ) { date_Change( &p_sys->dts, p_dec->fmt_in.video.i_frame_rate * 2, p_dec->fmt_in.video.i_frame_rate_base ); } if( b_avc ) { /* This type of stream is produced by mp4 and matroska * when we want to store it in another streamformat, you need to convert * The fmt_in.p_extra should ALWAYS contain the avcC * The fmt_out.p_extra should contain all the SPS and PPS with 4 byte startcodes */ if( h264_isavcC( p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra ) ) { free( p_dec->fmt_out.p_extra ); size_t i_size; p_dec->fmt_out.p_extra = h264_avcC_to_AnnexB_NAL( p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra, &i_size, &p_sys->i_avcC_length_size ); p_dec->fmt_out.i_extra = i_size; p_sys->b_recovered = !!p_dec->fmt_out.i_extra; if(!p_dec->fmt_out.p_extra) { msg_Err( p_dec, "Invalid AVC extradata"); Close( p_this ); return VLC_EGENERIC; } } else { msg_Err( p_dec, "Invalid or missing AVC extradata"); Close( p_this ); return VLC_EGENERIC; } /* Set callback */ p_dec->pf_packetize = PacketizeAVC1; } else { /* This type of stream contains data with 3 of 4 byte startcodes * The fmt_in.p_extra MAY contain SPS/PPS with 4 byte startcodes * The fmt_out.p_extra should be the same */ /* Set callback */ p_dec->pf_packetize = Packetize; } /* */ if( p_dec->fmt_out.i_extra > 0 ) { packetizer_Header( &p_sys->packetizer, p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra ); } if( b_avc ) { /* FIXME: that's not correct for every AVC */ if( !p_sys->b_new_pps || !p_sys->b_new_sps ) { msg_Err( p_dec, "Invalid or missing SPS %d or PPS %d in AVC extradata", p_sys->b_new_sps, p_sys->b_new_pps ); Close( p_this ); return VLC_EGENERIC; } msg_Dbg( p_dec, "Packetizer fed with AVC, nal length size=%d", p_sys->i_avcC_length_size ); } /* CC are the same for H264/AVC in T35 sections (ETSI TS 101 154) */ p_dec->pf_get_cc = GetCc; p_dec->pf_flush = PacketizeFlush; return VLC_SUCCESS; }