mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge b2g-inbound to m-c.
This commit is contained in:
commit
9c7c648b58
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "7412c36923b59f6e4d7076de5be7e6ded6ddc586",
|
||||
"revision": "c6aeaa41977d9410b9baac23ccea6909f796fd1f",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -324,6 +324,9 @@ DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
|
||||
void
|
||||
DOMMediaStream::CheckTracksAvailable()
|
||||
{
|
||||
if (mTrackTypesAvailable == 0) {
|
||||
return;
|
||||
}
|
||||
nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
|
||||
callbacks.SwapElements(mRunOnTracksAvailable);
|
||||
|
||||
|
@ -164,6 +164,7 @@ public:
|
||||
// We only care about track additions, we'll fire the notification even if
|
||||
// some of the tracks have been removed.
|
||||
// Takes ownership of aCallback.
|
||||
// If GetExpectedTracks() returns 0, the callback will be fired as soon as there are any tracks.
|
||||
void OnTracksAvailable(OnTracksAvailableCallback* aCallback);
|
||||
|
||||
/**
|
||||
@ -182,6 +183,7 @@ protected:
|
||||
void InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
|
||||
void InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
|
||||
void InitStreamCommon(MediaStream* aStream);
|
||||
|
||||
void CheckTracksAvailable();
|
||||
|
||||
class StreamListener;
|
||||
|
@ -17,12 +17,16 @@
|
||||
#include "nsIDOMFile.h"
|
||||
#include "mozilla/dom/BlobEvent.h"
|
||||
|
||||
|
||||
#include "mozilla/dom/AudioStreamTrack.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MediaRecorder, nsDOMEventTargetHelper,
|
||||
mStream)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaRecorder, nsDOMEventTargetHelper,
|
||||
mStream, mSession)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
||||
@ -45,7 +49,7 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
|
||||
*
|
||||
* Life cycle of a Session object.
|
||||
* 1) Initialization Stage (in main thread)
|
||||
* Setup media streams in MSG, and bind MediaEncoder with Source Stream.
|
||||
* Setup media streams in MSG, and bind MediaEncoder with Source Stream when mStream is available.
|
||||
* Resource allocation, such as encoded data cache buffer and MediaEncoder.
|
||||
* Create read thread.
|
||||
* Automatically switch to Extract stage in the end of this stage.
|
||||
@ -90,7 +94,7 @@ class MediaRecorder::Session: public nsIObserver
|
||||
}
|
||||
|
||||
private:
|
||||
Session *mSession;
|
||||
nsRefPtr<Session> mSession;
|
||||
};
|
||||
|
||||
// Record thread task.
|
||||
@ -110,7 +114,36 @@ class MediaRecorder::Session: public nsIObserver
|
||||
}
|
||||
|
||||
private:
|
||||
Session *mSession;
|
||||
nsRefPtr<Session> mSession;
|
||||
};
|
||||
|
||||
// For Ensure recorder has tracks to record.
|
||||
class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
|
||||
{
|
||||
public:
|
||||
TracksAvailableCallback(Session *aSession)
|
||||
: mSession(aSession) {}
|
||||
virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
|
||||
{
|
||||
uint8_t trackType = aStream->GetHintContents();
|
||||
// ToDo: GetHintContents return 0 when recording media tags.
|
||||
if (trackType == 0) {
|
||||
nsTArray<nsRefPtr<mozilla::dom::AudioStreamTrack> > audioTracks;
|
||||
aStream->GetAudioTracks(audioTracks);
|
||||
nsTArray<nsRefPtr<mozilla::dom::VideoStreamTrack> > videoTracks;
|
||||
aStream->GetVideoTracks(videoTracks);
|
||||
// What is inside the track
|
||||
if (videoTracks.Length() > 0) {
|
||||
trackType |= DOMMediaStream::HINT_CONTENTS_VIDEO;
|
||||
}
|
||||
if (audioTracks.Length() > 0) {
|
||||
trackType |= DOMMediaStream::HINT_CONTENTS_AUDIO;
|
||||
}
|
||||
}
|
||||
mSession->AfterTracksAdded(trackType);
|
||||
}
|
||||
private:
|
||||
nsRefPtr<Session> mSession;
|
||||
};
|
||||
|
||||
// Main thread task.
|
||||
@ -154,6 +187,7 @@ class MediaRecorder::Session: public nsIObserver
|
||||
friend class PushBlobRunnable;
|
||||
friend class ExtractRunnable;
|
||||
friend class DestroyRunnable;
|
||||
friend class TracksAvailableCallback;
|
||||
|
||||
public:
|
||||
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
|
||||
@ -169,8 +203,6 @@ public:
|
||||
// Only DestroyRunnable is allowed to delete Session object.
|
||||
virtual ~Session()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
CleanupStreams();
|
||||
}
|
||||
|
||||
@ -179,22 +211,6 @@ public:
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
SetupStreams();
|
||||
|
||||
// Create a thread to read encode media data from MediaEncoder.
|
||||
if (!mReadThread) {
|
||||
nsresult rv = NS_NewNamedThread("Media Encoder", getter_AddRefs(mReadThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
CleanupStreams();
|
||||
mRecorder->NotifyError(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In case source media stream does not notify track end, recieve
|
||||
// shutdown notification and stop Read Thread.
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
|
||||
mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void Stop()
|
||||
@ -290,14 +306,57 @@ private:
|
||||
mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
|
||||
|
||||
// Allocate encoder and bind with the Track Union Stream.
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""));
|
||||
MOZ_ASSERT(mEncoder, "CreateEncoder failed");
|
||||
|
||||
if (mEncoder) {
|
||||
mTrackUnionStream->AddListener(mEncoder);
|
||||
}
|
||||
TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSession);
|
||||
mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
|
||||
}
|
||||
|
||||
void AfterTracksAdded(uint8_t aTrackTypes)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Allocate encoder and bind with union stream.
|
||||
// At this stage, the API doesn't allow UA to choose the output mimeType format.
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
|
||||
|
||||
if (!mEncoder) {
|
||||
DoSessionEndTask(NS_ERROR_ABORT);
|
||||
return;
|
||||
}
|
||||
|
||||
// media stream is ready but has been issued stop command
|
||||
if (mRecorder->mState == RecordingState::Inactive) {
|
||||
DoSessionEndTask(NS_OK);
|
||||
return;
|
||||
}
|
||||
mTrackUnionStream->AddListener(mEncoder);
|
||||
// Create a thread to read encode media data from MediaEncoder.
|
||||
if (!mReadThread) {
|
||||
nsresult rv = NS_NewNamedThread("Media Encoder", getter_AddRefs(mReadThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
DoSessionEndTask(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In case source media stream does not notify track end, recieve
|
||||
// shutdown notification and stop Read Thread.
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
|
||||
mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
// application should get blob and onstop event
|
||||
void DoSessionEndTask(nsresult rv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mRecorder->NotifyError(rv);
|
||||
}
|
||||
CleanupStreams();
|
||||
// Destroy this session object in main thread.
|
||||
NS_DispatchToMainThread(new PushBlobRunnable(this));
|
||||
NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(this)));
|
||||
}
|
||||
void CleanupStreams()
|
||||
{
|
||||
if (mInputPort.get()) {
|
||||
@ -430,11 +489,10 @@ MediaRecorder::Stop(ErrorResult& aResult)
|
||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mState = RecordingState::Inactive;
|
||||
|
||||
mSession->Stop();
|
||||
mSession = nullptr;
|
||||
|
||||
mState = RecordingState::Inactive;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -104,7 +104,7 @@ protected:
|
||||
// The current state of the MediaRecorder object.
|
||||
RecordingState mState;
|
||||
// Current recording session.
|
||||
Session *mSession;
|
||||
nsRefPtr<Session> mSession;
|
||||
// Thread safe for mMimeType.
|
||||
Mutex mMutex;
|
||||
// It specifies the container format as well as the audio and video capture formats.
|
||||
|
@ -19,9 +19,14 @@ class ContainerWriter {
|
||||
public:
|
||||
ContainerWriter()
|
||||
: mInitialized(false)
|
||||
, mIsWritingComplete(false)
|
||||
{}
|
||||
virtual ~ContainerWriter() {}
|
||||
|
||||
// Mapping to DOMLocalMediaStream::TrackTypeHints
|
||||
enum {
|
||||
HAS_AUDIO = 1 << 0,
|
||||
HAS_VIDEO = 1 << 1,
|
||||
};
|
||||
enum {
|
||||
END_OF_STREAM = 1 << 0
|
||||
};
|
||||
@ -44,6 +49,11 @@ public:
|
||||
*/
|
||||
virtual nsresult SetMetadata(TrackMetadataBase* aMetadata) = 0;
|
||||
|
||||
/**
|
||||
* Indicate if the writer has finished to output data
|
||||
*/
|
||||
virtual bool IsWritingComplete() { return mIsWritingComplete; }
|
||||
|
||||
enum {
|
||||
FLUSH_NEEDED = 1 << 0,
|
||||
GET_HEADER = 1 << 1
|
||||
@ -59,9 +69,9 @@ public:
|
||||
*/
|
||||
virtual nsresult GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
uint32_t aFlags = 0) = 0;
|
||||
|
||||
protected:
|
||||
bool mInitialized;
|
||||
bool mIsWritingComplete;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@ -52,6 +52,8 @@ public:
|
||||
P_FRAME, // predicted frame
|
||||
B_FRAME, // bidirectionally predicted frame
|
||||
AUDIO_FRAME, // audio frame
|
||||
AAC_CSD, // AAC codec specific data
|
||||
AVC_CSD, // AVC codec specific data
|
||||
UNKNOW // FrameType not set
|
||||
};
|
||||
const nsTArray<uint8_t>& GetFrameData() const
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "MediaEncoder.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef MOZ_OGG
|
||||
#include "OggWriter.h"
|
||||
@ -12,50 +14,30 @@
|
||||
#ifdef MOZ_OPUS
|
||||
#include "OpusTrackEncoder.h"
|
||||
#endif
|
||||
#ifdef MOZ_WEBM_ENCODER
|
||||
#include "VorbisTrackEncoder.h"
|
||||
#include "VP8TrackEncoder.h"
|
||||
#include "WebMWriter.h"
|
||||
#endif
|
||||
#ifdef MOZ_OMX_ENCODER
|
||||
#include "OmxTrackEncoder.h"
|
||||
#include "ISOMediaWriter.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <android/log.h>
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
#endif
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gMediaEncoderLog;
|
||||
#define LOG(type, msg) PR_LOG(gMediaEncoderLog, type, msg)
|
||||
#else
|
||||
#define LOG(args,...)
|
||||
#define LOG(type, msg)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define TRACK_BUFFER_LEN 8192
|
||||
|
||||
namespace {
|
||||
|
||||
template <class String>
|
||||
static bool
|
||||
TypeListContains(char const *const * aTypes, const String& aType)
|
||||
{
|
||||
for (int32_t i = 0; aTypes[i]; ++i) {
|
||||
if (aType.EqualsASCII(aTypes[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MOZ_OGG
|
||||
// The recommended mime-type for Ogg Opus files is audio/ogg.
|
||||
// See http://wiki.xiph.org/OggOpus for more details.
|
||||
static const char* const gOggTypes[2] = {
|
||||
"audio/ogg",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static bool
|
||||
IsOggType(const nsAString& aType)
|
||||
{
|
||||
if (!MediaDecoder::IsOggEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TypeListContains(gOggTypes, aType);
|
||||
}
|
||||
#endif
|
||||
} //anonymous namespace
|
||||
static nsIThread* sEncoderThread = nullptr;
|
||||
|
||||
void
|
||||
MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
@ -67,13 +49,15 @@ MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
{
|
||||
// Process the incoming raw track data from MediaStreamGraph, called on the
|
||||
// thread of MediaStreamGraph.
|
||||
if (aQueuedMedia.GetType() == MediaSegment::AUDIO) {
|
||||
if (mAudioEncoder && aQueuedMedia.GetType() == MediaSegment::AUDIO) {
|
||||
mAudioEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
|
||||
aTrackOffset, aTrackEvents,
|
||||
aQueuedMedia);
|
||||
|
||||
} else {
|
||||
// Type video is not supported for now.
|
||||
} else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) {
|
||||
mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
|
||||
aTrackOffset, aTrackEvents,
|
||||
aQueuedMedia);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,49 +65,88 @@ void
|
||||
MediaEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
|
||||
{
|
||||
// In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
|
||||
LOG("NotifyRemoved in [MediaEncoder].");
|
||||
mAudioEncoder->NotifyRemoved(aGraph);
|
||||
LOG(PR_LOG_DEBUG, ("NotifyRemoved in [MediaEncoder]."));
|
||||
if (mAudioEncoder) {
|
||||
mAudioEncoder->NotifyRemoved(aGraph);
|
||||
}
|
||||
if (mVideoEncoder) {
|
||||
mVideoEncoder->NotifyRemoved(aGraph);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
MediaEncoder::OnEncoderThread()
|
||||
{
|
||||
return NS_GetCurrentThread() == sEncoderThread;
|
||||
}
|
||||
/* static */
|
||||
already_AddRefed<MediaEncoder>
|
||||
MediaEncoder::CreateEncoder(const nsAString& aMIMEType)
|
||||
MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaEncoderLog) {
|
||||
gMediaEncoderLog = PR_NewLogModule("MediaEncoder");
|
||||
}
|
||||
#endif
|
||||
nsAutoPtr<ContainerWriter> writer;
|
||||
nsAutoPtr<AudioTrackEncoder> audioEncoder;
|
||||
nsAutoPtr<VideoTrackEncoder> videoEncoder;
|
||||
nsRefPtr<MediaEncoder> encoder;
|
||||
|
||||
if (aMIMEType.IsEmpty()) {
|
||||
// TODO: Should pick out a default container+codec base on the track
|
||||
// coming from MediaStreamGraph. For now, just default to Ogg+Opus.
|
||||
const_cast<nsAString&>(aMIMEType) = NS_LITERAL_STRING("audio/ogg");
|
||||
nsString mimeType;
|
||||
if (!aTrackTypes) {
|
||||
LOG(PR_LOG_ERROR, ("NO TrackTypes!!!"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isAudioOnly = FindInReadable(NS_LITERAL_STRING("audio/"), aMIMEType);
|
||||
#ifdef MOZ_OGG
|
||||
if (IsOggType(aMIMEType)) {
|
||||
writer = new OggWriter();
|
||||
if (!isAudioOnly) {
|
||||
// Initialize the videoEncoder.
|
||||
#ifdef MOZ_WEBM_ENCODER
|
||||
else if (MediaDecoder::IsWebMEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_VIDEO))) {
|
||||
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
|
||||
audioEncoder = new VorbisTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
audioEncoder = new OpusTrackEncoder();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
// If the given mime-type is video but fail to create the video encoder.
|
||||
if (!isAudioOnly) {
|
||||
videoEncoder = new VP8TrackEncoder();
|
||||
writer = new WebMWriter(aTrackTypes);
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
NS_ENSURE_TRUE(videoEncoder, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
|
||||
}
|
||||
|
||||
// Return null if we fail to create the audio encoder.
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
|
||||
#endif //MOZ_WEBM_ENCODER
|
||||
#ifdef MOZ_OMX_ENCODER
|
||||
else if (aMIMEType.EqualsLiteral(VIDEO_MP4) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_VIDEO)) {
|
||||
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
|
||||
audioEncoder = new OmxAudioTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
}
|
||||
videoEncoder = new OmxVideoTrackEncoder();
|
||||
writer = new ISOMediaWriter(aTrackTypes);
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
NS_ENSURE_TRUE(videoEncoder, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(VIDEO_MP4);
|
||||
}
|
||||
#endif // MOZ_OMX_ENCODER
|
||||
#ifdef MOZ_OGG
|
||||
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(AUDIO_OGG) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_AUDIO))) {
|
||||
writer = new OggWriter();
|
||||
audioEncoder = new OpusTrackEncoder();
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(AUDIO_OGG);
|
||||
}
|
||||
#endif // MOZ_OGG
|
||||
else {
|
||||
LOG(PR_LOG_ERROR, ("Can not find any encoder to record this media stream"));
|
||||
return nullptr;
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Create encoder result:a[%d] v[%d] w[%d] mimeType = %s.",
|
||||
audioEncoder != nullptr, videoEncoder != nullptr,
|
||||
writer != nullptr, mimeType.get()));
|
||||
encoder = new MediaEncoder(writer.forget(), audioEncoder.forget(),
|
||||
videoEncoder.forget(), aMIMEType);
|
||||
|
||||
|
||||
videoEncoder.forget(), mimeType);
|
||||
return encoder.forget();
|
||||
}
|
||||
|
||||
@ -156,75 +179,75 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
nsAString& aMIMEType)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!sEncoderThread) {
|
||||
sEncoderThread = NS_GetCurrentThread();
|
||||
}
|
||||
aMIMEType = mMIMEType;
|
||||
|
||||
bool reloop = true;
|
||||
while (reloop) {
|
||||
switch (mState) {
|
||||
case ENCODE_METADDATA: {
|
||||
nsRefPtr<TrackMetadataBase> meta = mAudioEncoder->GetMetadata();
|
||||
if (meta == nullptr) {
|
||||
LOG("ERROR! AudioEncoder get null Metadata!");
|
||||
mState = ENCODE_ERROR;
|
||||
LOG(PR_LOG_DEBUG, ("ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
|
||||
nsresult rv = CopyMetadataToMuxer(mAudioEncoder.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_ERROR, ("Error! Fail to Set Audio Metadata"));
|
||||
break;
|
||||
}
|
||||
nsresult rv = mWriter->SetMetadata(meta);
|
||||
rv = CopyMetadataToMuxer(mVideoEncoder.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("ERROR! writer can't accept audio metadata!");
|
||||
mState = ENCODE_ERROR;
|
||||
break;
|
||||
LOG(PR_LOG_ERROR, ("Error! Fail to Set Video Metadata"));
|
||||
break;
|
||||
}
|
||||
|
||||
rv = mWriter->GetContainerData(aOutputBufs,
|
||||
ContainerWriter::GET_HEADER);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("ERROR! writer fail to generate header!");
|
||||
LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
|
||||
mState = ENCODE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(PR_LOG_DEBUG, ("Finish ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
|
||||
mState = ENCODE_TRACK;
|
||||
break;
|
||||
}
|
||||
|
||||
case ENCODE_TRACK: {
|
||||
LOG(PR_LOG_DEBUG, ("ENCODE_TRACK TimeStamp = %f", GetEncodeTimeStamp()));
|
||||
EncodedFrameContainer encodedData;
|
||||
nsresult rv = mAudioEncoder->GetEncodedTrack(encodedData);
|
||||
nsresult rv = NS_OK;
|
||||
rv = WriteEncodedDataToMuxer(mAudioEncoder.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
// Encoding might be canceled.
|
||||
LOG("ERROR! Fail to get encoded data from encoder.");
|
||||
mState = ENCODE_ERROR;
|
||||
LOG(PR_LOG_ERROR, ("Error! Fail to write audio encoder data to muxer"));
|
||||
break;
|
||||
}
|
||||
rv = mWriter->WriteEncodedTrack(encodedData,
|
||||
mAudioEncoder->IsEncodingComplete() ?
|
||||
ContainerWriter::END_OF_STREAM : 0);
|
||||
LOG(PR_LOG_DEBUG, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
||||
rv = WriteEncodedDataToMuxer(mVideoEncoder.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("ERROR! Fail to write encoded track to the media container.");
|
||||
mState = ENCODE_ERROR;
|
||||
LOG(PR_LOG_ERROR, ("Fail to write video encoder data to muxer"));
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(PR_LOG_DEBUG, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
||||
// In audio only or video only case, let unavailable track's flag to be true.
|
||||
bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
|
||||
bool isVideoCompleted = (mVideoEncoder && mVideoEncoder->IsEncodingComplete()) || !mVideoEncoder;
|
||||
rv = mWriter->GetContainerData(aOutputBufs,
|
||||
mAudioEncoder->IsEncodingComplete() ?
|
||||
isAudioCompleted && isVideoCompleted ?
|
||||
ContainerWriter::FLUSH_NEEDED : 0);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Successfully get the copy of final container data from writer.
|
||||
reloop = false;
|
||||
}
|
||||
|
||||
mState = (mAudioEncoder->IsEncodingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
|
||||
mState = (mWriter->IsWritingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
|
||||
LOG(PR_LOG_DEBUG, ("END ENCODE_TRACK TimeStamp = %f "
|
||||
"mState = %d aComplete %d vComplete %d",
|
||||
GetEncodeTimeStamp(), mState, isAudioCompleted, isVideoCompleted));
|
||||
break;
|
||||
}
|
||||
|
||||
case ENCODE_DONE:
|
||||
LOG("MediaEncoder has been shutdown.");
|
||||
mShutdown = true;
|
||||
reloop = false;
|
||||
break;
|
||||
case ENCODE_ERROR:
|
||||
LOG("ERROR! MediaEncoder got error!");
|
||||
LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
|
||||
mShutdown = true;
|
||||
reloop = false;
|
||||
break;
|
||||
@ -234,4 +257,52 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEncoder::WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder)
|
||||
{
|
||||
if (aTrackEncoder == nullptr) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (aTrackEncoder->IsEncodingComplete()) {
|
||||
return NS_OK;
|
||||
}
|
||||
EncodedFrameContainer encodedVideoData;
|
||||
nsresult rv = aTrackEncoder->GetEncodedTrack(encodedVideoData);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Encoding might be canceled.
|
||||
LOG(PR_LOG_ERROR, ("Error! Fail to get encoded data from video encoder."));
|
||||
mState = ENCODE_ERROR;
|
||||
return rv;
|
||||
}
|
||||
rv = mWriter->WriteEncodedTrack(encodedVideoData,
|
||||
aTrackEncoder->IsEncodingComplete() ?
|
||||
ContainerWriter::END_OF_STREAM : 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_ERROR, ("Error! Fail to write encoded video track to the media container."));
|
||||
mState = ENCODE_ERROR;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaEncoder::CopyMetadataToMuxer(TrackEncoder *aTrackEncoder)
|
||||
{
|
||||
if (aTrackEncoder == nullptr) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<TrackMetadataBase> meta = aTrackEncoder->GetMetadata();
|
||||
if (meta == nullptr) {
|
||||
LOG(PR_LOG_ERROR, ("Error! metadata = null"));
|
||||
mState = ENCODE_ERROR;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
nsresult rv = mWriter->SetMetadata(meta);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_ERROR, ("Error! SetMetadata fail"));
|
||||
mState = ENCODE_ERROR;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ public :
|
||||
: mWriter(aWriter)
|
||||
, mAudioEncoder(aAudioEncoder)
|
||||
, mVideoEncoder(aVideoEncoder)
|
||||
, mStartTime(TimeStamp::Now())
|
||||
, mMIMEType(aMIMEType)
|
||||
, mState(MediaEncoder::ENCODE_METADDATA)
|
||||
, mShutdown(false)
|
||||
@ -91,8 +92,12 @@ public :
|
||||
* to create the encoder. For now, default aMIMEType to "audio/ogg" and use
|
||||
* Ogg+Opus if it is empty.
|
||||
*/
|
||||
static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType);
|
||||
|
||||
static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
|
||||
uint8_t aTrackTypes = ContainerWriter::HAS_AUDIO);
|
||||
/**
|
||||
* Check if run on Encoder thread
|
||||
*/
|
||||
static bool OnEncoderThread();
|
||||
/**
|
||||
* Encodes the raw track data and returns the final container data. Assuming
|
||||
* it is called on a single worker thread. The buffer of container data is
|
||||
@ -128,12 +133,24 @@ public :
|
||||
}
|
||||
|
||||
private:
|
||||
// Get encoded data from trackEncoder and write to muxer
|
||||
nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
|
||||
// Get metadata from trackEncoder and copy to muxer
|
||||
nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder);
|
||||
nsAutoPtr<ContainerWriter> mWriter;
|
||||
nsAutoPtr<AudioTrackEncoder> mAudioEncoder;
|
||||
nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
|
||||
TimeStamp mStartTime;
|
||||
nsString mMIMEType;
|
||||
int mState;
|
||||
bool mShutdown;
|
||||
// Get duration from create encoder, for logging purpose
|
||||
double GetEncodeTimeStamp()
|
||||
{
|
||||
TimeDuration decodeTime;
|
||||
decodeTime = TimeStamp::Now() - mStartTime;
|
||||
return decodeTime.ToMilliseconds();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -162,7 +162,9 @@ OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
if (rc) {
|
||||
ProduceOggPage(aOutputBufs);
|
||||
}
|
||||
|
||||
if (aFlags & ContainerWriter::FLUSH_NEEDED) {
|
||||
mIsWritingComplete = true;
|
||||
}
|
||||
return (rc > 0) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ public:
|
||||
|
||||
// Check metadata type integrity and reject unacceptable track encoder.
|
||||
nsresult SetMetadata(TrackMetadataBase* aMetadata) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsresult Init();
|
||||
|
||||
|
@ -69,13 +69,23 @@ function startTest(test, token) {
|
||||
'Events fired from ondataavailable should be BlobEvent');
|
||||
is(evt.type, 'dataavailable',
|
||||
'Event type should dataavailable');
|
||||
ok(evt.data.size > 0,
|
||||
'Blob data received should be greater than zero');
|
||||
is(evt.data.type, expectedMimeType,
|
||||
'Blob data received should have type = ' + expectedMimeType);
|
||||
|
||||
is(mediaRecorder.mimeType, expectedMimeType,
|
||||
'Mime type in ondataavailable = ' + expectedMimeType);
|
||||
// The initialization of encoder can be cancelled.
|
||||
// On some platforms, the stop method may run after media stream track
|
||||
// available, so the blob can contain the header data.
|
||||
if (evt.data.size > 0) {
|
||||
is(evt.data.type, expectedMimeType,
|
||||
'Blob data received and should have mime type');
|
||||
is(mediaRecorder.mimeType, expectedMimeType,
|
||||
'Media Recorder mime type in ondataavailable = ' + expectedMimeType);
|
||||
} else if (evt.data.size === 0) {
|
||||
is(mediaRecorder.mimeType, '',
|
||||
'Blob data mime type is empty');
|
||||
is(mediaRecorder.mimeType, '',
|
||||
'Media Recorder mime type in ondataavailable is empty');
|
||||
} else {
|
||||
ok(false, 'Blob size can not be negative');
|
||||
}
|
||||
|
||||
// onstop should not have fired before ondataavailable
|
||||
if (onStopFired) {
|
||||
|
@ -123,7 +123,6 @@ public:
|
||||
info.spaceType() = mSpaceType;
|
||||
|
||||
EnableFMRadio(info);
|
||||
IFMRadioService::Singleton()->EnableAudio(true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -302,9 +301,9 @@ FMRadioService::EnableAudio(bool aAudioEnabled)
|
||||
return;
|
||||
}
|
||||
|
||||
bool AudioEnabled;
|
||||
audioManager->GetFmRadioAudioEnabled(&AudioEnabled);
|
||||
if (AudioEnabled != aAudioEnabled) {
|
||||
bool audioEnabled;
|
||||
audioManager->GetFmRadioAudioEnabled(&audioEnabled);
|
||||
if (audioEnabled != aAudioEnabled) {
|
||||
audioManager->SetFmRadioAudioEnabled(aAudioEnabled);
|
||||
}
|
||||
}
|
||||
@ -736,6 +735,12 @@ FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
|
||||
// radio is enabled, we have to set the frequency first.
|
||||
SetFMRadioFrequency(mPendingFrequencyInKHz);
|
||||
|
||||
// Bug 949855: enable audio after the FM radio HW is enabled, to make sure
|
||||
// 'hw.fm.isAnalog' could be detected as |true| during first time launch.
|
||||
// This case is for audio output on analog path, i.e. 'ro.moz.fm.noAnalog'
|
||||
// is not |true|.
|
||||
EnableAudio(true);
|
||||
|
||||
// Update the current frequency without sending the`FrequencyChanged`
|
||||
// event, to make sure the FM app will get the right frequency when the
|
||||
// `EnabledChange` event is sent.
|
||||
|
@ -1769,7 +1769,8 @@ TabChild::UpdateTapState(const WidgetTouchEvent& aEvent, nsEventStatus aStatus)
|
||||
return;
|
||||
}
|
||||
if (aStatus == nsEventStatus_eConsumeNoDefault ||
|
||||
nsIPresShell::gPreventMouseEvents) {
|
||||
nsIPresShell::gPreventMouseEvents ||
|
||||
aEvent.mFlags.mMultipleActionsPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1872,10 +1873,11 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
|
||||
|
||||
if (innerWindow && innerWindow->HasTouchEventListeners()) {
|
||||
SendContentReceivedTouch(aGuid, nsIPresShell::gPreventMouseEvents);
|
||||
SendContentReceivedTouch(aGuid, nsIPresShell::gPreventMouseEvents ||
|
||||
localEvent.mFlags.mMultipleActionsPrevented);
|
||||
}
|
||||
} else {
|
||||
UpdateTapState(aEvent, status);
|
||||
UpdateTapState(localEvent, status);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2440,6 +2440,9 @@ add_test(function test_fetch_icc_recodes() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify SimRecordHelper.readMWIS
|
||||
*/
|
||||
add_test(function test_read_mwis() {
|
||||
let worker = newUint8Worker();
|
||||
let helper = worker.GsmPDUHelper;
|
||||
@ -2506,6 +2509,9 @@ add_test(function test_read_mwis() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify SimRecordHelper.updateMWIS
|
||||
*/
|
||||
add_test(function test_update_mwis() {
|
||||
let worker = newUint8Worker();
|
||||
let pduHelper = worker.GsmPDUHelper;
|
||||
@ -2610,3 +2616,110 @@ add_test(function test_update_mwis() {
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify the call flow of receiving Class 2 SMS stored in SIM:
|
||||
* 1. UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM.
|
||||
* 2. SimRecordHelper.readSMS().
|
||||
* 3. sendChromeMessage() with rilMessageType == "sms-received".
|
||||
*/
|
||||
add_test(function test_read_new_sms_on_sim() {
|
||||
// Instead of reusing newUint8Worker defined in this file,
|
||||
// we define our own worker to fake the methods in WorkerBuffer dynamically.
|
||||
function newSmsOnSimWorkerHelper() {
|
||||
let _postedMessage;
|
||||
let _worker = newWorker({
|
||||
postRILMessage: function fakePostRILMessage(data) {
|
||||
},
|
||||
postMessage: function fakePostMessage(message) {
|
||||
_postedMessage = message;
|
||||
}
|
||||
});
|
||||
|
||||
_worker.debug = do_print;
|
||||
|
||||
return {
|
||||
get postedMessage() {
|
||||
return _postedMessage;
|
||||
},
|
||||
get worker() {
|
||||
return _worker;
|
||||
},
|
||||
fakeWokerBuffer: function fakeWokerBuffer() {
|
||||
let index = 0; // index for read
|
||||
let buf = [];
|
||||
_worker.Buf.writeUint8 = function (value) {
|
||||
buf.push(value);
|
||||
};
|
||||
_worker.Buf.readUint8 = function () {
|
||||
return buf[index++];
|
||||
};
|
||||
_worker.Buf.seekIncoming = function (offset) {
|
||||
index += offset;
|
||||
};
|
||||
_worker.Buf.getReadAvailable = function () {
|
||||
return buf.length - index;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let workerHelper = newSmsOnSimWorkerHelper();
|
||||
let worker = workerHelper.worker;
|
||||
|
||||
worker.ICCIOHelper.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) {
|
||||
// SimStatus: Unread, SMSC:+0123456789, Sender: +9876543210, Text: How are you?
|
||||
let SimSmsPduHex = "0306911032547698040A9189674523010000208062917314080CC8F71D14969741F977FD07"
|
||||
// In 4.2.25 EF_SMS Short Messages of 3GPP TS 31.102:
|
||||
// 1. Record length == 176 bytes.
|
||||
// 2. Any bytes in the record following the TPDU shall be filled with 'FF'.
|
||||
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
|
||||
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
|
||||
|
||||
workerHelper.fakeWokerBuffer();
|
||||
|
||||
worker.Buf.writeString(SimSmsPduHex);
|
||||
|
||||
options.recordSize = 176; // Record length is fixed to 176 bytes.
|
||||
if (options.callback) {
|
||||
options.callback(options);
|
||||
}
|
||||
};
|
||||
|
||||
function newSmsOnSimParcel() {
|
||||
let data = new Uint8Array(4 + 4); // Int32List with 1 element.
|
||||
let offset = 0;
|
||||
|
||||
function writeInt(value) {
|
||||
data[offset++] = value & 0xFF;
|
||||
data[offset++] = (value >> 8) & 0xFF;
|
||||
data[offset++] = (value >> 16) & 0xFF;
|
||||
data[offset++] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
writeInt(1); // Length of Int32List
|
||||
writeInt(1); // RecordNum = 1.
|
||||
|
||||
return newIncomingParcel(-1,
|
||||
RESPONSE_TYPE_UNSOLICITED,
|
||||
UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM,
|
||||
data);
|
||||
}
|
||||
|
||||
function do_test() {
|
||||
worker.onRILMessage(newSmsOnSimParcel());
|
||||
|
||||
let postedMessage = workerHelper.postedMessage;
|
||||
|
||||
do_check_eq("sms-received", postedMessage.rilMessageType);
|
||||
do_check_eq("+0123456789", postedMessage.SMSC);
|
||||
do_check_eq("+9876543210", postedMessage.sender);
|
||||
do_check_eq("How are you?", postedMessage.fullBody);
|
||||
}
|
||||
|
||||
do_test();
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -124,16 +124,17 @@ public:
|
||||
CSSRect GetExpandedScrollableRect() const
|
||||
{
|
||||
CSSRect scrollableRect = mScrollableRect;
|
||||
if (scrollableRect.width < mCompositionBounds.width) {
|
||||
CSSRect compBounds = CalculateCompositedRectInCssPixels();
|
||||
if (scrollableRect.width < compBounds.width) {
|
||||
scrollableRect.x = std::max(0.f,
|
||||
scrollableRect.x - (mCompositionBounds.width - scrollableRect.width));
|
||||
scrollableRect.width = mCompositionBounds.width;
|
||||
scrollableRect.x - (compBounds.width - scrollableRect.width));
|
||||
scrollableRect.width = compBounds.width;
|
||||
}
|
||||
|
||||
if (scrollableRect.height < mCompositionBounds.height) {
|
||||
if (scrollableRect.height < compBounds.height) {
|
||||
scrollableRect.y = std::max(0.f,
|
||||
scrollableRect.y - (mCompositionBounds.height - scrollableRect.height));
|
||||
scrollableRect.height = mCompositionBounds.height;
|
||||
scrollableRect.y - (compBounds.height - scrollableRect.height));
|
||||
scrollableRect.height = compBounds.height;
|
||||
}
|
||||
|
||||
return scrollableRect;
|
||||
|
@ -778,7 +778,10 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent
|
||||
SetState(PANNING);
|
||||
mX.StartTouch(aEvent.mFocusPoint.x);
|
||||
mY.StartTouch(aEvent.mFocusPoint.y);
|
||||
} else {
|
||||
SetState(NOTHING);
|
||||
}
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
ScheduleComposite();
|
||||
|
@ -287,6 +287,8 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
mState = GESTURE_NONE;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mState == GESTURE_WAITING_PINCH) {
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
|
||||
if (aClearTouches) {
|
||||
|
Loading…
Reference in New Issue
Block a user