static int SetOutputType(decoder_t *p_dec, DWORD stream_id, IMFMediaType **result) { decoder_sys_t *p_sys = p_dec->p_sys; HRESULT hr; *result = NULL; IMFMediaType *output_media_type = NULL; /* * Enumerate available output types. The list is ordered by * preference thus we will use the first one unless YV12/I420 is * available for video or float32 for audio. */ int output_type_index = 0; bool found = false; for (int i = 0; !found; ++i) { hr = IMFTransform_GetOutputAvailableType(p_sys->mft, stream_id, i, &output_media_type); if (hr == MF_E_NO_MORE_TYPES) break; else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { /* The input type must be set before setting the output type for this MFT. */ return VLC_SUCCESS; } else if (FAILED(hr)) goto error; GUID subtype; hr = IMFMediaType_GetGUID(output_media_type, &MF_MT_SUBTYPE, &subtype); if (FAILED(hr)) goto error; if (p_dec->fmt_in.i_cat == VIDEO_ES) { if (IsEqualGUID(&subtype, &MFVideoFormat_YV12) || IsEqualGUID(&subtype, &MFVideoFormat_I420)) found = true; } else { UINT32 bits_per_sample; hr = IMFMediaType_GetUINT32(output_media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample); if (FAILED(hr)) continue; if (bits_per_sample == 32 && IsEqualGUID(&subtype, &MFAudioFormat_Float)) found = true; } if (found) output_type_index = i; IMFMediaType_Release(output_media_type); output_media_type = NULL; } /* * It's not an error if we don't find the output type we were * looking for, in this case we use the first available type which * is the "preferred" output type for this MFT. */ hr = IMFTransform_GetOutputAvailableType(p_sys->mft, stream_id, output_type_index, &output_media_type); if (FAILED(hr)) goto error; hr = IMFTransform_SetOutputType(p_sys->mft, stream_id, output_media_type, 0); if (FAILED(hr)) goto error; GUID subtype; hr = IMFMediaType_GetGUID(output_media_type, &MF_MT_SUBTYPE, &subtype); if (FAILED(hr)) goto error; if (p_dec->fmt_in.i_cat == VIDEO_ES) { video_format_Copy( &p_dec->fmt_out.video, &p_dec->fmt_in.video ); p_dec->fmt_out.i_codec = vlc_fourcc_GetCodec(p_dec->fmt_in.i_cat, subtype.Data1); } else { p_dec->fmt_out.audio = p_dec->fmt_in.audio; UINT32 bitspersample = 0; hr = IMFMediaType_GetUINT32(output_media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bitspersample); if (SUCCEEDED(hr) && bitspersample) p_dec->fmt_out.audio.i_bitspersample = bitspersample; UINT32 channels = 0; hr = IMFMediaType_GetUINT32(output_media_type, &MF_MT_AUDIO_NUM_CHANNELS, &channels); if (SUCCEEDED(hr) && channels) p_dec->fmt_out.audio.i_channels = channels; UINT32 rate = 0; hr = IMFMediaType_GetUINT32(output_media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate); if (SUCCEEDED(hr) && rate) p_dec->fmt_out.audio.i_rate = rate; vlc_fourcc_t fourcc; wf_tag_to_fourcc(subtype.Data1, &fourcc, NULL); p_dec->fmt_out.i_codec = vlc_fourcc_GetCodecAudio(fourcc, p_dec->fmt_out.audio.i_bitspersample); p_dec->fmt_out.audio.i_physical_channels = pi_channels_maps[p_dec->fmt_out.audio.i_channels]; } *result = output_media_type; return VLC_SUCCESS; error: msg_Err(p_dec, "Error in SetOutputType()"); if (output_media_type) IMFMediaType_Release(output_media_type); return VLC_EGENERIC; }
static int parse_Manifest( stream_t *s ) { stream_sys_t *p_sys = s->p_sys; xml_reader_t *vlc_reader = NULL; int type = UNKNOWN_ES; const char *name, *value; stream_t *st = s->p_source; msg_Dbg( s, "Manifest parsing\n" ); vlc_reader = xml_ReaderCreate( st, st ); if( !vlc_reader ) { msg_Err( s, "Failed to open source for parsing" ); return VLC_EGENERIC; } const char *node; uint8_t *WaveFormatEx; sms_stream_t *sms = NULL; quality_level_t *ql = NULL; custom_attrs_t *cp = NULL; int64_t start_time = 0, duration = 0; int64_t computed_start_time = 0, computed_duration = 0; unsigned next_track_id = 1; int loop_count = 0; bool b_weird = false; int ret = VLC_SUCCESS; #define TIMESCALE 10000000 while( (type = xml_ReaderNextNode( vlc_reader, &node )) > 0 ) { switch( type ) { case XML_READER_STARTELEM: if( !strcmp( node, "SmoothStreamingMedia" ) ) { while( (name = xml_ReaderNextAttr( vlc_reader, &value )) ) { if( !strcmp( name, "Duration" ) ) p_sys->vod_duration = strtoull( value, NULL, 10 ); else if( !strcmp( name, "TimeScale" ) ) p_sys->timescale = strtoul( value, NULL, 10 ); else if ( !strcmp( name, "LookAheadFragmentCount" ) ) p_sys->download.lookahead_count = strtoul( value, NULL, 10 ); } if( !p_sys->timescale ) p_sys->timescale = TIMESCALE; } else if( !strcmp( node, "StreamIndex" ) ) { sms_Free( sms ); sms = sms_New(); if( unlikely( !sms ) ) { ret = VLC_ENOMEM; goto cleanup; } sms->id = next_track_id; next_track_id++; while( (name = xml_ReaderNextAttr( vlc_reader, &value )) ) { if( !strcmp( name, "Type" ) ) { if( !strcmp( value, "video" ) ) sms->type = VIDEO_ES; else if( !strcmp( value, "audio" ) ) sms->type = AUDIO_ES; else if( !strcmp( value, "text" ) ) sms->type = SPU_ES; } else if( !strcmp( name, "Name" ) ) sms->name = strdup( value ); else if( !strcmp( name, "TimeScale" ) ) sms->timescale = strtoull( value, NULL, 10 ); else if( !strcmp( name, "FourCC" ) ) sms->default_FourCC = VLC_FOURCC( value[0], value[1], value[2], value[3] ); else if( !strcmp( name, "Chunks" ) ) { sms->vod_chunks_nb = strtoul( value, NULL, 10 ); if( sms->vod_chunks_nb == 0 ) /* live */ sms->vod_chunks_nb = UINT32_MAX; } else if( !strcmp( name, "QualityLevels" ) ) sms->qlevel_nb = strtoul( value, NULL, 10 ); else if( !strcmp( name, "Url" ) ) sms->url_template = strdup(value); } if( !sms->timescale ) sms->timescale = TIMESCALE; if( !sms->name ) { if( sms->type == VIDEO_ES ) sms->name = strdup( "video" ); else if( sms->type == AUDIO_ES ) sms->name = strdup( "audio" ); else if( sms->type == SPU_ES ) sms->name = strdup( "text" ); } } else if ( !strcmp( node, "CustomAttributes" ) ) { if (!sms || !ql || cp) break; cp = (custom_attrs_t *) calloc( 1, sizeof(*cp) ); if( unlikely( !cp ) ) { ret = VLC_ENOMEM; goto cleanup; } } else if ( !strcmp( node, "Attribute" ) ) { if (!sms || !ql || !cp) break; while( (name = xml_ReaderNextAttr( vlc_reader, &value )) ) { if( !strcmp( name, "Name" ) && !cp->psz_key ) cp->psz_key = strdup( value ); else if( !strcmp( name, "Value" ) && !cp->psz_value ) cp->psz_value = strdup( value ); } } else if( !strcmp( node, "QualityLevel" ) ) { if ( !sms ) break; ql = ql_New(); if( !ql ) { ret = VLC_ENOMEM; goto cleanup; } while( (name = xml_ReaderNextAttr( vlc_reader, &value )) ) { if( !strcmp( name, "Index" ) ) ql->Index = strtol( value, NULL, 10 ); else if( !strcmp( name, "Bitrate" ) ) ql->Bitrate = strtoul( value, NULL, 10 ); else if( !strcmp( name, "PacketSize" ) ) ql->nBlockAlign = strtoul( value, NULL, 10 ); else if( !strcmp( name, "FourCC" ) ) ql->FourCC = VLC_FOURCC( value[0], value[1], value[2], value[3] ); else if( !strcmp( name, "CodecPrivateData" ) ) ql->CodecPrivateData = strdup( value ); else if( !strcmp( name, "WaveFormatEx" ) ) { WaveFormatEx = decode_string_hex_to_binary( value ); uint16_t data_len = ((uint16_t *)WaveFormatEx)[8]; ql->CodecPrivateData = strndup( value + 36, data_len * 2 ); uint16_t wf_tag = ((uint16_t *)WaveFormatEx)[0]; wf_tag_to_fourcc( wf_tag, &ql->FourCC, NULL ); ql->Channels = ((uint16_t *)WaveFormatEx)[1]; ql->SamplingRate = ((uint32_t *)WaveFormatEx)[1]; ql->nBlockAlign = ((uint16_t *)WaveFormatEx)[6]; ql->BitsPerSample = ((uint16_t *)WaveFormatEx)[7]; free( WaveFormatEx ); } else if( !strcmp( name, "MaxWidth" ) || !strcmp( name, "Width" ) ) ql->MaxWidth = strtoul( value, NULL, 10 ); else if( !strcmp( name, "MaxHeight" ) || !strcmp( name, "Height" ) ) ql->MaxHeight = strtoul( value, NULL, 10 ); else if( !strcmp( name, "Channels" ) ) ql->Channels = strtoul( value, NULL, 10 ); else if( !strcmp( name, "SamplingRate" ) ) ql->SamplingRate = strtoul( value, NULL, 10 ); else if( !strcmp( name, "BitsPerSample" ) ) ql->BitsPerSample = strtoul( value, NULL, 10 ); } ARRAY_APPEND( sms->qlevels, ql ); } else if ( !strcmp( node, "Content" ) && sms && !sms->url_template ) { /* empty(@Url) && ./Content == manifest embedded content */ sms_Free( sms ); sms = NULL; } else if( !strcmp( node, "c" ) ) { if ( !sms ) break; loop_count++; start_time = duration = -1; while( (name = xml_ReaderNextAttr( vlc_reader, &value )) ) { if( !strcmp( name, "t" ) ) start_time = strtoll( value, NULL, 10 ); if( !strcmp( name, "d" ) ) duration = strtoll( value, NULL, 10 ); } if( start_time == -1 ) { assert( duration != -1 ); computed_start_time += computed_duration; computed_duration = duration; } else if( duration == -1 ) { assert( start_time != -1 ); /* Handle weird Manifests which give only the start time * of the first segment. In those cases, we have to look * at the start time of the second segment to compute * the duration of the first one. */ if( loop_count == 1 ) { b_weird = true; computed_start_time = start_time; continue; } computed_duration = start_time - computed_start_time; if( !b_weird ) computed_start_time = start_time; } else { if( b_weird ) computed_duration = start_time - computed_start_time; else { computed_start_time = start_time; computed_duration = duration; } } if( unlikely( chunk_AppendNew( sms, computed_duration, computed_start_time ) == NULL ) ) { ret = VLC_ENOMEM; goto cleanup; } if( b_weird && start_time != -1 ) computed_start_time = start_time; } break; case XML_READER_ENDELEM: if ( !strcmp( node, "CustomAttributes" ) ) { if ( cp ) { ARRAY_APPEND(ql->custom_attrs, cp); cp = NULL; } } else if ( !strcmp( node, "Attribute" ) ) { if( !cp->psz_key || !cp->psz_value ) { cleanup_attributes( &cp ); } } else if( strcmp( node, "StreamIndex" ) ) break; else if ( sms ) { ARRAY_APPEND( p_sys->sms, sms ); computed_start_time = 0; computed_duration = 0; loop_count = 0; if( b_weird && !chunk_AppendNew( sms, computed_duration, computed_start_time ) ) { ret = VLC_ENOMEM; goto cleanup; } b_weird = false; if( sms->qlevel_nb == 0 ) sms->qlevel_nb = sms->qlevels.i_size; sms = NULL; } break; case XML_READER_TEXT: break; default: ret = VLC_EGENERIC; goto cleanup; } } #undef TIMESCALE cleanup: cleanup_attributes( &cp ); sms_Free( sms ); xml_ReaderDelete( vlc_reader ); return ret; }
/***************************************************************************** * Open: check file and initializes structures *****************************************************************************/ static int Open( vlc_object_t * p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; unsigned int i_size; unsigned int i_extended; const char *psz_name; WAVEFORMATEXTENSIBLE *p_wf_ext = NULL; WAVEFORMATEX *p_wf = NULL; /* Is it a wav file ? */ if( stream_Peek( p_demux->s, &p_peek, 12 ) < 12 ) return VLC_EGENERIC; if( memcmp( p_peek, "RIFF", 4 ) || memcmp( &p_peek[8], "WAVE", 4 ) ) { return VLC_EGENERIC; } p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = malloc( sizeof( *p_sys ) ); if( unlikely(!p_sys) ) return VLC_ENOMEM; p_sys->p_es = NULL; p_sys->i_chans_to_reorder = 0; p_sys->i_channel_mask = 0; /* skip riff header */ if( stream_Read( p_demux->s, NULL, 12 ) != 12 ) goto error; /* search fmt chunk */ if( ChunkFind( p_demux, "fmt ", &i_size ) ) { msg_Err( p_demux, "cannot find 'fmt ' chunk" ); goto error; } i_size += 2; if( i_size < sizeof( WAVEFORMATEX ) ) { msg_Err( p_demux, "invalid 'fmt ' chunk" ); goto error; } if( stream_Read( p_demux->s, NULL, 8 ) != 8 ) goto error; /* load waveformatex */ p_wf_ext = malloc( i_size ); if( unlikely( !p_wf_ext ) ) goto error; p_wf = &p_wf_ext->Format; p_wf->cbSize = 0; i_size -= 2; if( stream_Read( p_demux->s, p_wf, i_size ) != (int)i_size || ( ( i_size & 1 ) && stream_Read( p_demux->s, NULL, 1 ) != 1 ) ) { msg_Err( p_demux, "cannot load 'fmt ' chunk" ); goto error; } es_format_Init( &p_sys->fmt, AUDIO_ES, 0 ); wf_tag_to_fourcc( GetWLE( &p_wf->wFormatTag ), &p_sys->fmt.i_codec, &psz_name ); p_sys->fmt.audio.i_channels = GetWLE ( &p_wf->nChannels ); p_sys->fmt.audio.i_rate = GetDWLE( &p_wf->nSamplesPerSec ); p_sys->fmt.audio.i_blockalign = GetWLE( &p_wf->nBlockAlign ); p_sys->fmt.i_bitrate = GetDWLE( &p_wf->nAvgBytesPerSec ) * 8; p_sys->fmt.audio.i_bitspersample = GetWLE( &p_wf->wBitsPerSample ); if( i_size >= sizeof(WAVEFORMATEX) ) p_sys->fmt.i_extra = __MIN( GetWLE( &p_wf->cbSize ), i_size - sizeof(WAVEFORMATEX) ); i_extended = 0; /* Handle new WAVE_FORMAT_EXTENSIBLE wav files */ /* see the following link for more information: * http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EFAA */ if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_EXTENSIBLE && i_size >= sizeof( WAVEFORMATEXTENSIBLE ) && ( p_sys->fmt.i_extra + sizeof( WAVEFORMATEX ) >= sizeof( WAVEFORMATEXTENSIBLE ) ) ) { unsigned i_channel_mask; GUID guid_subformat; guid_subformat = p_wf_ext->SubFormat; guid_subformat.Data1 = GetDWLE( &p_wf_ext->SubFormat.Data1 ); guid_subformat.Data2 = GetWLE( &p_wf_ext->SubFormat.Data2 ); guid_subformat.Data3 = GetWLE( &p_wf_ext->SubFormat.Data3 ); sf_tag_to_fourcc( &guid_subformat, &p_sys->fmt.i_codec, &psz_name ); i_extended = sizeof( WAVEFORMATEXTENSIBLE ) - sizeof( WAVEFORMATEX ); p_sys->fmt.i_extra -= i_extended; i_channel_mask = GetDWLE( &p_wf_ext->dwChannelMask ); if( i_channel_mask ) { int i_match = 0; for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(*pi_channels_src); i++ ) { if( i_channel_mask & pi_channels_src[i] ) { if( !( p_sys->i_channel_mask & pi_channels_in[i] ) ) i_match++; i_channel_mask &= ~pi_channels_src[i]; p_sys->i_channel_mask |= pi_channels_in[i]; if( i_match >= p_sys->fmt.audio.i_channels ) break; } } if( i_channel_mask ) msg_Warn( p_demux, "Some channels are unrecognized or uselessly specified (0x%x)", i_channel_mask ); if( i_match < p_sys->fmt.audio.i_channels ) { int i_missing = p_sys->fmt.audio.i_channels - i_match; msg_Warn( p_demux, "Trying to fill up unspecified position for %d channels", p_sys->fmt.audio.i_channels - i_match ); static const uint32_t pi_pair[] = { AOUT_CHAN_REARLEFT|AOUT_CHAN_REARRIGHT, AOUT_CHAN_MIDDLELEFT|AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT }; /* FIXME: Unused yet static const uint32_t pi_center[] = { AOUT_CHAN_REARCENTER, 0, AOUT_CHAN_CENTER }; */ /* Try to complete with pair */ for( unsigned i = 0; i < sizeof(pi_pair)/sizeof(*pi_pair); i++ ) { if( i_missing >= 2 && !(p_sys->i_channel_mask & pi_pair[i] ) ) { i_missing -= 2; p_sys->i_channel_mask |= pi_pair[i]; } } /* Well fill up with what we can */ for( unsigned i = 0; i < sizeof(pi_channels_in)/sizeof(*pi_channels_in) && i_missing > 0; i++ ) { if( !( p_sys->i_channel_mask & pi_channels_in[i] ) ) { p_sys->i_channel_mask |= pi_channels_in[i]; i_missing--; if( i_missing <= 0 ) break; } } i_match = p_sys->fmt.audio.i_channels - i_missing; } if( i_match < p_sys->fmt.audio.i_channels ) { msg_Err( p_demux, "Invalid/unsupported channel mask" ); p_sys->i_channel_mask = 0; } } } else if( GetWLE( &p_wf->wFormatTag ) == WAVE_FORMAT_PCM && p_sys->fmt.audio.i_channels > 2 && p_sys->fmt.audio.i_channels <= 9 ) { for( int i = 0; i < p_sys->fmt.audio.i_channels; i++ ) p_sys->i_channel_mask |= pi_channels_in[i]; } if( p_sys->i_channel_mask ) { if( p_sys->fmt.i_codec == VLC_FOURCC('a','r','a','w') || p_sys->fmt.i_codec == VLC_FOURCC('p','c','m',' ') || p_sys->fmt.i_codec == VLC_FOURCC('a','f','l','t') ) p_sys->i_chans_to_reorder = aout_CheckChannelReorder( pi_channels_in, NULL, p_sys->i_channel_mask, p_sys->pi_chan_table ); msg_Dbg( p_demux, "channel mask: %x, reordering: %u", p_sys->i_channel_mask, p_sys->i_chans_to_reorder ); } p_sys->fmt.audio.i_physical_channels = p_sys->fmt.audio.i_original_channels = p_sys->i_channel_mask; if( p_sys->fmt.i_extra > 0 ) { p_sys->fmt.p_extra = malloc( p_sys->fmt.i_extra ); if( unlikely(!p_sys->fmt.p_extra) ) { p_sys->fmt.i_extra = 0; goto error; } memcpy( p_sys->fmt.p_extra, (uint8_t *)p_wf + sizeof( WAVEFORMATEX ) + i_extended, p_sys->fmt.i_extra ); } msg_Dbg( p_demux, "format: 0x%4.4x, fourcc: %4.4s, channels: %d, " "freq: %u Hz, bitrate: %uKo/s, blockalign: %d, bits/samples: %d, " "extra size: %d", GetWLE( &p_wf->wFormatTag ), (char *)&p_sys->fmt.i_codec, p_sys->fmt.audio.i_channels, p_sys->fmt.audio.i_rate, p_sys->fmt.i_bitrate / 8 / 1024, p_sys->fmt.audio.i_blockalign, p_sys->fmt.audio.i_bitspersample, p_sys->fmt.i_extra ); free( p_wf ); p_wf = NULL; switch( p_sys->fmt.i_codec ) { case VLC_FOURCC( 'a', 'r', 'a', 'w' ): case VLC_FOURCC( 'a', 'f', 'l', 't' ): case VLC_FOURCC( 'u', 'l', 'a', 'w' ): case VLC_CODEC_ALAW: case VLC_CODEC_MULAW: case VLC_FOURCC( 'p', 'c', 'm', ' ' ): if( FrameInfo_PCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_ADPCM_MS: if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_ADPCM_IMA_WAV: if( FrameInfo_IMA_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_FOURCC( 'm', 's', 0x00, 0x61 ): case VLC_FOURCC( 'm', 's', 0x00, 0x62 ): /* FIXME not sure at all FIXME */ if( FrameInfo_MS_ADPCM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; case VLC_CODEC_MPGA: case VLC_CODEC_A52: /* FIXME set end of area FIXME */ goto error; case VLC_CODEC_GSM_MS: case VLC_CODEC_ADPCM_G726: case VLC_CODEC_TRUESPEECH: case VLC_CODEC_ATRAC3: case VLC_CODEC_G723_1: if( FrameInfo_MSGSM( &p_sys->i_frame_size, &p_sys->i_frame_samples, &p_sys->fmt ) ) goto error; break; default: msg_Err( p_demux, "unsupported codec (%4.4s)", (char*)&p_sys->fmt.i_codec ); goto error; } if( p_sys->i_frame_size <= 0 || p_sys->i_frame_samples <= 0 ) { msg_Dbg( p_demux, "invalid frame size: %i %i", p_sys->i_frame_size, p_sys->i_frame_samples ); goto error; } if( p_sys->fmt.audio.i_rate <= 0 ) { msg_Dbg( p_demux, "invalid sample rate: %i", p_sys->fmt.audio.i_rate ); goto error; } msg_Dbg( p_demux, "found %s audio format", psz_name ); if( ChunkFind( p_demux, "data", &p_sys->i_data_size ) ) { msg_Err( p_demux, "cannot find 'data' chunk" ); goto error; } if( stream_Read( p_demux->s, NULL, 8 ) != 8 ) goto error; p_sys->i_data_pos = stream_Tell( p_demux->s ); if( p_sys->fmt.i_bitrate <= 0 ) { p_sys->fmt.i_bitrate = (int64_t)p_sys->i_frame_size * p_sys->fmt.audio.i_rate * 8 / p_sys->i_frame_samples; } p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt ); date_Init( &p_sys->pts, p_sys->fmt.audio.i_rate, 1 ); date_Set( &p_sys->pts, 1 ); return VLC_SUCCESS; error: msg_Err( p_demux, "An error occurred during wav demuxing" ); free( p_wf ); free( p_sys ); return VLC_EGENERIC; }