mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
7f6e8b96b9
Report that we can play MP3 inside MP4 on Windows Vista and later in HTMLMediaElement.canPlayType. Chrome and IE on Windows match this behaviour. Add a test file with MP3 contained inside MP4. Note the B2G emulator can't play this file, so I added a codecs parameter to the file's mime type so that decoder backends have to opt-in to testing with it. Enable playback of MP3 inside MP4 in WMFReader. Change from reporting the IMFSourceReader's duration inside WMFReader, to instead report the IMFSourceReader's duration as the media "end time". This is needed because the MP3-contained-in-MP4 file's first sample output by the IMFSourceReader has a non-zero timestamp, and the MediaDecoderStateMachine assumes that the media samples will be in the range [$firstSampleStartTime, $firstSampleStartTime+$reportedDuration]. But that's not the case here, the IMFSourceReader seems to output samples in the range [0,$reportedDuration]. This assumption mismatch means on the MP3-contained-in-MP4 file we end up trying to seek after what the IMFSourceReader assumes is the end of the file, which fails and causes test failures.
164 lines
5.2 KiB
C++
164 lines
5.2 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "WMF.h"
|
|
#include "WMFDecoder.h"
|
|
#include "WMFReader.h"
|
|
#include "WMFUtils.h"
|
|
#include "MediaDecoderStateMachine.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/WindowsVersion.h"
|
|
#include "nsCharSeparatedTokenizer.h"
|
|
|
|
#ifdef MOZ_DIRECTSHOW
|
|
#include "DirectShowDecoder.h"
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
MediaDecoderStateMachine* WMFDecoder::CreateStateMachine()
|
|
{
|
|
return new MediaDecoderStateMachine(this, new WMFReader(this));
|
|
}
|
|
|
|
/* static */
|
|
bool
|
|
WMFDecoder::IsMP3Supported()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
|
if (!MediaDecoder::IsWMFEnabled()) {
|
|
return false;
|
|
}
|
|
// MP3 works fine in WMF on Windows Vista and Windows 8.
|
|
if (!IsWin7OrLater()) {
|
|
return true;
|
|
}
|
|
// MP3 support is disabled if we're on Windows 7 and no service packs are
|
|
// installed, since it's crashy. Win7 with service packs is not so crashy,
|
|
// so we enable it there. Note we prefer DirectShow for MP3 playback, but
|
|
// we still support MP3 in MP4 via WMF, or MP3 in WMF if DirectShow is
|
|
// disabled.
|
|
return IsWin7SP1OrLater();
|
|
}
|
|
|
|
static bool
|
|
IsSupportedH264Codec(const nsAString& aCodec)
|
|
{
|
|
// According to the WMF documentation:
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
|
|
// "The Media Foundation H.264 video decoder is a Media Foundation Transform
|
|
// that supports decoding of Baseline, Main, and High profiles, up to level
|
|
// 5.1.". We also report that we can play Extended profile, as there are
|
|
// bitstreams that are Extended compliant that are also Baseline compliant.
|
|
|
|
// H.264 codecs parameters have a type defined as avc1.PPCCLL, where
|
|
// PP = profile_idc, CC = constraint_set flags, LL = level_idc.
|
|
// We ignore the constraint_set flags, as it's not clear from the WMF
|
|
// documentation what constraints the WMF H.264 decoder supports.
|
|
// See http://blog.pearce.org.nz/2013/11/what-does-h264avc1-codecs-parameters.html
|
|
// for more details.
|
|
if (aCodec.Length() != strlen("avc1.PPCCLL")) {
|
|
return false;
|
|
}
|
|
|
|
// Verify the codec starts with "avc1.".
|
|
const nsAString& sample = Substring(aCodec, 0, 5);
|
|
if (!sample.EqualsASCII("avc1.")) {
|
|
return false;
|
|
}
|
|
|
|
// Extract the profile_idc and level_idc. Note: the constraint_set flags
|
|
// are ignored, it's not clear from the WMF documentation if they make a
|
|
// difference.
|
|
nsresult rv = NS_OK;
|
|
const int32_t profile = PromiseFlatString(Substring(aCodec, 5, 2)).ToInteger(&rv, 16);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
const int32_t level = PromiseFlatString(Substring(aCodec, 9, 2)).ToInteger(&rv, 16);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
return level >= eAVEncH264VLevel1 &&
|
|
level <= eAVEncH264VLevel5_1 &&
|
|
(profile == eAVEncH264VProfile_Base ||
|
|
profile == eAVEncH264VProfile_Main ||
|
|
profile == eAVEncH264VProfile_Extended ||
|
|
profile == eAVEncH264VProfile_High);
|
|
}
|
|
|
|
bool
|
|
WMFDecoder::CanPlayType(const nsACString& aType,
|
|
const nsAString& aCodecs)
|
|
{
|
|
if (!MediaDecoder::IsWMFEnabled() ||
|
|
NS_FAILED(LoadDLLs())) {
|
|
return false;
|
|
}
|
|
|
|
// Assume that if LoadDLLs() didn't fail, we can playback the types that
|
|
// we know should be supported by Windows Media Foundation.
|
|
if ((aType.EqualsASCII("audio/mpeg") || aType.EqualsASCII("audio/mp3")) &&
|
|
IsMP3Supported()) {
|
|
// Note: We block MP3 playback on Window 7 SP0 since it seems to crash
|
|
// in some circumstances.
|
|
return !aCodecs.Length() || aCodecs.EqualsASCII("mp3");
|
|
}
|
|
|
|
// AAC-LC or MP3 in M4A.
|
|
if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
|
|
return !aCodecs.Length() ||
|
|
aCodecs.EqualsASCII("mp4a.40.2") ||
|
|
aCodecs.EqualsASCII("mp3");
|
|
}
|
|
|
|
if (!aType.EqualsASCII("video/mp4")) {
|
|
return false;
|
|
}
|
|
|
|
// H.264 + AAC in MP4. Verify that all the codecs specifed are ones that
|
|
// we expect that we can play.
|
|
nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
|
|
bool expectMoreTokens = false;
|
|
while (tokenizer.hasMoreTokens()) {
|
|
const nsSubstring& token = tokenizer.nextToken();
|
|
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
|
|
if (token.EqualsASCII("mp4a.40.2") || // AAC-LC
|
|
token.EqualsASCII("mp3") ||
|
|
IsSupportedH264Codec(token)) {
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
if (expectMoreTokens) {
|
|
// Last codec name was empty
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
WMFDecoder::LoadDLLs()
|
|
{
|
|
return SUCCEEDED(wmf::LoadDLLs()) ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
void
|
|
WMFDecoder::UnloadDLLs()
|
|
{
|
|
wmf::UnloadDLLs();
|
|
}
|
|
|
|
/* static */
|
|
bool
|
|
WMFDecoder::IsEnabled()
|
|
{
|
|
// We only use WMF on Windows Vista and up
|
|
return IsVistaOrLater() &&
|
|
Preferences::GetBool("media.windows-media-foundation.enabled");
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|