
On Android, we would like to use MediaCodec output buffers to hold decoded frames until they can be rendered to a texture. There can only be one texture buffer used at the same time and therefore the calculated decode time in VCMTiming will be wrong since that calculation will also include the time where the decoder waited for the upper layers (that depend on network jitter and actual render time) to release the frame. This new method will be used in https://codereview.webrtc.org/1422963003/ BUG=webrtc:4993 R=stefan@webrtc.org TBR=mflodman@webrtc.org Review URL: https://codereview.webrtc.org/1414693006 . Cr-Commit-Position: refs/heads/master@{#10576}
209 lines
5.8 KiB
C++
209 lines
5.8 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "webrtc/modules/video_coding/main/interface/video_coding.h"
|
|
#include "webrtc/modules/video_coding/main/source/generic_decoder.h"
|
|
#include "webrtc/modules/video_coding/main/source/internal_defines.h"
|
|
#include "webrtc/system_wrappers/include/clock.h"
|
|
#include "webrtc/system_wrappers/include/logging.h"
|
|
|
|
namespace webrtc {
|
|
|
|
VCMDecodedFrameCallback::VCMDecodedFrameCallback(VCMTiming& timing,
|
|
Clock* clock)
|
|
:
|
|
_critSect(CriticalSectionWrapper::CreateCriticalSection()),
|
|
_clock(clock),
|
|
_receiveCallback(NULL),
|
|
_timing(timing),
|
|
_timestampMap(kDecoderFrameMemoryLength),
|
|
_lastReceivedPictureID(0)
|
|
{
|
|
}
|
|
|
|
VCMDecodedFrameCallback::~VCMDecodedFrameCallback()
|
|
{
|
|
delete _critSect;
|
|
}
|
|
|
|
void VCMDecodedFrameCallback::SetUserReceiveCallback(
|
|
VCMReceiveCallback* receiveCallback)
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
_receiveCallback = receiveCallback;
|
|
}
|
|
|
|
VCMReceiveCallback* VCMDecodedFrameCallback::UserReceiveCallback()
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
return _receiveCallback;
|
|
}
|
|
|
|
int32_t VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage) {
|
|
return Decoded(decodedImage, -1);
|
|
}
|
|
|
|
int32_t VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage,
|
|
int64_t decode_time_ms) {
|
|
// TODO(holmer): We should improve this so that we can handle multiple
|
|
// callbacks from one call to Decode().
|
|
VCMFrameInformation* frameInfo;
|
|
VCMReceiveCallback* callback;
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
frameInfo = _timestampMap.Pop(decodedImage.timestamp());
|
|
callback = _receiveCallback;
|
|
}
|
|
|
|
if (frameInfo == NULL) {
|
|
LOG(LS_WARNING) << "Too many frames backed up in the decoder, dropping "
|
|
"this one.";
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
|
}
|
|
|
|
const int64_t now_ms = _clock->TimeInMilliseconds();
|
|
if (decode_time_ms < 0) {
|
|
decode_time_ms =
|
|
static_cast<int32_t>(now_ms - frameInfo->decodeStartTimeMs);
|
|
}
|
|
_timing.StopDecodeTimer(
|
|
decodedImage.timestamp(),
|
|
decode_time_ms,
|
|
now_ms,
|
|
frameInfo->renderTimeMs);
|
|
|
|
if (callback != NULL)
|
|
{
|
|
decodedImage.set_render_time_ms(frameInfo->renderTimeMs);
|
|
decodedImage.set_rotation(frameInfo->rotation);
|
|
callback->FrameToRender(decodedImage);
|
|
}
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
|
}
|
|
|
|
int32_t
|
|
VCMDecodedFrameCallback::ReceivedDecodedReferenceFrame(
|
|
const uint64_t pictureId)
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
if (_receiveCallback != NULL)
|
|
{
|
|
return _receiveCallback->ReceivedDecodedReferenceFrame(pictureId);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int32_t
|
|
VCMDecodedFrameCallback::ReceivedDecodedFrame(const uint64_t pictureId)
|
|
{
|
|
_lastReceivedPictureID = pictureId;
|
|
return 0;
|
|
}
|
|
|
|
uint64_t VCMDecodedFrameCallback::LastReceivedPictureID() const
|
|
{
|
|
return _lastReceivedPictureID;
|
|
}
|
|
|
|
void VCMDecodedFrameCallback::Map(uint32_t timestamp,
|
|
VCMFrameInformation* frameInfo) {
|
|
CriticalSectionScoped cs(_critSect);
|
|
_timestampMap.Add(timestamp, frameInfo);
|
|
}
|
|
|
|
int32_t VCMDecodedFrameCallback::Pop(uint32_t timestamp)
|
|
{
|
|
CriticalSectionScoped cs(_critSect);
|
|
if (_timestampMap.Pop(timestamp) == NULL)
|
|
{
|
|
return VCM_GENERAL_ERROR;
|
|
}
|
|
return VCM_OK;
|
|
}
|
|
|
|
VCMGenericDecoder::VCMGenericDecoder(VideoDecoder& decoder, bool isExternal)
|
|
:
|
|
_callback(NULL),
|
|
_frameInfos(),
|
|
_nextFrameInfoIdx(0),
|
|
_decoder(decoder),
|
|
_codecType(kVideoCodecUnknown),
|
|
_isExternal(isExternal),
|
|
_keyFrameDecoded(false)
|
|
{
|
|
}
|
|
|
|
VCMGenericDecoder::~VCMGenericDecoder()
|
|
{
|
|
}
|
|
|
|
int32_t VCMGenericDecoder::InitDecode(const VideoCodec* settings,
|
|
int32_t numberOfCores)
|
|
{
|
|
_codecType = settings->codecType;
|
|
|
|
return _decoder.InitDecode(settings, numberOfCores);
|
|
}
|
|
|
|
int32_t VCMGenericDecoder::Decode(const VCMEncodedFrame& frame,
|
|
int64_t nowMs)
|
|
{
|
|
_frameInfos[_nextFrameInfoIdx].decodeStartTimeMs = nowMs;
|
|
_frameInfos[_nextFrameInfoIdx].renderTimeMs = frame.RenderTimeMs();
|
|
_frameInfos[_nextFrameInfoIdx].rotation = frame.rotation();
|
|
_callback->Map(frame.TimeStamp(), &_frameInfos[_nextFrameInfoIdx]);
|
|
|
|
_nextFrameInfoIdx = (_nextFrameInfoIdx + 1) % kDecoderFrameMemoryLength;
|
|
int32_t ret = _decoder.Decode(frame.EncodedImage(),
|
|
frame.MissingFrame(),
|
|
frame.FragmentationHeader(),
|
|
frame.CodecSpecific(),
|
|
frame.RenderTimeMs());
|
|
|
|
if (ret < WEBRTC_VIDEO_CODEC_OK)
|
|
{
|
|
LOG(LS_WARNING) << "Failed to decode frame with timestamp "
|
|
<< frame.TimeStamp() << ", error code: " << ret;
|
|
_callback->Pop(frame.TimeStamp());
|
|
return ret;
|
|
}
|
|
else if (ret == WEBRTC_VIDEO_CODEC_NO_OUTPUT ||
|
|
ret == WEBRTC_VIDEO_CODEC_REQUEST_SLI)
|
|
{
|
|
// No output
|
|
_callback->Pop(frame.TimeStamp());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int32_t
|
|
VCMGenericDecoder::Release()
|
|
{
|
|
return _decoder.Release();
|
|
}
|
|
|
|
int32_t VCMGenericDecoder::Reset()
|
|
{
|
|
return _decoder.Reset();
|
|
}
|
|
|
|
int32_t VCMGenericDecoder::RegisterDecodeCompleteCallback(VCMDecodedFrameCallback* callback)
|
|
{
|
|
_callback = callback;
|
|
return _decoder.RegisterDecodeCompleteCallback(callback);
|
|
}
|
|
|
|
bool VCMGenericDecoder::External() const
|
|
{
|
|
return _isExternal;
|
|
}
|
|
|
|
} // namespace
|