зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1198664 - Part 2 - Use looper to process decoder tasks. r=bwu
This commit is contained in:
Родитель
3e0637f43f
Коммит
993994a080
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <android/log.h>
|
||||
#define MCP_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "MediaCodecProxy", __VA_ARGS__)
|
||||
#define TIMEOUT_DEQUEUE_INPUTBUFFER_MS 1000000ll
|
||||
|
||||
namespace android {
|
||||
|
||||
|
@ -571,7 +570,8 @@ bool MediaCodecProxy::UpdateOutputBuffers()
|
|||
}
|
||||
|
||||
status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
|
||||
int64_t aTimestampUsecs, uint64_t aflags)
|
||||
int64_t aTimestampUsecs, uint64_t aflags,
|
||||
int64_t aTimeoutUs)
|
||||
{
|
||||
// Read Lock for mCodec
|
||||
{
|
||||
|
@ -583,9 +583,11 @@ status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
|
|||
}
|
||||
|
||||
size_t index;
|
||||
status_t err = dequeueInputBuffer(&index, TIMEOUT_DEQUEUE_INPUTBUFFER_MS);
|
||||
status_t err = dequeueInputBuffer(&index, aTimeoutUs);
|
||||
if (err != OK) {
|
||||
MCP_LOG("dequeueInputBuffer returned %d", err);
|
||||
if (err != -EAGAIN) {
|
||||
MCP_LOG("dequeueInputBuffer returned %d", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ public:
|
|||
|
||||
// If aData is null, will notify decoder input EOS
|
||||
status_t Input(const uint8_t* aData, uint32_t aDataSize,
|
||||
int64_t aTimestampUsecs, uint64_t flags);
|
||||
int64_t aTimestampUsecs, uint64_t flags, int64_t aTimeoutUs = 0);
|
||||
status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
|
||||
bool Prepare();
|
||||
void ReleaseMediaResources();
|
||||
|
|
|
@ -21,12 +21,14 @@
|
|||
#include "MediaData.h"
|
||||
#include "MediaInfo.h"
|
||||
|
||||
#define CODECCONFIG_TIMEOUT_US 10000LL
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
|
||||
|
||||
#include <android/log.h>
|
||||
#define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
|
||||
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
|
||||
|
||||
using namespace android;
|
||||
typedef android::MediaCodecProxy MediaCodecProxy;
|
||||
|
@ -92,7 +94,8 @@ GonkAudioDecoderManager::InitMediaCodecProxy()
|
|||
|
||||
if (mMimeType.EqualsLiteral("audio/mp4a-latm")) {
|
||||
rv = mDecoder->Input(mCodecSpecificData->Elements(), mCodecSpecificData->Length(), 0,
|
||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
|
||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
|
||||
CODECCONFIG_TIMEOUT_US);
|
||||
}
|
||||
|
||||
if (rv == OK) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include <android/log.h>
|
||||
#define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
|
||||
#define INPUT_TIMEOUT_US 0LL // Don't wait for buffer if none is available.
|
||||
#define MIN_QUEUED_SAMPLES 2
|
||||
|
||||
extern PRLogModuleInfo* GetPDMLog();
|
||||
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
@ -45,40 +47,53 @@ GonkDecoderManager::InitLoopers(MediaData::Type aType)
|
|||
nsresult
|
||||
GonkDecoderManager::Input(MediaRawData* aSample)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsRefPtr<MediaRawData> sample;
|
||||
|
||||
if (!aSample) {
|
||||
if (aSample) {
|
||||
sample = aSample;
|
||||
} else {
|
||||
// It means EOS with empty sample.
|
||||
sample = new MediaRawData();
|
||||
} else {
|
||||
sample = aSample;
|
||||
}
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mQueuedSamples.AppendElement(sample);
|
||||
}
|
||||
|
||||
mQueuedSamples.AppendElement(sample);
|
||||
sp<AMessage> input = new AMessage(kNotifyProcessInput, id());
|
||||
if (!aSample) {
|
||||
input->setInt32("input-eos", 1);
|
||||
}
|
||||
input->post();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
GonkDecoderManager::ProcessQueuedSample()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
status_t rv;
|
||||
while (mQueuedSamples.Length()) {
|
||||
nsRefPtr<MediaRawData> data = mQueuedSamples.ElementAt(0);
|
||||
{
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
rv = mDecoder->Input(reinterpret_cast<const uint8_t*>(data->Data()),
|
||||
data->Size(),
|
||||
data->mTime,
|
||||
0);
|
||||
0,
|
||||
INPUT_TIMEOUT_US);
|
||||
}
|
||||
if (rv == OK) {
|
||||
mQueuedSamples.RemoveElementAt(0);
|
||||
mWaitOutput.AppendElement(data->mOffset);
|
||||
} else if (rv == -EAGAIN || rv == -ETIMEDOUT) {
|
||||
// In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill
|
||||
// buffer on time.
|
||||
return NS_OK;
|
||||
break;
|
||||
} else {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return mQueuedSamples.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -95,10 +110,13 @@ GonkDecoderManager::Flush()
|
|||
|
||||
mLastTime = 0;
|
||||
|
||||
if (mDecoder->flush() != OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
MonitorAutoLock lock(mFlushMonitor);
|
||||
mIsFlushing = true;
|
||||
sp<AMessage> flush = new AMessage(kNotifyProcessFlush, id());
|
||||
flush->post();
|
||||
while (mIsFlushing) {
|
||||
lock.Wait();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -123,10 +141,112 @@ GonkDecoderManager::HasQueuedSample()
|
|||
return mQueuedSamples.Length();
|
||||
}
|
||||
|
||||
void
|
||||
GonkDecoderManager::ProcessInput(bool aEndOfStream)
|
||||
{
|
||||
status_t rv = ProcessQueuedSamples();
|
||||
if (rv >= 0) {
|
||||
if (!aEndOfStream && rv <= MIN_QUEUED_SAMPLES) {
|
||||
mDecodeCallback->InputExhausted();
|
||||
}
|
||||
|
||||
if (mToDo.get() == nullptr) {
|
||||
mToDo = new AMessage(kNotifyDecoderActivity, id());
|
||||
if (aEndOfStream) {
|
||||
mToDo->setInt32("input-eos", 1);
|
||||
}
|
||||
mDecoder->requestActivityNotification(mToDo);
|
||||
}
|
||||
} else {
|
||||
GMDD_LOG("input processed: error#%d", rv);
|
||||
mDecodeCallback->Error();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDecoderManager::ProcessFlush()
|
||||
{
|
||||
MonitorAutoLock lock(mFlushMonitor);
|
||||
mWaitOutput.Clear();
|
||||
if (mDecoder->flush() != OK) {
|
||||
GMDD_LOG("flush error");
|
||||
mDecodeCallback->Error();
|
||||
}
|
||||
mIsFlushing = false;
|
||||
lock.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
GonkDecoderManager::ProcessToDo(bool aEndOfStream)
|
||||
{
|
||||
MOZ_ASSERT(mToDo.get() != nullptr);
|
||||
mToDo.clear();
|
||||
|
||||
if (HasQueuedSample()) {
|
||||
status_t pendingInput = ProcessQueuedSamples();
|
||||
if (pendingInput < 0) {
|
||||
mDecodeCallback->Error();
|
||||
return;
|
||||
}
|
||||
if (!aEndOfStream && pendingInput <= MIN_QUEUED_SAMPLES) {
|
||||
mDecodeCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
while (mWaitOutput.Length() > 0) {
|
||||
nsRefPtr<MediaData> output;
|
||||
int64_t offset = mWaitOutput.ElementAt(0);
|
||||
rv = Output(offset, output);
|
||||
if (rv == NS_OK) {
|
||||
mWaitOutput.RemoveElementAt(0);
|
||||
mDecodeCallback->Output(output);
|
||||
} else if (rv == NS_ERROR_ABORT) {
|
||||
GMDD_LOG("eos output");
|
||||
mWaitOutput.RemoveElementAt(0);
|
||||
MOZ_ASSERT(mQueuedSamples.IsEmpty());
|
||||
MOZ_ASSERT(mWaitOutput.IsEmpty());
|
||||
// EOS
|
||||
if (output) {
|
||||
mDecodeCallback->Output(output);
|
||||
}
|
||||
mDecodeCallback->DrainComplete();
|
||||
return;
|
||||
} else if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
break;
|
||||
} else {
|
||||
mDecodeCallback->Error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasQueuedSample() || mWaitOutput.Length() > 0) {
|
||||
mToDo = new AMessage(kNotifyDecoderActivity, id());
|
||||
mDecoder->requestActivityNotification(mToDo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GonkDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
|
||||
{
|
||||
switch (aMessage->what()) {
|
||||
case kNotifyProcessInput:
|
||||
{
|
||||
int32_t eos = 0;
|
||||
ProcessInput(aMessage->findInt32("input-eos", &eos) && eos);
|
||||
break;
|
||||
}
|
||||
case kNotifyProcessFlush:
|
||||
{
|
||||
ProcessFlush();
|
||||
break;
|
||||
}
|
||||
case kNotifyDecoderActivity:
|
||||
{
|
||||
int32_t eos = 0;
|
||||
ProcessToDo(aMessage->findInt32("input-eos", &eos) && eos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
TRESPASS();
|
||||
|
@ -139,10 +259,7 @@ GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
|
|||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mManager(aManager)
|
||||
, mSignaledEOS(false)
|
||||
, mDrainComplete(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
|
||||
mManager->SetDecodeCallback(aCallback);
|
||||
|
@ -156,8 +273,6 @@ GonkMediaDataDecoder::~GonkMediaDataDecoder()
|
|||
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||
GonkMediaDataDecoder::Init()
|
||||
{
|
||||
mDrainComplete = false;
|
||||
|
||||
return mManager->Init();
|
||||
}
|
||||
|
||||
|
@ -176,79 +291,10 @@ GonkMediaDataDecoder::Shutdown()
|
|||
nsresult
|
||||
GonkMediaDataDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(
|
||||
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
|
||||
this,
|
||||
&GonkMediaDataDecoder::ProcessDecode,
|
||||
nsRefPtr<MediaRawData>(aSample)));
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
mManager->Input(aSample);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
{
|
||||
nsresult rv = mManager->Input(aSample);
|
||||
if (rv != NS_OK) {
|
||||
NS_WARNING("GonkMediaDataDecoder failed to input data");
|
||||
GMDD_LOG("Failed to input data err: %d",int(rv));
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
if (aSample) {
|
||||
mLastStreamOffset = aSample->mOffset;
|
||||
}
|
||||
ProcessOutput();
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessOutput()
|
||||
{
|
||||
nsRefPtr<MediaData> output;
|
||||
nsresult rv = NS_ERROR_ABORT;
|
||||
|
||||
while (!mDrainComplete) {
|
||||
// There are samples in queue, try to send them into decoder when EOS.
|
||||
if (mSignaledEOS && mManager->HasQueuedSample()) {
|
||||
GMDD_LOG("ProcessOutput: drain all input samples");
|
||||
rv = mManager->Input(nullptr);
|
||||
}
|
||||
rv = mManager->Output(mLastStreamOffset, output);
|
||||
if (rv == NS_OK) {
|
||||
mCallback->Output(output);
|
||||
continue;
|
||||
} else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
|
||||
// Try to get more frames before getting EOS frame
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE && !mSignaledEOS) {
|
||||
mCallback->InputExhausted();
|
||||
return;
|
||||
}
|
||||
if (rv != NS_OK) {
|
||||
NS_WARNING("GonkMediaDataDecoder failed to output data");
|
||||
GMDD_LOG("Failed to output data");
|
||||
// GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
|
||||
if (rv == NS_ERROR_ABORT) {
|
||||
if (output) {
|
||||
mCallback->Output(output);
|
||||
}
|
||||
mCallback->DrainComplete();
|
||||
MOZ_ASSERT_IF(mSignaledEOS, !mManager->HasQueuedSample());
|
||||
mSignaledEOS = false;
|
||||
mDrainComplete = true;
|
||||
return;
|
||||
}
|
||||
GMDD_LOG("Callback error!");
|
||||
mCallback->Error();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkMediaDataDecoder::Flush()
|
||||
{
|
||||
|
@ -257,25 +303,13 @@ GonkMediaDataDecoder::Flush()
|
|||
// it's executing at all. Note the MP4Reader ignores all output while
|
||||
// flushing.
|
||||
mTaskQueue->Flush();
|
||||
mDrainComplete = false;
|
||||
return mManager->Flush();
|
||||
}
|
||||
|
||||
void
|
||||
GonkMediaDataDecoder::ProcessDrain()
|
||||
{
|
||||
// Notify decoder input EOS by sending a null data.
|
||||
ProcessDecode(nullptr);
|
||||
mSignaledEOS = true;
|
||||
ProcessOutput();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GonkMediaDataDecoder::Drain()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &GonkMediaDataDecoder::ProcessDrain);
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
mManager->Input(nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,19 +26,12 @@ public:
|
|||
|
||||
virtual ~GonkDecoderManager() {}
|
||||
|
||||
// Add samples into OMX decoder or queue them if decoder is out of input buffer.
|
||||
nsresult Input(MediaRawData* aSample);
|
||||
|
||||
// Produces decoded output, it blocks until output can be produced or a timeout
|
||||
// is expired or until EOS. Returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
|
||||
// if there's not enough data to produce more output. If this returns a failure
|
||||
// code other than NS_ERROR_NOT_AVAILABLE, an error will be reported to the
|
||||
// MP4Reader.
|
||||
// The overrided class should follow the same behaviour.
|
||||
virtual nsresult Output(int64_t aStreamOffset,
|
||||
nsRefPtr<MediaData>& aOutput) = 0;
|
||||
virtual nsRefPtr<InitPromise> Init() = 0;
|
||||
|
||||
// Asynchronously send sample into mDecoder. If out of input buffer, aSample
|
||||
// will be queued for later re-send.
|
||||
nsresult Input(MediaRawData* aSample);
|
||||
|
||||
// Flush the queued sample.
|
||||
nsresult Flush();
|
||||
|
||||
|
@ -59,6 +52,8 @@ protected:
|
|||
GonkDecoderManager()
|
||||
: mMutex("GonkDecoderManager")
|
||||
, mLastTime(0)
|
||||
, mFlushMonitor("GonkDecoderManager::Flush")
|
||||
, mIsFlushing(false)
|
||||
, mDecodeCallback(nullptr)
|
||||
{}
|
||||
|
||||
|
@ -66,6 +61,21 @@ protected:
|
|||
|
||||
void onMessageReceived(const android::sp<android::AMessage> &aMessage) override;
|
||||
|
||||
// Produces decoded output. It returns NS_OK on success, or NS_ERROR_NOT_AVAILABLE
|
||||
// when output is not produced yet.
|
||||
// If this returns a failure code other than NS_ERROR_NOT_AVAILABLE, an error
|
||||
// will be reported through mDecodeCallback.
|
||||
virtual nsresult Output(int64_t aStreamOffset,
|
||||
nsRefPtr<MediaData>& aOutput) = 0;
|
||||
|
||||
// Send queued samples to OMX. It returns how many samples are still in
|
||||
// queue after processing, or negtive error code if failed.
|
||||
int32_t ProcessQueuedSamples();
|
||||
|
||||
void ProcessInput(bool aEndOfStream);
|
||||
void ProcessFlush();
|
||||
void ProcessToDo(bool aEndOfStream);
|
||||
|
||||
nsRefPtr<MediaByteBuffer> mCodecSpecificData;
|
||||
|
||||
nsAutoCString mMimeType;
|
||||
|
@ -74,8 +84,18 @@ protected:
|
|||
android::sp<android::MediaCodecProxy> mDecoder;
|
||||
// Looper for mDecoder to run on.
|
||||
android::sp<android::ALooper> mDecodeLooper;
|
||||
// Looper to run decode tasks such as recycling output buffers.
|
||||
// Looper to run decode tasks such as processing input, output, flush, and
|
||||
// recycling output buffers.
|
||||
android::sp<android::ALooper> mTaskLooper;
|
||||
enum {
|
||||
// Decoder will send this to indicate internal state change such as input or
|
||||
// output buffers availability. Used to run pending input & output tasks.
|
||||
kNotifyDecoderActivity = 'nda ',
|
||||
// Signal the decoder to flush.
|
||||
kNotifyProcessFlush = 'npf ',
|
||||
// Used to process queued samples when there is new input.
|
||||
kNotifyProcessInput = 'npi ',
|
||||
};
|
||||
|
||||
MozPromiseHolder<InitPromise> mInitPromise;
|
||||
|
||||
|
@ -87,6 +107,17 @@ protected:
|
|||
|
||||
int64_t mLastTime; // The last decoded frame presentation time.
|
||||
|
||||
Monitor mFlushMonitor; // Waits for flushing to complete.
|
||||
bool mIsFlushing;
|
||||
|
||||
// Remembers the notification that is currently waiting for the decoder event
|
||||
// to avoid requesting more than one notification at the time, which is
|
||||
// forbidden by mDecoder.
|
||||
android::sp<android::AMessage> mToDo;
|
||||
|
||||
// Stores the offset of every output that needs to be read from mDecoder.
|
||||
nsTArray<int64_t> mWaitOutput;
|
||||
|
||||
MediaDataDecoderCallback* mDecodeCallback; // Reports decoder output or error.
|
||||
};
|
||||
|
||||
|
@ -114,33 +145,9 @@ public:
|
|||
nsresult Shutdown() override;
|
||||
|
||||
private:
|
||||
|
||||
// Called on the task queue. Inserts the sample into the decoder, and
|
||||
// extracts output if available, if aSample is null, it means there is
|
||||
// no data from source, it will notify the decoder EOS and flush all the
|
||||
// decoded frames.
|
||||
void ProcessDecode(MediaRawData* aSample);
|
||||
|
||||
// Called on the task queue. Extracts output if available, and delivers
|
||||
// it to the reader. Called after ProcessDecode() and ProcessDrain().
|
||||
void ProcessOutput();
|
||||
|
||||
// Called on the task queue. Orders the Gonk to drain, and then extracts
|
||||
// all available output.
|
||||
void ProcessDrain();
|
||||
|
||||
RefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
android::sp<GonkDecoderManager> mManager;
|
||||
|
||||
// The last offset into the media resource that was passed into Input().
|
||||
// This is used to approximate the decoder's position in the media resource.
|
||||
int64_t mLastStreamOffset;
|
||||
// Set it ture when there is no input data
|
||||
bool mSignaledEOS;
|
||||
// Set if there is no more output data from decoder
|
||||
bool mDrainComplete;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
#include "mozilla/layers/TextureClient.h"
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 3000
|
||||
#define CODECCONFIG_TIMEOUT_US 10000LL
|
||||
#define READ_OUTPUT_BUFFER_TIMEOUT_US 0LL
|
||||
|
||||
#include <android/log.h>
|
||||
#define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
|
||||
|
@ -416,7 +417,8 @@ GonkVideoDecoderManager::codecReserved()
|
|||
if (mMimeType.EqualsLiteral("video/mp4v-es")) {
|
||||
rv = mDecoder->Input(mCodecSpecificData->Elements(),
|
||||
mCodecSpecificData->Length(), 0,
|
||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
|
||||
android::MediaCodec::BUFFER_FLAG_CODECCONFIG,
|
||||
CODECCONFIG_TIMEOUT_US);
|
||||
}
|
||||
|
||||
if (rv != OK) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче