📄 mp4.c
字号:
p_stream = malloc( sizeof( mp4_stream_t ) ); es_format_Copy( &p_stream->fmt, p_input->p_fmt ); p_stream->i_track_id = p_sys->i_nb_streams + 1; p_stream->i_length_neg = 0; p_stream->i_entry_count = 0; p_stream->i_entry_max = 1000; p_stream->entry = calloc( p_stream->i_entry_max, sizeof( mp4_entry_t ) ); p_stream->i_dts_start = 0; p_stream->i_duration = 0; p_stream->avc.i_profile = 77; p_stream->avc.i_profile_compat = 64; p_stream->avc.i_level = 30; p_stream->avc.i_sps = 0; p_stream->avc.sps = NULL; p_stream->avc.i_pps = 0; p_stream->avc.pps = NULL; p_input->p_sys = p_stream; msg_Dbg( p_mux, "adding input" ); TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, p_stream ); return VLC_SUCCESS;}/***************************************************************************** * DelStream: *****************************************************************************/static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input ){ msg_Dbg( p_mux, "removing input" ); return VLC_SUCCESS;}static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts ){ mtime_t i_dts; int i_stream; int i; for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ ) { block_fifo_t *p_fifo = p_mux->pp_inputs[i]->p_fifo; block_t *p_buf; if( p_fifo->i_depth <= 1 ) { if( p_mux->pp_inputs[i]->p_fmt->i_cat != SPU_ES ) { return -1; // wait that all fifo have at least 2 packets } /* For SPU, we wait only 1 packet */ continue; } p_buf = block_FifoShow( p_fifo ); if( i_stream < 0 || p_buf->i_dts < i_dts ) { i_dts = p_buf->i_dts; i_stream = i; } } if( pi_stream ) { *pi_stream = i_stream; } if( pi_dts ) { *pi_dts = i_dts; } return i_stream;}/***************************************************************************** * Mux: *****************************************************************************/static int Mux( sout_mux_t *p_mux ){ sout_mux_sys_t *p_sys = p_mux->p_sys; for( ;; ) { sout_input_t *p_input; int i_stream; mp4_stream_t *p_stream; block_t *p_data; mtime_t i_dts; if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 ) { return( VLC_SUCCESS ); } p_input = p_mux->pp_inputs[i_stream]; p_stream = (mp4_stream_t*)p_input->p_sys; p_data = block_FifoGet( p_input->p_fifo ); if( p_stream->fmt.i_codec == VLC_FOURCC( 'h', '2', '6', '4' ) ) { ConvertAVC1( p_mux, p_stream, p_data ); } else if( p_stream->fmt.i_codec == VLC_FOURCC( 's', 'u', 'b', 't' ) ) { p_data = ConvertSUBT( p_mux, p_stream, p_data ); } if( p_stream->fmt.i_cat != SPU_ES ) { /* Fix length of the sample */ if( p_input->p_fifo->i_depth > 0 ) { block_t *p_next = block_FifoShow( p_input->p_fifo ); int64_t i_diff = p_next->i_dts - p_data->i_dts; if( i_diff < I64C(1000000 ) ) /* protection */ { p_data->i_length = i_diff; } } if( p_data->i_length <= 0 ) { msg_Warn( p_mux, "i_length <= 0" ); p_stream->i_length_neg += p_data->i_length - 1; p_data->i_length = 1; } else if( p_stream->i_length_neg < 0 ) { int64_t i_recover = __MIN( p_data->i_length / 4, - p_stream->i_length_neg ); p_data->i_length -= i_recover; p_stream->i_length_neg += i_recover; } } /* Save starting time */ if( p_stream->i_entry_count == 0 ) { p_stream->i_dts_start = p_data->i_dts; /* Update global dts_start */ if( p_sys->i_dts_start <= 0 || p_stream->i_dts_start < p_sys->i_dts_start ) { p_sys->i_dts_start = p_stream->i_dts_start; } } if( p_stream->fmt.i_cat == SPU_ES && p_stream->i_entry_count > 0 ) { int64_t i_length = p_data->i_dts - p_stream->i_last_dts; if( i_length <= 0 ) { /* FIXME handle this broken case */ i_length = 1; } /* Fix last entry */ if( p_stream->entry[p_stream->i_entry_count-1].i_length <= 0 ) { p_stream->entry[p_stream->i_entry_count-1].i_length = i_length; } } /* add index entry */ p_stream->entry[p_stream->i_entry_count].i_pos = p_sys->i_pos; p_stream->entry[p_stream->i_entry_count].i_size = p_data->i_buffer; p_stream->entry[p_stream->i_entry_count].i_pts_dts= __MAX( p_data->i_pts - p_data->i_dts, 0 ); p_stream->entry[p_stream->i_entry_count].i_length = p_data->i_length; p_stream->entry[p_stream->i_entry_count].i_flags = p_data->i_flags; p_stream->i_entry_count++; /* XXX: -1 to always have 2 entry for easy adding of empty SPU */ if( p_stream->i_entry_count >= p_stream->i_entry_max - 1 ) { p_stream->i_entry_max += 1000; p_stream->entry = realloc( p_stream->entry, p_stream->i_entry_max * sizeof( mp4_entry_t ) ); } /* update */ p_stream->i_duration += p_data->i_length; p_sys->i_pos += p_data->i_buffer; /* Save the DTS */ p_stream->i_last_dts = p_data->i_dts; /* write data */ sout_AccessOutWrite( p_mux->p_access, p_data ); if( p_stream->fmt.i_cat == SPU_ES ) { int64_t i_length = p_stream->entry[p_stream->i_entry_count-1].i_length; if( i_length != 0 ) { /* TODO */ msg_Dbg( p_mux, "writing an empty sub" ) ; /* Append a idx entry */ p_stream->entry[p_stream->i_entry_count].i_pos = p_sys->i_pos; p_stream->entry[p_stream->i_entry_count].i_size = 3; p_stream->entry[p_stream->i_entry_count].i_pts_dts= 0; p_stream->entry[p_stream->i_entry_count].i_length = 0; p_stream->entry[p_stream->i_entry_count].i_flags = 0; /* XXX: No need to grow the entry here */ p_stream->i_entry_count++; /* Fix last dts */ p_stream->i_last_dts += i_length; /* Write a " " */ p_data = block_New( p_mux, 3 ); p_data->p_buffer[0] = 0; p_data->p_buffer[1] = 1; p_data->p_buffer[2] = ' '; p_sys->i_pos += p_data->i_buffer; sout_AccessOutWrite( p_mux->p_access, p_data ); } /* Fix duration */ p_stream->i_duration = p_stream->i_last_dts - p_stream->i_dts_start; } } return( VLC_SUCCESS );}/***************************************************************************** * *****************************************************************************/static block_t *ConvertSUBT( sout_mux_t *p_mux, mp4_stream_t *tk, block_t *p_block ){ p_block = block_Realloc( p_block, 2, p_block->i_buffer ); /* No trailling '\0' */ if( p_block->i_buffer > 2 && p_block->p_buffer[p_block->i_buffer-1] == '\0' ) p_block->i_buffer--; p_block->p_buffer[0] = ( (p_block->i_buffer - 2) >> 8 )&0xff; p_block->p_buffer[1] = ( (p_block->i_buffer - 2) )&0xff; return p_block;}static void ConvertAVC1( sout_mux_t *p_mux, mp4_stream_t *tk, block_t *p_block ){ uint8_t *last = p_block->p_buffer; /* Assume it starts with 0x00000001 */ uint8_t *dat = &p_block->p_buffer[4]; uint8_t *end = &p_block->p_buffer[p_block->i_buffer]; /* Replace the 4 bytes start code with 4 bytes size, * FIXME are all startcodes 4 bytes ? (I don't think :( */ while( dat < end ) { int i_size; while( dat < end - 4 ) { if( dat[0] == 0x00 && dat[1] == 0x00 && dat[2] == 0x00 && dat[3] == 0x01 ) { break; } dat++; } if( dat >= end - 4 ) { dat = end; } /* Fix size */ i_size = dat - &last[4]; last[0] = ( i_size >> 24 )&0xff; last[1] = ( i_size >> 16 )&0xff; last[2] = ( i_size >> 8 )&0xff; last[3] = ( i_size )&0xff; if( (last[4]&0x1f) == 7 && tk->avc.i_sps <= 0 ) /* SPS */ { tk->avc.i_sps = i_size; tk->avc.sps = malloc( i_size ); memcpy( tk->avc.sps, &last[4], i_size ); tk->avc.i_profile = tk->avc.sps[1]; tk->avc.i_profile = tk->avc.sps[2]; tk->avc.i_level = tk->avc.sps[3]; } else if( (last[4]&0x1f) == 8 && tk->avc.i_pps <= 0 ) /* PPS */ { tk->avc.i_pps = i_size; tk->avc.pps = malloc( i_size ); memcpy( tk->avc.pps, &last[4], i_size ); } last = dat; dat += 4; }}static int GetDescrLength( int i_size ){ if( i_size < 0x00000080 ) return 2 + i_size; else if( i_size < 0x00004000 ) return 3 + i_size; else if( i_size < 0x00200000 ) return 4 + i_size; else return 5 + i_size;}static bo_t *GetESDS( mp4_stream_t *p_stream ){ bo_t *esds; int i_stream_type; int i_object_type_indication; int i_decoder_specific_info_size; unsigned int i; int64_t i_bitrate_avg = 0; int64_t i_bitrate_max = 0; /* Compute avg/max bitrate */ for( i = 0; i < p_stream->i_entry_count; i++ ) { i_bitrate_avg += p_stream->entry[i].i_size; if( p_stream->entry[i].i_length > 0) { int64_t i_bitrate = I64C(8000000) * p_stream->entry[i].i_size / p_stream->entry[i].i_length; if( i_bitrate > i_bitrate_max ) i_bitrate_max = i_bitrate; } } if( p_stream->i_duration > 0 ) i_bitrate_avg = I64C(8000000) * i_bitrate_avg / p_stream->i_duration; else i_bitrate_avg = 0; if( i_bitrate_max <= 1 ) i_bitrate_max = 0x7fffffff; /* */ if( p_stream->fmt.i_extra > 0 ) { i_decoder_specific_info_size = GetDescrLength( p_stream->fmt.i_extra ); } else { i_decoder_specific_info_size = 0; } esds = box_full_new( "esds", 0, 0 ); /* ES_Descr */ bo_add_descr( esds, 0x03, 3 + GetDescrLength( 13 + i_decoder_specific_info_size ) + GetDescrLength( 1 ) ); bo_add_16be( esds, p_stream->i_track_id ); bo_add_8 ( esds, 0x1f ); // flags=0|streamPriority=0x1f /* DecoderConfigDescr */ bo_add_descr( esds, 0x04, 13 + i_decoder_specific_info_size ); switch( p_stream->fmt.i_codec ) { case VLC_FOURCC( 'm', 'p', '4', 'v' ): i_object_type_indication = 0x20; break; case VLC_FOURCC( 'm', 'p', 'g', 'v' ): /* FIXME MPEG-I=0x6b, MPEG-II = 0x60 -> 0x65 */ i_object_type_indication = 0x60; break; case VLC_FOURCC( 'm', 'p', '4', 'a' ): /* FIXME for mpeg2-aac == 0x66->0x68 */ i_object_type_indication = 0x40; break; case VLC_FOURCC( 'm', 'p', 'g', 'a' ): i_object_type_indication = p_stream->fmt.audio.i_rate < 32000 ? 0x69 : 0x6b; break; default: i_object_type_indication = 0x00; break; } i_stream_type = p_stream->fmt.i_cat == VIDEO_ES ? 0x04 : 0x05; bo_add_8 ( esds, i_object_type_indication ); bo_add_8 ( esds, ( i_stream_type << 2 ) | 1 ); bo_add_24be( esds, 1024 * 1024 ); // bufferSizeDB bo_add_32be( esds, i_bitrate_max ); // maxBitrate bo_add_32be( esds, i_bitrate_avg ); // avgBitrate if( p_stream->fmt.i_extra > 0 ) { int i; /* DecoderSpecificInfo */ bo_add_descr( esds, 0x05, p_stream->fmt.i_extra ); for( i = 0; i < p_stream->fmt.i_extra; i++ ) { bo_add_8( esds, ((uint8_t*)p_stream->fmt.p_extra)[i] ); } } /* SL_Descr mandatory */ bo_add_descr( esds, 0x06, 1 ); bo_add_8 ( esds, 0x02 ); // sl_predefined box_fix( esds ); return esds;}static bo_t *GetWaveTag( mp4_stream_t *p_stream ){ bo_t *wave; bo_t *box;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -