mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	MediaCapture: Fix MP3 audio encoding
This commit is contained in:
		
							parent
							
								
									3772706603
								
							
						
					
					
						commit
						3175214c92
					
				|  | @ -41,6 +41,7 @@ | |||
| #pragma comment(lib, "mfreadwrite") | ||||
| #pragma comment(lib, "mfplat") | ||||
| #pragma comment(lib, "mfuuid") | ||||
| #pragma comment(lib, "Mf") | ||||
| #endif | ||||
| 
 | ||||
| #ifndef __ANDROID__ | ||||
|  | @ -341,7 +342,7 @@ void MediaCaptureBase::EncoderThreadEntryPoint() | |||
|       break; | ||||
| 
 | ||||
|     PendingFrame& pf = m_pending_frames[m_frames_encode_consume_pos]; | ||||
|     DebugAssert(pf.state == PendingFrame::State::NeedsEncoding); | ||||
|     DebugAssert(!IsCapturingVideo() || pf.state == PendingFrame::State::NeedsEncoding); | ||||
| 
 | ||||
|     lock.unlock(); | ||||
| 
 | ||||
|  | @ -684,19 +685,22 @@ private: | |||
| static std::once_flag s_media_foundation_initialized_flag; | ||||
| static HRESULT s_media_foundation_initialized = S_OK; | ||||
| 
 | ||||
| struct MediaFoundationCodec | ||||
| struct MediaFoundationVideoCodec | ||||
| { | ||||
|   const char* name; | ||||
|   const char* display_name; | ||||
|   const GUID& guid; | ||||
|   bool require_hardware; | ||||
| }; | ||||
| static constexpr const MediaFoundationCodec s_media_foundation_audio_codecs[] = { | ||||
|   {"aac", "Advanced Audio Coding", MFAudioFormat_AAC, false}, | ||||
|   {"mp3", "MPEG-2 Audio Layer III", MFAudioFormat_MP3, false}, | ||||
|   {"pcm", "Uncompressed PCM", MFAudioFormat_PCM, false}, | ||||
| struct MediaFoundationAudioCodec | ||||
| { | ||||
|   const char* name; | ||||
|   const char* display_name; | ||||
|   const GUID& guid; | ||||
|   u32 min_bitrate; | ||||
|   u32 max_bitrate; | ||||
| }; | ||||
| static constexpr const MediaFoundationCodec s_media_foundation_video_codecs[] = { | ||||
| static constexpr const MediaFoundationVideoCodec s_media_foundation_video_codecs[] = { | ||||
|   {"h264", "H.264 with Software Encoding", MFVideoFormat_H264, false}, | ||||
|   {"h264_hw", "H.264 with Hardware Encoding", MFVideoFormat_H264, true}, | ||||
|   {"h265", "H.265 with Software Encoding", MFVideoFormat_H265, false}, | ||||
|  | @ -708,6 +712,11 @@ static constexpr const MediaFoundationCodec s_media_foundation_video_codecs[] = | |||
|   {"av1", "AV1 with Software Encoding", MFVideoFormat_AV1, false}, | ||||
|   {"av1_hw", "AV1 with Hardware Encoding", MFVideoFormat_AV1, false}, | ||||
| }; | ||||
| static constexpr const MediaFoundationAudioCodec s_media_foundation_audio_codecs[] = { | ||||
|   {"aac", "Advanced Audio Coding", MFAudioFormat_AAC, 64, 224}, | ||||
|   {"mp3", "MPEG-2 Audio Layer III", MFAudioFormat_MP3, 64, 320}, | ||||
|   {"pcm", "Uncompressed PCM", MFAudioFormat_PCM, 0, std::numeric_limits<u32>::max()}, | ||||
| }; | ||||
| 
 | ||||
| static bool InitializeMediaFoundation(Error* error) | ||||
| { | ||||
|  | @ -738,8 +747,9 @@ std::unique_ptr<MediaCapture> MediaCaptureMF::Create(Error* error) | |||
| MediaCapture::ContainerList MediaCaptureMF::GetContainerList() | ||||
| { | ||||
|   return { | ||||
|     {"avi", "Audio Video Interleave"},     {"mp4", "MPEG-4 Part 14"}, | ||||
|     {"mkv", "Matroska Media Container"},   {"mp3", "MPEG-2 Audio Layer III"}, | ||||
|     {"avi", "Audio Video Interleave"}, | ||||
|     {"mp4", "MPEG-4 Part 14"}, | ||||
|     {"mp3", "MPEG-2 Audio Layer III"}, | ||||
|     {"wav", "Waveform Audio File Format"}, | ||||
|   }; | ||||
| } | ||||
|  | @ -748,7 +758,7 @@ MediaCapture::ContainerList MediaCaptureMF::GetAudioCodecList(const char* contai | |||
| { | ||||
|   ContainerList ret; | ||||
|   ret.reserve(std::size(s_media_foundation_audio_codecs)); | ||||
|   for (const MediaFoundationCodec& codec : s_media_foundation_audio_codecs) | ||||
|   for (const MediaFoundationAudioCodec& codec : s_media_foundation_audio_codecs) | ||||
|     ret.emplace_back(codec.name, codec.display_name); | ||||
|   return ret; | ||||
| } | ||||
|  | @ -757,7 +767,7 @@ MediaCapture::ContainerList MediaCaptureMF::GetVideoCodecList(const char* contai | |||
| { | ||||
|   ContainerList ret; | ||||
|   ret.reserve(std::size(s_media_foundation_video_codecs)); | ||||
|   for (const MediaFoundationCodec& codec : s_media_foundation_video_codecs) | ||||
|   for (const MediaFoundationVideoCodec& codec : s_media_foundation_video_codecs) | ||||
|     ret.emplace_back(codec.name, codec.display_name); | ||||
|   return ret; | ||||
| } | ||||
|  | @ -848,8 +858,9 @@ bool MediaCaptureMF::InternalBeginCapture(float fps, float aspect, u32 sample_ra | |||
|       Error::SetHResult(error, "Audio AddStream() failed: ", hr); | ||||
|     } | ||||
| 
 | ||||
|     if (SUCCEEDED(hr) && FAILED(hr = m_sink_writer->SetInputMediaType(m_audio_stream_index, audio_input_type.Get(), | ||||
|                                                                       nullptr))) [[unlikely]] | ||||
|     if (SUCCEEDED(hr) && audio_input_type && | ||||
|         FAILED(hr = m_sink_writer->SetInputMediaType(m_audio_stream_index, audio_input_type.Get(), nullptr))) | ||||
|       [[unlikely]] | ||||
|     { | ||||
|       Error::SetHResult(error, "Audio SetInputMediaType() failed: ", hr); | ||||
|     } | ||||
|  | @ -864,8 +875,11 @@ bool MediaCaptureMF::InternalBeginCapture(float fps, float aspect, u32 sample_ra | |||
|       Error::SetHResult(error, "Getting video encode event generator failed: ", hr); | ||||
|   } | ||||
| 
 | ||||
|   if (SUCCEEDED(hr) && FAILED(hr = m_video_encode_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0))) | ||||
|   if (capture_video && SUCCEEDED(hr) && | ||||
|       FAILED(hr = m_video_encode_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0))) | ||||
|   { | ||||
|     Error::SetHResult(error, "MFT_MESSAGE_NOTIFY_START_OF_STREAM failed: ", hr); | ||||
|   } | ||||
| 
 | ||||
|   if (FAILED(hr)) | ||||
|   { | ||||
|  | @ -986,7 +1000,7 @@ MediaCaptureMF::ComPtr<IMFTransform> MediaCaptureMF::CreateVideoEncodeTransform( | |||
|   if (!codec.empty()) | ||||
|   { | ||||
|     bool found = false; | ||||
|     for (const MediaFoundationCodec& tcodec : s_media_foundation_video_codecs) | ||||
|     for (const MediaFoundationVideoCodec& tcodec : s_media_foundation_video_codecs) | ||||
|     { | ||||
|       if (StringUtil::EqualNoCase(codec, tcodec.name)) | ||||
|       { | ||||
|  | @ -1508,23 +1522,16 @@ bool MediaCaptureMF::ProcessVideoEvents(Error* error) | |||
| bool MediaCaptureMF::GetAudioTypes(std::string_view codec, ComPtr<IMFMediaType>* input_type, | ||||
|                                    ComPtr<IMFMediaType>* output_type, u32 sample_rate, u32 bitrate, Error* error) | ||||
| { | ||||
|   HRESULT hr; | ||||
|   if (FAILED(hr = MFCreateMediaType(input_type->GetAddressOf())) || | ||||
|       FAILED(hr = MFCreateMediaType(output_type->GetAddressOf()))) [[unlikely]] | ||||
|   { | ||||
|     Error::SetHResult(error, "Audio MFCreateMediaType() failed: ", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   GUID output_subtype = MFAudioFormat_AAC; | ||||
|   if (!codec.empty()) | ||||
|   { | ||||
|     bool found = false; | ||||
|     for (const MediaFoundationCodec& tcodec : s_media_foundation_audio_codecs) | ||||
|     for (const MediaFoundationAudioCodec& tcodec : s_media_foundation_audio_codecs) | ||||
|     { | ||||
|       if (StringUtil::EqualNoCase(codec, tcodec.name)) | ||||
|       { | ||||
|         output_subtype = tcodec.guid; | ||||
|         bitrate = std::clamp(bitrate, tcodec.min_bitrate, tcodec.max_bitrate); | ||||
|         found = true; | ||||
|         break; | ||||
|       } | ||||
|  | @ -1536,23 +1543,87 @@ bool MediaCaptureMF::GetAudioTypes(std::string_view codec, ComPtr<IMFMediaType>* | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   HRESULT hr; | ||||
|   if (FAILED(hr = MFCreateMediaType(input_type->GetAddressOf()))) [[unlikely]] | ||||
|   { | ||||
|     Error::SetHResult(error, "Audio MFCreateMediaType() failed: ", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   const u32 block_align = AUDIO_CHANNELS * (AUDIO_BITS_PER_SAMPLE / 8); | ||||
|   const u32 bytes_per_second = block_align * sample_rate; | ||||
| 
 | ||||
|   if (FAILED(hr = (*input_type)->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)) || | ||||
|       FAILED(hr = (*input_type)->SetGUID(MF_MT_SUBTYPE, AUDIO_INPUT_MEDIA_FORMAT)) || | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, AUDIO_CHANNELS)) || | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, AUDIO_BITS_PER_SAMPLE)) || | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sample_rate)) || | ||||
| 
 | ||||
|       FAILED(hr = (*output_type)->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)) || | ||||
|       FAILED(hr = (*output_type)->SetGUID(MF_MT_SUBTYPE, output_subtype)) || | ||||
|       FAILED(hr = (*output_type)->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, AUDIO_CHANNELS)) || | ||||
|       FAILED(hr = (*output_type)->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, AUDIO_BITS_PER_SAMPLE)) || | ||||
|       FAILED(hr = (*output_type)->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sample_rate)) || | ||||
|       FAILED(hr = (*output_type)->SetUINT32(MF_MT_AVG_BITRATE, bitrate * 1000))) [[unlikely]] | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, block_align)) || | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytes_per_second)) || | ||||
|       FAILED(hr = (*input_type)->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE))) [[unlikely]] | ||||
|   { | ||||
|     Error::SetHResult(error, "Audio setting attributes failed: ", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // If our input type is PCM, no need for an input type, it's the same as output.
 | ||||
|   if (output_subtype == AUDIO_INPUT_MEDIA_FORMAT) | ||||
|   { | ||||
|     *output_type = std::move(*input_type); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   ComPtr<IMFCollection> output_types_collection; | ||||
|   DWORD output_types_collection_size = 0; | ||||
|   hr = MFTranscodeGetAudioOutputAvailableTypes(output_subtype, 0, nullptr, output_types_collection.GetAddressOf()); | ||||
|   if (FAILED(hr) || FAILED(hr = output_types_collection->GetElementCount(&output_types_collection_size))) [[unlikely]] | ||||
|   { | ||||
|     Error::SetHResult(error, "MFTranscodeGetAudioOutputAvailableTypes() failed: ", hr); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   std::vector<std::pair<ComPtr<IMFMediaType>, u32>> output_types; | ||||
|   for (DWORD i = 0; i < output_types_collection_size; i++) | ||||
|   { | ||||
|     ComPtr<IUnknown> current_output_type; | ||||
|     ComPtr<IMFMediaType> current_output_type_c; | ||||
|     if (SUCCEEDED(hr = output_types_collection->GetElement(i, current_output_type.GetAddressOf())) && | ||||
|         SUCCEEDED(current_output_type.As(¤t_output_type_c))) | ||||
|     { | ||||
|       UINT32 current_channel_count, current_sample_rate; | ||||
|       if (SUCCEEDED(current_output_type_c->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, ¤t_channel_count)) && | ||||
|           SUCCEEDED(current_output_type_c->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, ¤t_sample_rate)) && | ||||
|           current_channel_count == AUDIO_CHANNELS && current_sample_rate == sample_rate) | ||||
|       { | ||||
|         u32 current_bitrate; | ||||
|         if (SUCCEEDED(current_output_type_c->GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, ¤t_bitrate))) | ||||
|           current_bitrate *= 8; | ||||
|         else if (FAILED(current_output_type_c->GetUINT32(MF_MT_AVG_BITRATE, ¤t_bitrate))) | ||||
|           continue; | ||||
| 
 | ||||
|         output_types.emplace_back(std::move(current_output_type_c), current_bitrate); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // pick the closest bitrate
 | ||||
|   const u32 bitrate_kbps = bitrate * 1000; | ||||
|   std::pair<ComPtr<IMFMediaType>, u32>* selected_output_type = nullptr; | ||||
|   for (auto it = output_types.begin(); it != output_types.end(); ++it) | ||||
|   { | ||||
|     if (it->second >= bitrate_kbps && | ||||
|         (!selected_output_type || (selected_output_type->second - bitrate_kbps) > (it->second - bitrate_kbps))) | ||||
|     { | ||||
|       selected_output_type = &(*it); | ||||
|     } | ||||
|   } | ||||
|   if (!selected_output_type) | ||||
|   { | ||||
|     Error::SetStringView(error, "Unable to find a matching audio output type."); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   *output_type = std::move(selected_output_type->first); | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Stenzek
						Stenzek