michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include michael@0: #else michael@0: #include michael@0: #endif michael@0: #include michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Types.h" michael@0: #include "MPAPI.h" michael@0: michael@0: #include "android/log.h" michael@0: michael@0: #define MAX_DECODER_NAME_LEN 256 michael@0: #define AVC_MIME_TYPE "video/avc" michael@0: michael@0: #if !defined(MOZ_ANDROID_FROYO) michael@0: #define DEFAULT_STAGEFRIGHT_FLAGS OMXCodec::kClientNeedsFramebuffer michael@0: #else michael@0: #define DEFAULT_STAGEFRIGHT_FLAGS 0 michael@0: #endif michael@0: michael@0: #undef LOG michael@0: #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args) michael@0: michael@0: #if defined(MOZ_ANDROID_FROYO) || defined(MOZ_ANDROID_GB) michael@0: // Android versions 2.x.x have common API differences michael@0: #define MOZ_ANDROID_V2_X_X michael@0: #endif michael@0: michael@0: #if !defined(MOZ_ANDROID_V2_X_X) && !defined(MOZ_ANDROID_HC) michael@0: #define MOZ_ANDROID_V4_OR_ABOVE michael@0: #endif michael@0: michael@0: #if defined(MOZ_ANDROID_V4_OR_ABOVE) michael@0: #include michael@0: #endif michael@0: michael@0: using namespace MPAPI; michael@0: michael@0: #if !defined(MOZ_STAGEFRIGHT_OFF_T) michael@0: #define MOZ_STAGEFRIGHT_OFF_T off64_t michael@0: #endif michael@0: michael@0: using namespace android; michael@0: michael@0: namespace OmxPlugin { michael@0: michael@0: const int OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka = 0x7FA30C01; michael@0: const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; michael@0: const int OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100; michael@0: michael@0: class OmxDecoder { michael@0: PluginHost *mPluginHost; michael@0: Decoder *mDecoder; michael@0: sp mVideoTrack; michael@0: sp mVideoSource; michael@0: sp mAudioTrack; michael@0: sp mAudioSource; michael@0: int32_t mVideoWidth; michael@0: int32_t mVideoHeight; michael@0: int32_t mVideoColorFormat; michael@0: int32_t mVideoStride; michael@0: int32_t mVideoSliceHeight; michael@0: int32_t mVideoCropLeft; michael@0: int32_t mVideoCropTop; michael@0: int32_t mVideoCropRight; michael@0: int32_t mVideoCropBottom; michael@0: int32_t mVideoRotation; michael@0: int32_t mAudioChannels; michael@0: int32_t mAudioSampleRate; michael@0: int64_t mDurationUs; michael@0: MediaBuffer *mVideoBuffer; michael@0: VideoFrame mVideoFrame; michael@0: MediaBuffer *mAudioBuffer; michael@0: AudioFrame mAudioFrame; michael@0: ColorConverter *mColorConverter; michael@0: michael@0: // 'true' if a read from the audio stream was done while reading the metadata michael@0: bool mAudioMetadataRead; michael@0: michael@0: void ReleaseVideoBuffer(); michael@0: void ReleaseAudioBuffer(); michael@0: michael@0: void ToVideoFrame_YUV420Planar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: void ToVideoFrame_CbYCrY(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: void ToVideoFrame_YUV420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: void ToVideoFrame_YVU420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: void ToVideoFrame_YUV420PackedSemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: void ToVideoFrame_YVU420PackedSemiPlanar32m4ka(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); michael@0: bool ToVideoFrame_RGB565(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback); michael@0: bool ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback); michael@0: bool ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback); michael@0: bool ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback); michael@0: bool ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, michael@0: int32_t aAudioChannels, int32_t aAudioSampleRate); michael@0: public: michael@0: OmxDecoder(PluginHost *aPluginHost, Decoder *aDecoder); michael@0: ~OmxDecoder(); michael@0: michael@0: bool Init(); michael@0: bool SetVideoFormat(); michael@0: bool SetAudioFormat(); michael@0: michael@0: void GetDuration(int64_t *durationUs) { michael@0: *durationUs = mDurationUs; michael@0: } michael@0: michael@0: void GetVideoParameters(int32_t *width, int32_t *height) { michael@0: *width = mVideoWidth; michael@0: *height = mVideoHeight; michael@0: } michael@0: michael@0: void GetAudioParameters(int32_t *numChannels, int32_t *sampleRate) { michael@0: *numChannels = mAudioChannels; michael@0: *sampleRate = mAudioSampleRate; michael@0: } michael@0: michael@0: bool HasVideo() { michael@0: return mVideoSource != nullptr; michael@0: } michael@0: michael@0: bool HasAudio() { michael@0: return mAudioSource != nullptr; michael@0: } michael@0: michael@0: bool ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs, BufferCallback *aBufferCallback); michael@0: bool ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs); michael@0: }; michael@0: michael@0: #if !defined(MOZ_WIDGET_GONK) michael@0: static class OmxClientInstance { michael@0: public: michael@0: OmxClientInstance() michael@0: : mClient(new OMXClient()) michael@0: , mStatus(mClient->connect()) michael@0: { michael@0: } michael@0: michael@0: status_t IsValid() michael@0: { michael@0: return mStatus == OK; michael@0: } michael@0: michael@0: OMXClient *get() michael@0: { michael@0: return mClient; michael@0: } michael@0: michael@0: ~OmxClientInstance() michael@0: { michael@0: if (mStatus == OK) { michael@0: mClient->disconnect(); michael@0: } michael@0: delete mClient; michael@0: } michael@0: michael@0: private: michael@0: OMXClient *mClient; michael@0: status_t mStatus; michael@0: } sClientInstance; michael@0: #endif michael@0: michael@0: OmxDecoder::OmxDecoder(PluginHost *aPluginHost, Decoder *aDecoder) : michael@0: mPluginHost(aPluginHost), michael@0: mDecoder(aDecoder), michael@0: mVideoWidth(0), michael@0: mVideoHeight(0), michael@0: mVideoColorFormat(0), michael@0: mVideoStride(0), michael@0: mVideoSliceHeight(0), michael@0: mVideoCropLeft(0), michael@0: mVideoCropTop(0), michael@0: mVideoCropRight(0), michael@0: mVideoCropBottom(0), michael@0: mVideoRotation(0), michael@0: mAudioChannels(-1), michael@0: mAudioSampleRate(-1), michael@0: mDurationUs(-1), michael@0: mVideoBuffer(nullptr), michael@0: mAudioBuffer(nullptr), michael@0: mColorConverter(nullptr), michael@0: mAudioMetadataRead(false) michael@0: { michael@0: } michael@0: michael@0: OmxDecoder::~OmxDecoder() michael@0: { michael@0: ReleaseVideoBuffer(); michael@0: ReleaseAudioBuffer(); michael@0: michael@0: if (mVideoSource.get()) { michael@0: mVideoSource->stop(); michael@0: } michael@0: michael@0: if (mAudioSource.get()) { michael@0: mAudioSource->stop(); michael@0: } michael@0: michael@0: #ifndef MOZ_ANDROID_HC michael@0: if (mColorConverter) { michael@0: delete mColorConverter; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: class AutoStopMediaSource { michael@0: sp mMediaSource; michael@0: public: michael@0: AutoStopMediaSource(sp aMediaSource) : mMediaSource(aMediaSource) { michael@0: } michael@0: michael@0: ~AutoStopMediaSource() { michael@0: mMediaSource->stop(); michael@0: } michael@0: }; michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: static sp sOMX = nullptr; michael@0: static sp GetOMX() { michael@0: if(sOMX.get() == nullptr) { michael@0: sOMX = reinterpret_cast(new OMX); michael@0: } michael@0: return sOMX; michael@0: } michael@0: #endif michael@0: michael@0: static uint32_t michael@0: GetDefaultStagefrightFlags(PluginHost *aPluginHost) michael@0: { michael@0: uint32_t flags = DEFAULT_STAGEFRIGHT_FLAGS; michael@0: michael@0: #if !defined(MOZ_ANDROID_FROYO) michael@0: michael@0: char hardware[256] = ""; michael@0: aPluginHost->GetSystemInfoString("hardware", hardware, sizeof(hardware)); michael@0: michael@0: if (!strcmp("qcom", hardware)) { michael@0: // Qualcomm's OMXCodec implementation interprets this flag to mean that we michael@0: // only want a thumbnail and therefore only need one frame. After the first michael@0: // frame it returns EOS. michael@0: // All other OMXCodec implementations seen so far interpret this flag michael@0: // sanely; some do not return full framebuffers unless this flag is passed. michael@0: flags &= ~OMXCodec::kClientNeedsFramebuffer; michael@0: } michael@0: michael@0: LOG("Hardware %s; using default flags %#x\n", hardware, flags); michael@0: michael@0: #endif michael@0: michael@0: return flags; michael@0: } michael@0: michael@0: static uint32_t GetVideoCreationFlags(PluginHost* aPluginHost) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // Flag value of zero means return a hardware or software decoder michael@0: // depending on what the device supports. michael@0: return 0; michael@0: #else michael@0: // Check whether the user has set a pref to override our default OMXCodec michael@0: // CreationFlags flags. This is useful for A/B testing hardware and software michael@0: // decoders for performance and bugs. The interesting flag values are: michael@0: // 0 = Let Stagefright choose hardware or software decoding (default) michael@0: // 8 = Force software decoding michael@0: // 16 = Force hardware decoding michael@0: int32_t flags = 0; michael@0: aPluginHost->GetIntPref("media.stagefright.omxcodec.flags", &flags); michael@0: if (flags != 0) { michael@0: #if !defined(MOZ_ANDROID_V2_X_X) michael@0: LOG("media.stagefright.omxcodec.flags=%d", flags); michael@0: if ((flags & OMXCodec::kHardwareCodecsOnly) != 0) { michael@0: LOG("FORCE HARDWARE DECODING"); michael@0: } else if ((flags & OMXCodec::kSoftwareCodecsOnly) != 0) { michael@0: LOG("FORCE SOFTWARE DECODING"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: flags |= GetDefaultStagefrightFlags(aPluginHost); michael@0: michael@0: return static_cast(flags); michael@0: #endif michael@0: } michael@0: michael@0: enum ColorFormatSupport { michael@0: ColorFormatNotSupported = 0, michael@0: ColorFormatSupportOK, michael@0: ColorFormatSupportPreferred, michael@0: }; michael@0: michael@0: static ColorFormatSupport michael@0: IsColorFormatSupported(OMX_COLOR_FORMATTYPE aColorFormat) michael@0: { michael@0: switch (static_cast(aColorFormat)) { michael@0: case OMX_COLOR_FormatCbYCrY: michael@0: case OMX_COLOR_FormatYUV420Planar: michael@0: case OMX_COLOR_FormatYUV420SemiPlanar: michael@0: case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka: michael@0: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: michael@0: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: michael@0: LOG("Colour format %#x supported natively.", aColorFormat); michael@0: // Prefer natively supported colour formats over formats that need another michael@0: // slow software conversion. michael@0: return ColorFormatSupportPreferred; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: // These formats are okay if we can't find a better one; Android provides a michael@0: // software conversion to a sane colour format. michael@0: #if !defined(MOZ_ANDROID_HC) michael@0: if (ColorConverter(aColorFormat, OMX_COLOR_Format16bitRGB565).isValid()) { michael@0: LOG("Colour format %#x supported by Android ColorConverter.", aColorFormat); michael@0: return ColorFormatSupportOK; michael@0: } michael@0: #endif michael@0: michael@0: #if defined(MOZ_ANDROID_V4_OR_ABOVE) michael@0: I420ColorConverter yuvConverter; michael@0: michael@0: if (yuvConverter.isLoaded() && michael@0: yuvConverter.getDecoderOutputFormat() == aColorFormat) { michael@0: LOG("Colour format %#x supported by Android I420ColorConverter.", aColorFormat); michael@0: return ColorFormatSupportOK; michael@0: } michael@0: #endif michael@0: michael@0: return ColorFormatNotSupported; michael@0: } michael@0: michael@0: #if defined(MOZ_ANDROID_KK) michael@0: /** michael@0: * Look for a decoder that supports a colour format that we support. michael@0: */ michael@0: static bool michael@0: FindPreferredDecoderAndColorFormat(const sp& aOmx, michael@0: char *aDecoderName, michael@0: size_t aDecoderLen, michael@0: OMX_COLOR_FORMATTYPE *aColorFormat) michael@0: { michael@0: Vector codecs; michael@0: michael@0: // Get all AVC decoder/colour format pairs that this device supports. michael@0: QueryCodecs(aOmx, AVC_MIME_TYPE, true /* queryDecoders */, &codecs); michael@0: michael@0: // We assume that faster (hardware accelerated) decoders come first in the michael@0: // list, so we choose the first decoder with a colour format we can use. michael@0: for (uint32_t i = 0; i < codecs.size(); i++) { michael@0: const CodecCapabilities &caps = codecs[i]; michael@0: const Vector &colors = caps.mColorFormats; michael@0: michael@0: bool found = false; michael@0: for (uint32_t j = 0; j < colors.size(); j++) { michael@0: OMX_COLOR_FORMATTYPE color = (OMX_COLOR_FORMATTYPE)colors[j]; michael@0: michael@0: LOG("Decoder %s can output colour format %#x.\n", michael@0: caps.mComponentName.string(), color); michael@0: michael@0: ColorFormatSupport supported = IsColorFormatSupported(color); michael@0: michael@0: if (supported) { michael@0: strncpy(aDecoderName, caps.mComponentName.string(), aDecoderLen); michael@0: *aColorFormat = color; michael@0: found = true; michael@0: } michael@0: michael@0: if (supported == ColorFormatSupportPreferred) { michael@0: // The colour format is natively supported -- that's as good as we're michael@0: // going to get. michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (found) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: static sp CreateVideoSource(PluginHost* aPluginHost, michael@0: const sp& aOmx, michael@0: const sp& aVideoTrack) michael@0: { michael@0: uint32_t flags = GetVideoCreationFlags(aPluginHost); michael@0: michael@0: char decoderName[MAX_DECODER_NAME_LEN] = ""; michael@0: sp videoFormat = aVideoTrack->getFormat(); michael@0: michael@0: #if defined(MOZ_ANDROID_KK) michael@0: OMX_COLOR_FORMATTYPE colorFormat = (OMX_COLOR_FORMATTYPE)0; michael@0: if (FindPreferredDecoderAndColorFormat(aOmx, michael@0: decoderName, sizeof(decoderName), michael@0: &colorFormat)) { michael@0: // We found a colour format that we can handle. Tell OMXCodec to use it in michael@0: // case it isn't the default. michael@0: videoFormat->setInt32(kKeyColorFormat, colorFormat); michael@0: michael@0: LOG("Found compatible decoder %s with colour format %#x.\n", michael@0: decoderName, colorFormat); michael@0: } michael@0: #endif michael@0: michael@0: if (flags == DEFAULT_STAGEFRIGHT_FLAGS) { michael@0: // Let Stagefright choose hardware or software decoder. michael@0: sp videoSource = OMXCodec::Create(aOmx, videoFormat, michael@0: false, aVideoTrack, michael@0: decoderName[0] ? decoderName : nullptr, michael@0: flags); michael@0: if (videoSource == nullptr) michael@0: return nullptr; michael@0: michael@0: // Now that OMXCodec has parsed the video's AVCDecoderConfigurationRecord, michael@0: // check whether we know how to decode this video. michael@0: int32_t videoColorFormat; michael@0: if (videoSource->getFormat()->findInt32(kKeyColorFormat, &videoColorFormat)) { michael@0: michael@0: if (IsColorFormatSupported((OMX_COLOR_FORMATTYPE)videoColorFormat)) { michael@0: return videoSource; michael@0: } michael@0: michael@0: // We need to implement a ToVideoFrame_*() color conversion michael@0: // function for this video color format. michael@0: LOG("Unknown video color format: %#x", videoColorFormat); michael@0: } else { michael@0: LOG("Video color format not found"); michael@0: } michael@0: michael@0: // Throw away the videoSource and try again with new flags. michael@0: LOG("Falling back to software decoder"); michael@0: videoSource.clear(); michael@0: #if defined(MOZ_ANDROID_V2_X_X) michael@0: flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kPreferSoftwareCodecs; michael@0: #else michael@0: flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kSoftwareCodecsOnly; michael@0: #endif michael@0: } michael@0: michael@0: MOZ_ASSERT(flags != DEFAULT_STAGEFRIGHT_FLAGS); michael@0: return OMXCodec::Create(aOmx, aVideoTrack->getFormat(), false, aVideoTrack, michael@0: nullptr, flags); michael@0: } michael@0: michael@0: bool OmxDecoder::Init() michael@0: { michael@0: #if defined(MOZ_WIDGET_ANDROID) michael@0: // OMXClient::connect() always returns OK and aborts fatally if michael@0: // it can't connect. We may need to implement the connect functionality michael@0: // ourselves if this proves to be an issue. michael@0: if (!sClientInstance.IsValid()) { michael@0: LOG("OMXClient failed to connect"); michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: //register sniffers, if they are not registered in this process. michael@0: DataSource::RegisterDefaultSniffers(); michael@0: michael@0: sp dataSource = michael@0: DataSource::CreateFromURI(static_cast(mDecoder->mResource)); michael@0: if (!dataSource.get() || dataSource->initCheck()) { michael@0: return false; michael@0: } michael@0: michael@0: sp extractor = MediaExtractor::Create(dataSource); michael@0: if (extractor == nullptr) { michael@0: return false; michael@0: } michael@0: michael@0: ssize_t audioTrackIndex = -1; michael@0: ssize_t videoTrackIndex = -1; michael@0: const char *audioMime = nullptr; michael@0: const char *videoMime = nullptr; michael@0: michael@0: for (size_t i = 0; i < extractor->countTracks(); ++i) { michael@0: sp meta = extractor->getTrackMetaData(i); michael@0: michael@0: const char *mime; michael@0: if (!meta->findCString(kKeyMIMEType, &mime)) { michael@0: continue; michael@0: } michael@0: michael@0: if (videoTrackIndex == -1 && !strncasecmp(mime, "video/", 6)) { michael@0: videoTrackIndex = i; michael@0: videoMime = mime; michael@0: } else if (audioTrackIndex == -1 && !strncasecmp(mime, "audio/", 6)) { michael@0: audioTrackIndex = i; michael@0: audioMime = mime; michael@0: } michael@0: } michael@0: michael@0: if (videoTrackIndex == -1 && audioTrackIndex == -1) { michael@0: return false; michael@0: } michael@0: michael@0: int64_t totalDurationUs = 0; michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: sp omx = GetOMX(); michael@0: #else michael@0: sp omx = sClientInstance.get()->interface(); michael@0: #endif michael@0: michael@0: sp videoTrack; michael@0: sp videoSource; michael@0: if (videoTrackIndex != -1 && (videoTrack = extractor->getTrack(videoTrackIndex)) != nullptr) { michael@0: #if defined(MOZ_ANDROID_FROYO) michael@0: // Allow up to 720P video. michael@0: sp meta = extractor->getTrackMetaData(videoTrackIndex); michael@0: meta->setInt32(kKeyMaxInputSize, (1280 * 720 * 3) / 2); michael@0: #endif michael@0: videoSource = CreateVideoSource(mPluginHost, omx, videoTrack); michael@0: if (videoSource == nullptr) { michael@0: LOG("OMXCodec failed to initialize video decoder for \"%s\"", videoMime); michael@0: return false; michael@0: } michael@0: status_t status = videoSource->start(); michael@0: if (status != OK) { michael@0: LOG("videoSource->start() failed with status %#x", status); michael@0: return false; michael@0: } michael@0: int64_t durationUs; michael@0: if (videoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) { michael@0: if (durationUs < 0) michael@0: LOG("video duration %lld should be nonnegative", durationUs); michael@0: if (durationUs > totalDurationUs) michael@0: totalDurationUs = durationUs; michael@0: } michael@0: } michael@0: michael@0: sp audioTrack; michael@0: sp audioSource; michael@0: if (audioTrackIndex != -1 && (audioTrack = extractor->getTrack(audioTrackIndex)) != nullptr) michael@0: { michael@0: if (!strcasecmp(audioMime, "audio/raw")) { michael@0: audioSource = audioTrack; michael@0: } else { michael@0: audioSource = OMXCodec::Create(omx, michael@0: audioTrack->getFormat(), michael@0: false, // decoder michael@0: audioTrack); michael@0: } michael@0: michael@0: if (audioSource == nullptr) { michael@0: LOG("OMXCodec failed to initialize audio decoder for \"%s\"", audioMime); michael@0: return false; michael@0: } michael@0: michael@0: status_t status = audioSource->start(); michael@0: if (status != OK) { michael@0: LOG("audioSource->start() failed with status %#x", status); michael@0: return false; michael@0: } michael@0: michael@0: int64_t durationUs; michael@0: if (audioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) { michael@0: if (durationUs < 0) michael@0: LOG("audio duration %lld should be nonnegative", durationUs); michael@0: if (durationUs > totalDurationUs) michael@0: totalDurationUs = durationUs; michael@0: } michael@0: } michael@0: michael@0: // set decoder state michael@0: mVideoTrack = videoTrack; michael@0: mVideoSource = videoSource; michael@0: mAudioTrack = audioTrack; michael@0: mAudioSource = audioSource; michael@0: mDurationUs = totalDurationUs; michael@0: michael@0: if (mVideoSource.get() && !SetVideoFormat()) michael@0: return false; michael@0: michael@0: // To reliably get the channel and sample rate data we need to read from the michael@0: // audio source until we get a INFO_FORMAT_CHANGE status michael@0: if (mAudioSource.get()) { michael@0: if (mAudioSource->read(&mAudioBuffer) != INFO_FORMAT_CHANGED) { michael@0: sp meta = mAudioSource->getFormat(); michael@0: if (!meta->findInt32(kKeyChannelCount, &mAudioChannels) || michael@0: !meta->findInt32(kKeySampleRate, &mAudioSampleRate)) { michael@0: return false; michael@0: } michael@0: mAudioMetadataRead = true; michael@0: michael@0: if (mAudioChannels < 0) { michael@0: LOG("audio channel count %d must be nonnegative", mAudioChannels); michael@0: return false; michael@0: } michael@0: michael@0: if (mAudioSampleRate < 0) { michael@0: LOG("audio sample rate %d must be nonnegative", mAudioSampleRate); michael@0: return false; michael@0: } michael@0: } michael@0: else if (!SetAudioFormat()) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool OmxDecoder::SetVideoFormat() { michael@0: sp format = mVideoSource->getFormat(); michael@0: michael@0: // Stagefright's kKeyWidth and kKeyHeight are what MPAPI calls stride and michael@0: // slice height. Stagefright only seems to use its kKeyStride and michael@0: // kKeySliceHeight to initialize camera video formats. michael@0: michael@0: #if defined(DEBUG) && !defined(MOZ_ANDROID_FROYO) michael@0: int32_t unexpected; michael@0: if (format->findInt32(kKeyStride, &unexpected)) michael@0: LOG("Expected kKeyWidth, but found kKeyStride %d", unexpected); michael@0: if (format->findInt32(kKeySliceHeight, &unexpected)) michael@0: LOG("Expected kKeyHeight, but found kKeySliceHeight %d", unexpected); michael@0: #endif // DEBUG michael@0: michael@0: const char *componentName; michael@0: michael@0: if (!format->findInt32(kKeyWidth, &mVideoStride) || michael@0: !format->findInt32(kKeyHeight, &mVideoSliceHeight) || michael@0: !format->findCString(kKeyDecoderComponent, &componentName) || michael@0: !format->findInt32(kKeyColorFormat, &mVideoColorFormat) ) { michael@0: return false; michael@0: } michael@0: michael@0: if (mVideoStride <= 0) { michael@0: LOG("stride %d must be positive", mVideoStride); michael@0: return false; michael@0: } michael@0: michael@0: if (mVideoSliceHeight <= 0) { michael@0: LOG("slice height %d must be positive", mVideoSliceHeight); michael@0: return false; michael@0: } michael@0: michael@0: // Gingerbread does not support the kKeyCropRect key michael@0: #if !defined(MOZ_ANDROID_V2_X_X) michael@0: if (!format->findRect(kKeyCropRect, &mVideoCropLeft, &mVideoCropTop, michael@0: &mVideoCropRight, &mVideoCropBottom)) { michael@0: #endif michael@0: mVideoCropLeft = 0; michael@0: mVideoCropTop = 0; michael@0: mVideoCropRight = mVideoStride - 1; michael@0: mVideoCropBottom = mVideoSliceHeight - 1; michael@0: LOG("crop rect not available, assuming no cropping"); michael@0: #if !defined(MOZ_ANDROID_V2_X_X) michael@0: } michael@0: #endif michael@0: michael@0: if (mVideoCropLeft < 0 || mVideoCropLeft >= mVideoCropRight || mVideoCropRight >= mVideoStride || michael@0: mVideoCropTop < 0 || mVideoCropTop >= mVideoCropBottom || mVideoCropBottom >= mVideoSliceHeight) { michael@0: LOG("invalid crop rect %d,%d-%d,%d", mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom); michael@0: return false; michael@0: } michael@0: michael@0: mVideoWidth = mVideoCropRight - mVideoCropLeft + 1; michael@0: mVideoHeight = mVideoCropBottom - mVideoCropTop + 1; michael@0: MOZ_ASSERT(mVideoWidth > 0 && mVideoWidth <= mVideoStride); michael@0: MOZ_ASSERT(mVideoHeight > 0 && mVideoHeight <= mVideoSliceHeight); michael@0: michael@0: #if !defined(MOZ_ANDROID_FROYO) michael@0: if (!format->findInt32(kKeyRotation, &mVideoRotation)) { michael@0: #endif michael@0: mVideoRotation = 0; michael@0: #if !defined(MOZ_ANDROID_FROYO) michael@0: LOG("rotation not available, assuming 0"); michael@0: } michael@0: #endif michael@0: michael@0: if (mVideoRotation != 0 && mVideoRotation != 90 && michael@0: mVideoRotation != 180 && mVideoRotation != 270) { michael@0: LOG("invalid rotation %d, assuming 0", mVideoRotation); michael@0: } michael@0: michael@0: LOG("width: %d height: %d component: %s format: %#x stride: %d sliceHeight: %d rotation: %d crop: %d,%d-%d,%d", michael@0: mVideoWidth, mVideoHeight, componentName, mVideoColorFormat, michael@0: mVideoStride, mVideoSliceHeight, mVideoRotation, michael@0: mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool OmxDecoder::SetAudioFormat() { michael@0: // If the format changed, update our cached info. michael@0: if (!mAudioSource->getFormat()->findInt32(kKeyChannelCount, &mAudioChannels) || michael@0: !mAudioSource->getFormat()->findInt32(kKeySampleRate, &mAudioSampleRate)) { michael@0: return false; michael@0: } michael@0: michael@0: LOG("channelCount: %d sampleRate: %d", mAudioChannels, mAudioSampleRate); michael@0: michael@0: if (mAudioChannels < 0) { michael@0: LOG("audio channel count %d must be nonnegative", mAudioChannels); michael@0: return false; michael@0: } michael@0: michael@0: if (mAudioSampleRate < 0) { michael@0: LOG("audio sample rate %d must be nonnegative", mAudioSampleRate); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void OmxDecoder::ReleaseVideoBuffer() { michael@0: if (mVideoBuffer) { michael@0: mVideoBuffer->release(); michael@0: mVideoBuffer = nullptr; michael@0: } michael@0: } michael@0: michael@0: void OmxDecoder::ReleaseAudioBuffer() { michael@0: if (mAudioBuffer) { michael@0: mAudioBuffer->release(); michael@0: mAudioBuffer = nullptr; michael@0: } michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_YUV420Planar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: void *y = aData; michael@0: void *u = static_cast(y) + mVideoStride * mVideoSliceHeight; michael@0: void *v = static_cast(u) + mVideoStride/2 * mVideoSliceHeight/2; michael@0: aFrame->Set(aTimeUs, aKeyFrame, michael@0: aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, michael@0: y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, michael@0: u, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0, michael@0: v, mVideoStride/2, mVideoWidth/2, mVideoHeight/2, 0, 0); michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_CbYCrY(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: aFrame->Set(aTimeUs, aKeyFrame, michael@0: aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, michael@0: aData, mVideoStride, mVideoWidth, mVideoHeight, 1, 1, michael@0: aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 3, michael@0: aData, mVideoStride, mVideoWidth/2, mVideoHeight/2, 2, 3); michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_YUV420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: int32_t videoStride = mVideoStride; michael@0: int32_t videoSliceHeight = mVideoSliceHeight; michael@0: michael@0: // OMX.SEC.avcdec rounds mVideoStride and mVideoSliceHeight up to the nearest michael@0: // multiple of 16 but the data itself is too small to fit. What we do is check michael@0: // to see if the video size patches the raw width and height. If so we can michael@0: // use those figures instead. michael@0: michael@0: if (static_cast(aSize) == mVideoWidth * mVideoHeight * 3 / 2) { michael@0: videoStride = mVideoWidth; michael@0: videoSliceHeight = mVideoHeight; michael@0: } michael@0: michael@0: void *y = aData; michael@0: void *uv = static_cast(y) + (videoStride * videoSliceHeight); michael@0: aFrame->Set(aTimeUs, aKeyFrame, michael@0: aData, aSize, videoStride, videoSliceHeight, mVideoRotation, michael@0: y, videoStride, mVideoWidth, mVideoHeight, 0, 0, michael@0: uv, videoStride, mVideoWidth/2, mVideoHeight/2, 0, 1, michael@0: uv, videoStride, mVideoWidth/2, mVideoHeight/2, 1, 1); michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_YVU420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: ToVideoFrame_YUV420SemiPlanar(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: aFrame->Cb.mOffset = 1; michael@0: aFrame->Cr.mOffset = 0; michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_YUV420PackedSemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: void *y = aData; michael@0: void *uv = static_cast(y) + mVideoStride * (mVideoSliceHeight - mVideoCropTop/2); michael@0: aFrame->Set(aTimeUs, aKeyFrame, michael@0: aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, michael@0: y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, michael@0: uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 1, michael@0: uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 1, 1); michael@0: } michael@0: michael@0: void OmxDecoder::ToVideoFrame_YVU420PackedSemiPlanar32m4ka(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame) { michael@0: size_t roundedSliceHeight = (mVideoSliceHeight + 31) & ~31; michael@0: size_t roundedStride = (mVideoStride + 31) & ~31; michael@0: void *y = aData; michael@0: void *uv = static_cast(y) + (roundedStride * roundedSliceHeight); michael@0: aFrame->Set(aTimeUs, aKeyFrame, michael@0: aData, aSize, mVideoStride, mVideoSliceHeight, mVideoRotation, michael@0: y, mVideoStride, mVideoWidth, mVideoHeight, 0, 0, michael@0: uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 1, 1, michael@0: uv, mVideoStride, mVideoWidth/2, mVideoHeight/2, 0, 1); michael@0: } michael@0: michael@0: bool OmxDecoder::ToVideoFrame_RGB565(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) { michael@0: void *buffer = (*aBufferCallback)(mVideoWidth, mVideoHeight, MPAPI::RGB565); michael@0: michael@0: if (!buffer) { michael@0: return false; michael@0: } michael@0: michael@0: aFrame->mTimeUs = aTimeUs; michael@0: michael@0: memcpy(buffer, aData, mVideoWidth * mVideoHeight * 2); michael@0: michael@0: aFrame->mSize = mVideoWidth * mVideoHeight * 2; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool OmxDecoder::ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) { michael@0: #ifdef MOZ_ANDROID_HC michael@0: return false; michael@0: #else michael@0: if (!mColorConverter) { michael@0: mColorConverter = new ColorConverter((OMX_COLOR_FORMATTYPE)mVideoColorFormat, michael@0: OMX_COLOR_Format16bitRGB565); michael@0: } michael@0: michael@0: if (!mColorConverter->isValid()) { michael@0: return false; michael@0: } michael@0: michael@0: aFrame->mTimeUs = aTimeUs; michael@0: michael@0: void *buffer = (*aBufferCallback)(mVideoWidth, mVideoHeight, MPAPI::RGB565); michael@0: michael@0: if (!buffer) { michael@0: return false; michael@0: } michael@0: michael@0: aFrame->mSize = mVideoWidth * mVideoHeight * 2; michael@0: michael@0: #if defined(MOZ_ANDROID_V2_X_X) michael@0: mColorConverter->convert(mVideoWidth, mVideoHeight, michael@0: aData, 0 /* srcSkip */, michael@0: buffer, mVideoWidth * 2); michael@0: #else michael@0: mColorConverter->convert(aData, mVideoStride, mVideoSliceHeight, michael@0: mVideoCropLeft, mVideoCropTop, michael@0: mVideoCropLeft + mVideoWidth - 1, michael@0: mVideoCropTop + mVideoHeight - 1, michael@0: buffer, mVideoWidth, mVideoHeight, michael@0: 0, 0, mVideoWidth - 1, mVideoHeight - 1); michael@0: #endif michael@0: michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: bool OmxDecoder::ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) michael@0: { michael@0: #if defined(MOZ_ANDROID_V4_OR_ABOVE) michael@0: I420ColorConverter yuvConverter; michael@0: michael@0: if (!yuvConverter.isLoaded()) { michael@0: return false; michael@0: } michael@0: michael@0: if (yuvConverter.getDecoderOutputFormat() != mVideoColorFormat) { michael@0: return false; michael@0: } michael@0: michael@0: void *buffer = (*aBufferCallback)(mVideoWidth, mVideoHeight, MPAPI::I420); michael@0: michael@0: ARect crop = { mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom }; michael@0: int result = yuvConverter.convertDecoderOutputToI420(aData, michael@0: mVideoWidth, michael@0: mVideoHeight, michael@0: crop, michael@0: buffer); michael@0: michael@0: // result is 0 on success, -1 otherwise. michael@0: if (result == OK) { michael@0: aFrame->mTimeUs = aTimeUs; michael@0: aFrame->mSize = mVideoWidth * mVideoHeight * 3 / 2; michael@0: } michael@0: michael@0: return result == OK; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) { michael@0: switch (mVideoColorFormat) { michael@0: // Froyo support is best handled with the android color conversion code. I michael@0: // get corrupted videos when using our own routines below. michael@0: #if !defined(MOZ_ANDROID_FROYO) michael@0: case OMX_COLOR_FormatYUV420Planar: // e.g. Asus Transformer, Stagefright's software decoder michael@0: ToVideoFrame_YUV420Planar(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_COLOR_FormatCbYCrY: // e.g. Droid 1 michael@0: ToVideoFrame_CbYCrY(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_COLOR_FormatYUV420SemiPlanar: // e.g. Galaxy S III michael@0: ToVideoFrame_YUV420SemiPlanar(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: // e.g. Nexus One michael@0: ToVideoFrame_YVU420SemiPlanar(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka: // e.g. Otoro michael@0: ToVideoFrame_YVU420PackedSemiPlanar32m4ka(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: // e.g. Galaxy Nexus michael@0: ToVideoFrame_YUV420PackedSemiPlanar(aFrame, aTimeUs, aData, aSize, aKeyFrame); michael@0: break; michael@0: case OMX_COLOR_Format16bitRGB565: michael@0: return ToVideoFrame_RGB565(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback); michael@0: break; michael@0: #endif michael@0: default: michael@0: if (!ToVideoFrame_ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback) && michael@0: !ToVideoFrame_I420ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback)) { michael@0: LOG("Unknown video color format: %#x", mVideoColorFormat); michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool OmxDecoder::ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, int32_t aAudioChannels, int32_t aAudioSampleRate) michael@0: { michael@0: aFrame->Set(aTimeUs, reinterpret_cast(aData) + aDataOffset, aSize, aAudioChannels, aAudioSampleRate); michael@0: return true; michael@0: } michael@0: michael@0: class ReadOptions : public MediaSource::ReadOptions michael@0: { michael@0: // HTC have their own version of ReadOptions with extra fields. If we don't michael@0: // have this here, HTCOMXCodec will corrupt our stack. michael@0: uint32_t sadface[16]; michael@0: }; michael@0: michael@0: bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs, michael@0: BufferCallback *aBufferCallback) michael@0: { michael@0: MOZ_ASSERT(aSeekTimeUs >= -1); michael@0: michael@0: if (!mVideoSource.get()) michael@0: return false; michael@0: michael@0: ReleaseVideoBuffer(); michael@0: michael@0: status_t err; michael@0: michael@0: if (aSeekTimeUs != -1) { michael@0: ReadOptions options; michael@0: options.setSeekTo(aSeekTimeUs); michael@0: err = mVideoSource->read(&mVideoBuffer, &options); michael@0: } else { michael@0: err = mVideoSource->read(&mVideoBuffer); michael@0: } michael@0: michael@0: aFrame->mSize = 0; michael@0: michael@0: if (err == OK && mVideoBuffer->range_length() > 0) { michael@0: int64_t timeUs; michael@0: int32_t keyFrame; michael@0: michael@0: if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs) ) { michael@0: LOG("no frame time"); michael@0: return false; michael@0: } michael@0: michael@0: if (timeUs < 0) { michael@0: LOG("frame time %lld must be nonnegative", timeUs); michael@0: return false; michael@0: } michael@0: michael@0: if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) { michael@0: keyFrame = 0; michael@0: } michael@0: michael@0: char *data = reinterpret_cast(mVideoBuffer->data()) + mVideoBuffer->range_offset(); michael@0: size_t length = mVideoBuffer->range_length(); michael@0: michael@0: if (!ToVideoFrame(aFrame, timeUs, data, length, keyFrame, aBufferCallback)) { michael@0: return false; michael@0: } michael@0: } michael@0: else if (err == INFO_FORMAT_CHANGED) { michael@0: // If the format changed, update our cached info. michael@0: LOG("mVideoSource INFO_FORMAT_CHANGED"); michael@0: if (!SetVideoFormat()) michael@0: return false; michael@0: else michael@0: return ReadVideo(aFrame, aSeekTimeUs, aBufferCallback); michael@0: } michael@0: else if (err == ERROR_END_OF_STREAM) { michael@0: LOG("mVideoSource END_OF_STREAM"); michael@0: } michael@0: else if (err != OK) { michael@0: LOG("mVideoSource ERROR %#x", err); michael@0: } michael@0: michael@0: return err == OK; michael@0: } michael@0: michael@0: bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs) michael@0: { michael@0: MOZ_ASSERT(aSeekTimeUs >= -1); michael@0: michael@0: status_t err; michael@0: if (mAudioMetadataRead && aSeekTimeUs == -1) { michael@0: // Use the data read into the buffer during metadata time michael@0: err = OK; michael@0: } michael@0: else { michael@0: ReleaseAudioBuffer(); michael@0: if (aSeekTimeUs != -1) { michael@0: ReadOptions options; michael@0: options.setSeekTo(aSeekTimeUs); michael@0: err = mAudioSource->read(&mAudioBuffer, &options); michael@0: } else { michael@0: err = mAudioSource->read(&mAudioBuffer); michael@0: } michael@0: } michael@0: mAudioMetadataRead = false; michael@0: michael@0: aSeekTimeUs = -1; michael@0: michael@0: if (err == OK && mAudioBuffer->range_length() != 0) { michael@0: int64_t timeUs; michael@0: if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) { michael@0: LOG("no frame time"); michael@0: return false; michael@0: } michael@0: michael@0: if (timeUs < 0) { michael@0: LOG("frame time %lld must be nonnegative", timeUs); michael@0: return false; michael@0: } michael@0: michael@0: return ToAudioFrame(aFrame, timeUs, michael@0: mAudioBuffer->data(), michael@0: mAudioBuffer->range_offset(), michael@0: mAudioBuffer->range_length(), michael@0: mAudioChannels, mAudioSampleRate); michael@0: } michael@0: else if (err == INFO_FORMAT_CHANGED) { michael@0: // If the format changed, update our cached info. michael@0: LOG("mAudioSource INFO_FORMAT_CHANGED"); michael@0: if (!SetAudioFormat()) michael@0: return false; michael@0: else michael@0: return ReadAudio(aFrame, aSeekTimeUs); michael@0: } michael@0: else if (err == ERROR_END_OF_STREAM) { michael@0: LOG("mAudioSource END_OF_STREAM"); michael@0: } michael@0: else if (err != OK) { michael@0: LOG("mAudioSource ERROR %#x", err); michael@0: } michael@0: michael@0: return err == OK; michael@0: } michael@0: michael@0: static OmxDecoder *cast(Decoder *decoder) { michael@0: return reinterpret_cast(decoder->mPrivate); michael@0: } michael@0: michael@0: static void GetDuration(Decoder *aDecoder, int64_t *durationUs) { michael@0: cast(aDecoder)->GetDuration(durationUs); michael@0: } michael@0: michael@0: static void GetVideoParameters(Decoder *aDecoder, int32_t *width, int32_t *height) { michael@0: cast(aDecoder)->GetVideoParameters(width, height); michael@0: } michael@0: michael@0: static void GetAudioParameters(Decoder *aDecoder, int32_t *numChannels, int32_t *sampleRate) { michael@0: cast(aDecoder)->GetAudioParameters(numChannels, sampleRate); michael@0: } michael@0: michael@0: static bool HasVideo(Decoder *aDecoder) { michael@0: return cast(aDecoder)->HasVideo(); michael@0: } michael@0: michael@0: static bool HasAudio(Decoder *aDecoder) { michael@0: return cast(aDecoder)->HasAudio(); michael@0: } michael@0: michael@0: static bool ReadVideo(Decoder *aDecoder, VideoFrame *aFrame, int64_t aSeekTimeUs, BufferCallback *aBufferCallback) michael@0: { michael@0: return cast(aDecoder)->ReadVideo(aFrame, aSeekTimeUs, aBufferCallback); michael@0: } michael@0: michael@0: static bool ReadAudio(Decoder *aDecoder, AudioFrame *aFrame, int64_t aSeekTimeUs) michael@0: { michael@0: return cast(aDecoder)->ReadAudio(aFrame, aSeekTimeUs); michael@0: } michael@0: michael@0: static void DestroyDecoder(Decoder *aDecoder) michael@0: { michael@0: if (aDecoder->mPrivate) michael@0: delete reinterpret_cast(aDecoder->mPrivate); michael@0: } michael@0: michael@0: static bool Match(const char *aMimeChars, size_t aMimeLen, const char *aNeedle) michael@0: { michael@0: return !strncmp(aMimeChars, aNeedle, aMimeLen); michael@0: } michael@0: michael@0: static const char* const gCodecs[] = { michael@0: "avc1.42E01E", // H.264 Constrained Baseline Profile Level 3.0 michael@0: "avc1.42001E", // H.264 Baseline Profile Level 3.0 michael@0: "avc1.42001F", // H.264 Baseline Profile Level 3.1 michael@0: "avc1.4D401E", // H.264 Main Profile Level 3.0 michael@0: "avc1.4D401F", // H.264 Main Profile Level 3.1 michael@0: "mp4a.40.2", // AAC-LC michael@0: nullptr michael@0: }; michael@0: michael@0: static bool CanDecode(const char *aMimeChars, size_t aMimeLen, const char* const**aCodecs) michael@0: { michael@0: if (!Match(aMimeChars, aMimeLen, "video/mp4") && michael@0: !Match(aMimeChars, aMimeLen, "audio/mp4") && michael@0: !Match(aMimeChars, aMimeLen, "audio/mpeg") && michael@0: !Match(aMimeChars, aMimeLen, "application/octet-stream")) { // file urls michael@0: return false; michael@0: } michael@0: *aCodecs = gCodecs; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool CreateDecoder(PluginHost *aPluginHost, Decoder *aDecoder, const char *aMimeChars, size_t aMimeLen) michael@0: { michael@0: OmxDecoder *omx = new OmxDecoder(aPluginHost, aDecoder); michael@0: if (!omx || !omx->Init()) { michael@0: if (omx) michael@0: delete omx; michael@0: return false; michael@0: } michael@0: michael@0: aDecoder->mPrivate = omx; michael@0: aDecoder->GetDuration = GetDuration; michael@0: aDecoder->GetVideoParameters = GetVideoParameters; michael@0: aDecoder->GetAudioParameters = GetAudioParameters; michael@0: aDecoder->HasVideo = HasVideo; michael@0: aDecoder->HasAudio = HasAudio; michael@0: aDecoder->ReadVideo = ReadVideo; michael@0: aDecoder->ReadAudio = ReadAudio; michael@0: aDecoder->DestroyDecoder = DestroyDecoder; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace OmxPlugin michael@0: michael@0: // Export the manifest so MPAPI can find our entry points. michael@0: Manifest MOZ_EXPORT MPAPI_MANIFEST = { michael@0: OmxPlugin::CanDecode, michael@0: OmxPlugin::CreateDecoder michael@0: };