зеркало из https://github.com/mozilla/gecko-dev.git
235 строки
6.2 KiB
C++
235 строки
6.2 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
|
/* 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 "ISOMediaWriter.h"
|
|
#include "ISOControl.h"
|
|
#include "ISOMediaBoxes.h"
|
|
#include "ISOTrackMetadata.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "MediaEncoder.h"
|
|
#include "VideoUtils.h"
|
|
#include "GeckoProfiler.h"
|
|
|
|
#undef LOG
|
|
#ifdef MOZ_WIDGET_GONK
|
|
#include <android/log.h>
|
|
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
|
|
#else
|
|
#define LOG(args, ...)
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
const static uint32_t FRAG_DURATION = 2 * USECS_PER_S; // microsecond per unit
|
|
|
|
ISOMediaWriter::ISOMediaWriter(uint32_t aType, uint32_t aHint)
|
|
: ContainerWriter()
|
|
, mState(MUXING_HEAD)
|
|
, mBlobReady(false)
|
|
, mType(0)
|
|
{
|
|
if (aType & CREATE_AUDIO_TRACK) {
|
|
mType |= Audio_Track;
|
|
}
|
|
if (aType & CREATE_VIDEO_TRACK) {
|
|
mType |= Video_Track;
|
|
}
|
|
mControl = new ISOControl(aHint);
|
|
MOZ_COUNT_CTOR(ISOMediaWriter);
|
|
}
|
|
|
|
ISOMediaWriter::~ISOMediaWriter()
|
|
{
|
|
MOZ_COUNT_DTOR(ISOMediaWriter);
|
|
}
|
|
|
|
nsresult
|
|
ISOMediaWriter::RunState()
|
|
{
|
|
nsresult rv;
|
|
switch (mState) {
|
|
case MUXING_HEAD:
|
|
{
|
|
rv = mControl->GenerateFtyp();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = mControl->GenerateMoov();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mState = MUXING_FRAG;
|
|
break;
|
|
}
|
|
case MUXING_FRAG:
|
|
{
|
|
rv = mControl->GenerateMoof(mType);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool EOS;
|
|
if (ReadyToRunState(EOS) && EOS) {
|
|
mState = MUXING_DONE;
|
|
}
|
|
break;
|
|
}
|
|
case MUXING_DONE:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
mBlobReady = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
|
|
uint32_t aFlags)
|
|
{
|
|
PROFILER_LABEL("ISOMediaWriter", "WriteEncodedTrack",
|
|
js::ProfileEntry::Category::OTHER);
|
|
// Muxing complete, it doesn't allowed to reentry again.
|
|
if (mState == MUXING_DONE) {
|
|
MOZ_ASSERT(false);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
FragmentBuffer* frag = nullptr;
|
|
uint32_t len = aData.GetEncodedFrames().Length();
|
|
|
|
if (!len) {
|
|
// no frame? why bother to WriteEncodedTrack
|
|
return NS_OK;
|
|
}
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
RefPtr<EncodedFrame> frame(aData.GetEncodedFrames()[i]);
|
|
EncodedFrame::FrameType type = frame->GetFrameType();
|
|
if (type == EncodedFrame::AAC_AUDIO_FRAME ||
|
|
type == EncodedFrame::AAC_CSD ||
|
|
type == EncodedFrame::AMR_AUDIO_FRAME ||
|
|
type == EncodedFrame::AMR_AUDIO_CSD ||
|
|
type == EncodedFrame::EVRC_AUDIO_FRAME ||
|
|
type == EncodedFrame::EVRC_AUDIO_CSD) {
|
|
frag = mAudioFragmentBuffer;
|
|
} else if (type == EncodedFrame::AVC_I_FRAME ||
|
|
type == EncodedFrame::AVC_P_FRAME ||
|
|
type == EncodedFrame::AVC_B_FRAME ||
|
|
type == EncodedFrame::AVC_CSD) {
|
|
frag = mVideoFragmentBuffer;
|
|
} else {
|
|
MOZ_ASSERT(0);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
frag->AddFrame(frame);
|
|
}
|
|
|
|
// Encoder should send CSD (codec specific data) frame before sending the
|
|
// audio/video frames. When CSD data is ready, it is sufficient to generate a
|
|
// moov data. If encoder doesn't send CSD yet, muxer needs to wait before
|
|
// generating anything.
|
|
if (mType & Audio_Track && (!mAudioFragmentBuffer ||
|
|
!mAudioFragmentBuffer->HasCSD())) {
|
|
return NS_OK;
|
|
}
|
|
if (mType & Video_Track && (!mVideoFragmentBuffer ||
|
|
!mVideoFragmentBuffer->HasCSD())) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Only one FrameType in EncodedFrameContainer so it doesn't need to be
|
|
// inside the for-loop.
|
|
if (frag && (aFlags & END_OF_STREAM)) {
|
|
frag->SetEndOfStream();
|
|
}
|
|
|
|
nsresult rv;
|
|
bool EOS;
|
|
if (ReadyToRunState(EOS)) {
|
|
// Because track encoder won't generate new data after EOS, it needs to make
|
|
// sure the state reaches MUXING_DONE when EOS is signaled.
|
|
do {
|
|
rv = RunState();
|
|
} while (EOS && mState != MUXING_DONE);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool
|
|
ISOMediaWriter::ReadyToRunState(bool& aEOS)
|
|
{
|
|
aEOS = false;
|
|
bool bReadyToMux = true;
|
|
if ((mType & Audio_Track) && (mType & Video_Track)) {
|
|
if (!mAudioFragmentBuffer->HasEnoughData()) {
|
|
bReadyToMux = false;
|
|
}
|
|
if (!mVideoFragmentBuffer->HasEnoughData()) {
|
|
bReadyToMux = false;
|
|
}
|
|
|
|
if (mAudioFragmentBuffer->EOS() && mVideoFragmentBuffer->EOS()) {
|
|
aEOS = true;
|
|
bReadyToMux = true;
|
|
}
|
|
} else if (mType == Audio_Track) {
|
|
if (!mAudioFragmentBuffer->HasEnoughData()) {
|
|
bReadyToMux = false;
|
|
}
|
|
if (mAudioFragmentBuffer->EOS()) {
|
|
aEOS = true;
|
|
bReadyToMux = true;
|
|
}
|
|
} else if (mType == Video_Track) {
|
|
if (!mVideoFragmentBuffer->HasEnoughData()) {
|
|
bReadyToMux = false;
|
|
}
|
|
if (mVideoFragmentBuffer->EOS()) {
|
|
aEOS = true;
|
|
bReadyToMux = true;
|
|
}
|
|
}
|
|
|
|
return bReadyToMux;
|
|
}
|
|
|
|
nsresult
|
|
ISOMediaWriter::GetContainerData(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
|
|
uint32_t aFlags)
|
|
{
|
|
PROFILER_LABEL("ISOMediaWriter", "GetContainerData",
|
|
js::ProfileEntry::Category::OTHER);
|
|
if (mBlobReady) {
|
|
if (mState == MUXING_DONE) {
|
|
mIsWritingComplete = true;
|
|
}
|
|
mBlobReady = false;
|
|
return mControl->GetBufs(aOutputBufs);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
|
|
{
|
|
PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
|
|
js::ProfileEntry::Category::OTHER);
|
|
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
|
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
|
aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
|
|
mControl->SetMetadata(aMetadata);
|
|
mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
|
|
mControl->SetFragment(mAudioFragmentBuffer);
|
|
return NS_OK;
|
|
}
|
|
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AVC) {
|
|
mControl->SetMetadata(aMetadata);
|
|
mVideoFragmentBuffer = new FragmentBuffer(Video_Track, FRAG_DURATION);
|
|
mControl->SetFragment(mVideoFragmentBuffer);
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
} // namespace mozilla
|