mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-19 09:41:32 +08:00
After I made it so that the encoder internal data gets destroyed when
all outputs stop using it (fa7286f8), the media foundation h264 encoder
started having crashes on shutdown. After a lot of testing, I realized
that the reason it started happening is almost assuredly because active
encoding events had not yet been completed.
After making it wait on those events by calling DrainEvents(true), the
crashes stopped. So asynchronous actions were clearly still occurring
and it was shutting down while data was still being processed, thus
leading to a crash.
751 lines
15 KiB
C++
751 lines
15 KiB
C++
#include <obs-module.h>
|
|
#include <util/profiler.hpp>
|
|
|
|
#include "mf-common.hpp"
|
|
#include "mf-h264-encoder.hpp"
|
|
|
|
#include <codecapi.h>
|
|
#include <mferror.h>
|
|
|
|
using namespace MF;
|
|
|
|
static eAVEncH264VProfile MapProfile(H264Profile profile)
|
|
{
|
|
switch (profile) {
|
|
case H264ProfileBaseline: return eAVEncH264VProfile_Base;
|
|
case H264ProfileMain: return eAVEncH264VProfile_Main;
|
|
case H264ProfileHigh: return eAVEncH264VProfile_High;
|
|
default: return eAVEncH264VProfile_Base;
|
|
}
|
|
}
|
|
|
|
static eAVEncCommonRateControlMode MapRateControl(H264RateControl rc)
|
|
{
|
|
switch (rc) {
|
|
case H264RateControlCBR:
|
|
return eAVEncCommonRateControlMode_CBR;
|
|
case H264RateControlConstrainedVBR:
|
|
return eAVEncCommonRateControlMode_PeakConstrainedVBR;
|
|
case H264RateControlVBR:
|
|
return eAVEncCommonRateControlMode_UnconstrainedVBR;
|
|
case H264RateControlCQP:
|
|
return eAVEncCommonRateControlMode_Quality;
|
|
default:
|
|
return eAVEncCommonRateControlMode_CBR;
|
|
}
|
|
}
|
|
|
|
static UINT32 MapQpToQuality(H264QP &qp)
|
|
{
|
|
return 100 - (UINT32)floor(100.0 / 51.0 * qp.defaultQp + 0.5f);
|
|
}
|
|
|
|
static bool ProcessNV12(std::function<void(UINT32 height, INT32 plane)> func,
|
|
UINT32 height)
|
|
{
|
|
INT32 plane = 0;
|
|
|
|
func(height, plane++);
|
|
func(height / 2, plane);
|
|
|
|
return true;
|
|
}
|
|
|
|
H264Encoder::H264Encoder(const obs_encoder_t *encoder,
|
|
std::shared_ptr<EncoderDescriptor> descriptor,
|
|
UINT32 width,
|
|
UINT32 height,
|
|
UINT32 framerateNum,
|
|
UINT32 framerateDen,
|
|
H264Profile profile,
|
|
UINT32 bitrate)
|
|
: encoder(encoder),
|
|
descriptor(descriptor),
|
|
width(width),
|
|
height(height),
|
|
framerateNum(framerateNum),
|
|
framerateDen(framerateDen),
|
|
initialBitrate(bitrate),
|
|
profile(profile)
|
|
{}
|
|
|
|
H264Encoder::~H264Encoder()
|
|
{
|
|
// Make sure all events have finished before releasing.
|
|
// If you do not do this, you risk it releasing while there's still
|
|
// encoder activity, which can cause a crash with certain interfaces.
|
|
DrainEvent(true);
|
|
}
|
|
|
|
HRESULT H264Encoder::CreateMediaTypes(ComPtr<IMFMediaType> &i,
|
|
ComPtr<IMFMediaType> &o)
|
|
{
|
|
HRESULT hr;
|
|
HRC(MFCreateMediaType(&i));
|
|
HRC(MFCreateMediaType(&o));
|
|
|
|
HRC(i->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
|
|
HRC(i->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
|
|
HRC(MFSetAttributeSize(i, MF_MT_FRAME_SIZE, width, height));
|
|
HRC(MFSetAttributeRatio(i, MF_MT_FRAME_RATE, framerateNum,
|
|
framerateDen));
|
|
HRC(i->SetUINT32(MF_MT_INTERLACE_MODE,
|
|
MFVideoInterlaceMode::MFVideoInterlace_Progressive));
|
|
HRC(MFSetAttributeRatio(i, MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
|
|
|
|
HRC(o->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
|
|
HRC(o->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
|
|
HRC(MFSetAttributeSize(o, MF_MT_FRAME_SIZE, width, height));
|
|
HRC(MFSetAttributeRatio(o, MF_MT_FRAME_RATE, framerateNum,
|
|
framerateDen));
|
|
HRC(o->SetUINT32(MF_MT_AVG_BITRATE, initialBitrate * 1000));
|
|
HRC(o->SetUINT32(MF_MT_INTERLACE_MODE,
|
|
MFVideoInterlaceMode::MFVideoInterlace_Progressive));
|
|
HRC(MFSetAttributeRatio(o, MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
|
|
HRC(o->SetUINT32(MF_MT_MPEG2_LEVEL, (UINT32)-1));
|
|
HRC(o->SetUINT32(MF_MT_MPEG2_PROFILE, MapProfile(profile)));
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::DrainEvents()
|
|
{
|
|
HRESULT hr;
|
|
while ((hr = DrainEvent(false)) == S_OK);
|
|
if (hr == MF_E_NO_EVENTS_AVAILABLE)
|
|
hr = S_OK;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::DrainEvent(bool block)
|
|
{
|
|
HRESULT hr, eventStatus;
|
|
ComPtr<IMFMediaEvent> event;
|
|
MediaEventType type;
|
|
|
|
hr = eventGenerator->GetEvent(
|
|
block ? 0 : MF_EVENT_FLAG_NO_WAIT, &event);
|
|
|
|
if (hr != MF_E_NO_EVENTS_AVAILABLE && FAILED(hr))
|
|
goto fail;
|
|
if (hr == MF_E_NO_EVENTS_AVAILABLE)
|
|
return hr;
|
|
|
|
HRC(event->GetType(&type));
|
|
HRC(event->GetStatus(&eventStatus));
|
|
|
|
if (SUCCEEDED(eventStatus)) {
|
|
if (type == METransformNeedInput) {
|
|
inputRequests++;
|
|
}
|
|
else if (type == METransformHaveOutput) {
|
|
outputRequests++;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::InitializeEventGenerator()
|
|
{
|
|
HRESULT hr;
|
|
|
|
HRC(transform->QueryInterface(&eventGenerator));
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::InitializeExtraData()
|
|
{
|
|
HRESULT hr;
|
|
ComPtr<IMFMediaType> inputType;
|
|
UINT32 headerSize;
|
|
|
|
extraData.clear();
|
|
|
|
HRC(transform->GetOutputCurrentType(0, &inputType));
|
|
|
|
HRC(inputType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &headerSize));
|
|
|
|
extraData.resize(headerSize);
|
|
|
|
HRC(inputType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, extraData.data(),
|
|
headerSize, NULL));
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT SetCodecProperty(ComPtr<ICodecAPI> &codecApi, GUID guid,
|
|
bool value)
|
|
{
|
|
VARIANT v;
|
|
v.vt = VT_BOOL;
|
|
v.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
|
|
return codecApi->SetValue(&guid, &v);
|
|
}
|
|
|
|
static HRESULT SetCodecProperty(ComPtr<ICodecAPI> &codecApi, GUID guid,
|
|
UINT32 value)
|
|
{
|
|
VARIANT v;
|
|
v.vt = VT_UI4;
|
|
v.ulVal = value;
|
|
return codecApi->SetValue(&guid, &v);
|
|
}
|
|
|
|
static HRESULT SetCodecProperty(ComPtr<ICodecAPI> &codecApi, GUID guid,
|
|
UINT64 value)
|
|
{
|
|
VARIANT v;
|
|
v.vt = VT_UI8;
|
|
v.ullVal = value;
|
|
return codecApi->SetValue(&guid, &v);
|
|
}
|
|
|
|
bool H264Encoder::SetBitrate(UINT32 bitrate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonMeanBitRate,
|
|
UINT32(bitrate * 1000)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetQP(H264QP &qp)
|
|
{
|
|
HRESULT hr;
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonQuality,
|
|
UINT32(MapQpToQuality(qp))));
|
|
HRL(SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncVideoEncodeQP,
|
|
UINT64(qp.Pack(true))));
|
|
HRL(SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncVideoEncodeFrameTypeQP,
|
|
UINT64(qp.Pack(false))));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetMinQP(UINT32 minQp)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncVideoMinQP,
|
|
UINT32(minQp)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetMaxQP(UINT32 maxQp)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncVideoMaxQP,
|
|
UINT32(maxQp)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetRateControl(H264RateControl rateControl)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonRateControlMode,
|
|
UINT32(MapRateControl(rateControl))));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetKeyframeInterval(UINT32 seconds)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
float gopSize = float(framerateNum) / framerateDen * seconds;
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncMPVGOPSize,
|
|
UINT32(gopSize)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetMaxBitrate(UINT32 maxBitrate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonMaxBitRate,
|
|
UINT32(maxBitrate * 1000)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetLowLatency(bool lowLatency)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonLowLatency,
|
|
lowLatency));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetBufferSize(UINT32 bufferSize)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncCommonBufferSize,
|
|
UINT32(bufferSize * 1000)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetBFrameCount(UINT32 bFrames)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncMPVDefaultBPictureCount,
|
|
UINT32(bFrames)));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::SetEntropyEncoding(H264EntropyEncoding entropyEncoding)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (codecApi) {
|
|
HR_CHECK(LOG_WARNING, SetCodecProperty(codecApi,
|
|
CODECAPI_AVEncH264CABACEnable,
|
|
entropyEncoding == H264EntropyEncodingCABAC));
|
|
}
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::Initialize(std::function<bool(void)> func)
|
|
{
|
|
ProfileScope("H264Encoder::Initialize");
|
|
|
|
HRESULT hr;
|
|
|
|
ComPtr<IMFMediaType> inputType, outputType;
|
|
ComPtr<IMFAttributes> transformAttributes;
|
|
MFT_OUTPUT_STREAM_INFO streamInfo = {0};
|
|
|
|
HRC(CoCreateInstance(descriptor->Guid(), NULL, CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&transform)));
|
|
|
|
HRC(CreateMediaTypes(inputType, outputType));
|
|
|
|
if (descriptor->Async()) {
|
|
HRC(transform->GetAttributes(&transformAttributes));
|
|
HRC(transformAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK,
|
|
TRUE));
|
|
}
|
|
|
|
HRC(transform->QueryInterface(&codecApi));
|
|
|
|
if (func && !func()) {
|
|
MF_LOG(LOG_ERROR, "Failed setting custom properties");
|
|
goto fail;
|
|
}
|
|
|
|
MF_LOG(LOG_INFO, "Setting output type to transform");
|
|
LogMediaType(outputType.Get());
|
|
HRC(transform->SetOutputType(0, outputType.Get(), 0));
|
|
|
|
MF_LOG(LOG_INFO, "Setting input type to transform");
|
|
LogMediaType(inputType.Get());
|
|
HRC(transform->SetInputType(0, inputType.Get(), 0));
|
|
|
|
HRC(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
|
|
NULL));
|
|
|
|
HRC(transform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM,
|
|
NULL));
|
|
|
|
if (descriptor->Async())
|
|
HRC(InitializeEventGenerator());
|
|
|
|
HRC(transform->GetOutputStreamInfo(0, &streamInfo));
|
|
createOutputSample = !(streamInfo.dwFlags &
|
|
(MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
|
|
MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES));
|
|
|
|
return true;
|
|
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool H264Encoder::ExtraData(UINT8 **data, UINT32 *dataLength)
|
|
{
|
|
if (extraData.empty())
|
|
return false;
|
|
|
|
*data = extraData.data();
|
|
*dataLength = (UINT32)extraData.size();
|
|
|
|
return true;
|
|
}
|
|
|
|
HRESULT H264Encoder::CreateEmptySample(ComPtr<IMFSample> &sample,
|
|
ComPtr<IMFMediaBuffer> &buffer, DWORD length)
|
|
{
|
|
HRESULT hr;
|
|
|
|
HRC(MFCreateSample(&sample));
|
|
HRC(MFCreateMemoryBuffer(length, &buffer));
|
|
HRC(sample->AddBuffer(buffer.Get()));
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::EnsureCapacity(ComPtr<IMFSample> &sample, DWORD length)
|
|
{
|
|
HRESULT hr;
|
|
ComPtr<IMFMediaBuffer> buffer;
|
|
DWORD currentLength;
|
|
|
|
if (!sample) {
|
|
HRC(CreateEmptySample(sample, buffer, length));
|
|
}
|
|
else {
|
|
HRC(sample->GetBufferByIndex(0, &buffer));
|
|
}
|
|
|
|
HRC(buffer->GetMaxLength(¤tLength));
|
|
if (currentLength < length) {
|
|
HRC(sample->RemoveAllBuffers());
|
|
HRC(MFCreateMemoryBuffer(length, &buffer));
|
|
HRC(sample->AddBuffer(buffer));
|
|
}
|
|
else {
|
|
buffer->SetCurrentLength(0);
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT H264Encoder::ProcessInput(ComPtr<IMFSample> &sample)
|
|
{
|
|
ProfileScope("H264Encoder::ProcessInput(sample)");
|
|
|
|
HRESULT hr = S_OK;
|
|
if (descriptor->Async()) {
|
|
if (inputRequests == 1 && inputSamples.empty()) {
|
|
inputRequests--;
|
|
return transform->ProcessInput(0, sample, 0);
|
|
}
|
|
|
|
inputSamples.push(sample);
|
|
|
|
while (inputRequests > 0) {
|
|
if (inputSamples.empty())
|
|
return hr;
|
|
ComPtr<IMFSample> queuedSample = inputSamples.front();
|
|
inputSamples.pop();
|
|
inputRequests--;
|
|
HRC(transform->ProcessInput(0, queuedSample, 0));
|
|
}
|
|
} else {
|
|
return transform->ProcessInput(0, sample, 0);
|
|
}
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
bool H264Encoder::ProcessInput(UINT8 **data, UINT32 *linesize, UINT64 pts,
|
|
Status *status)
|
|
{
|
|
ProfileScope("H264Encoder::ProcessInput");
|
|
|
|
HRESULT hr;
|
|
ComPtr<IMFSample> sample;
|
|
ComPtr<IMFMediaBuffer> buffer;
|
|
BYTE *bufferData;
|
|
UINT64 sampleDur;
|
|
UINT32 imageSize;
|
|
|
|
HRC(MFCalculateImageSize(MFVideoFormat_NV12, width, height, &imageSize));
|
|
|
|
HRC(CreateEmptySample(sample, buffer, imageSize));
|
|
|
|
{
|
|
ProfileScope("H264EncoderCopyInputSample");
|
|
|
|
HRC(buffer->Lock(&bufferData, NULL, NULL));
|
|
|
|
ProcessNV12([&, this](DWORD height, int plane) {
|
|
MFCopyImage(bufferData, width, data[plane],
|
|
linesize[plane], width, height);
|
|
bufferData += width * height;
|
|
}, height);
|
|
}
|
|
|
|
HRC(buffer->Unlock());
|
|
HRC(buffer->SetCurrentLength(imageSize));
|
|
|
|
MFFrameRateToAverageTimePerFrame(framerateNum, framerateDen, &sampleDur);
|
|
|
|
HRC(sample->SetSampleTime(pts * sampleDur));
|
|
HRC(sample->SetSampleDuration(sampleDur));
|
|
|
|
if (descriptor->Async()) {
|
|
HRC(DrainEvents());
|
|
|
|
while (outputRequests > 0 && (hr = ProcessOutput()) == S_OK);
|
|
|
|
if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
|
|
MF_LOG_COM(LOG_ERROR, "ProcessOutput()", hr);
|
|
goto fail;
|
|
}
|
|
|
|
|
|
while (inputRequests == 0) {
|
|
hr = DrainEvent(false);
|
|
if (hr == MF_E_NO_EVENTS_AVAILABLE) {
|
|
Sleep(1);
|
|
continue;
|
|
}
|
|
if (FAILED(hr)) {
|
|
MF_LOG_COM(LOG_ERROR, "DrainEvent()", hr);
|
|
goto fail;
|
|
}
|
|
if (outputRequests > 0) {
|
|
hr = ProcessOutput();
|
|
if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT &&
|
|
FAILED(hr))
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRC(ProcessInput(sample));
|
|
|
|
*status = SUCCESS;
|
|
return true;
|
|
|
|
fail:
|
|
*status = FAILURE;
|
|
return false;
|
|
}
|
|
|
|
HRESULT H264Encoder::ProcessOutput()
|
|
{
|
|
HRESULT hr;
|
|
ComPtr<IMFSample> sample;
|
|
MFT_OUTPUT_STREAM_INFO outputInfo = { 0 };
|
|
|
|
DWORD outputStatus = 0;
|
|
MFT_OUTPUT_DATA_BUFFER output = { 0 };
|
|
ComPtr<IMFMediaBuffer> buffer;
|
|
BYTE *bufferData;
|
|
DWORD bufferLength;
|
|
INT64 samplePts;
|
|
INT64 sampleDts;
|
|
INT64 sampleDur;
|
|
std::unique_ptr<std::vector<BYTE>> data(new std::vector<BYTE>());
|
|
ComPtr<IMFMediaType> type;
|
|
std::unique_ptr<H264Frame> frame;
|
|
|
|
if (descriptor->Async()) {
|
|
HRC(DrainEvents());
|
|
|
|
if (outputRequests == 0)
|
|
return S_OK;
|
|
|
|
outputRequests--;
|
|
}
|
|
|
|
if (createOutputSample) {
|
|
HRC(transform->GetOutputStreamInfo(0, &outputInfo));
|
|
HRC(CreateEmptySample(sample, buffer, outputInfo.cbSize));
|
|
output.pSample = sample;
|
|
} else {
|
|
output.pSample = NULL;
|
|
}
|
|
|
|
while (true) {
|
|
hr = transform->ProcessOutput(0, 1, &output,
|
|
&outputStatus);
|
|
ComPtr<IMFCollection> events(output.pEvents);
|
|
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
|
return hr;
|
|
|
|
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
|
HRC(transform->GetOutputAvailableType(0, 0, &type));
|
|
HRC(transform->SetOutputType(0, type, 0));
|
|
MF_LOG(LOG_INFO, "Updating output type to transform");
|
|
LogMediaType(type);
|
|
if (descriptor->Async() && outputRequests > 0) {
|
|
outputRequests--;
|
|
continue;
|
|
} else {
|
|
return MF_E_TRANSFORM_NEED_MORE_INPUT;
|
|
}
|
|
}
|
|
|
|
if (hr != S_OK) {
|
|
MF_LOG_COM(LOG_ERROR, "transform->ProcessOutput()",
|
|
hr);
|
|
return hr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!createOutputSample)
|
|
sample.Set(output.pSample);
|
|
|
|
|
|
HRC(sample->GetBufferByIndex(0, &buffer));
|
|
|
|
bool keyframe = !!MFGetAttributeUINT32(sample,
|
|
MFSampleExtension_CleanPoint, FALSE);
|
|
|
|
HRC(buffer->Lock(&bufferData, NULL, &bufferLength));
|
|
|
|
if (keyframe && extraData.empty())
|
|
HRC(InitializeExtraData());
|
|
|
|
data->reserve(bufferLength + extraData.size());
|
|
|
|
if (keyframe)
|
|
data->insert(data->end(), extraData.begin(), extraData.end());
|
|
|
|
data->insert(data->end(), &bufferData[0], &bufferData[bufferLength]);
|
|
HRC(buffer->Unlock());
|
|
|
|
HRC(sample->GetSampleDuration(&sampleDur));
|
|
HRC(sample->GetSampleTime(&samplePts));
|
|
|
|
sampleDts = MFGetAttributeUINT64(sample,
|
|
MFSampleExtension_DecodeTimestamp, samplePts);
|
|
|
|
frame.reset(new H264Frame(keyframe,
|
|
samplePts / sampleDur,
|
|
sampleDts / sampleDur,
|
|
std::move(data)));
|
|
|
|
encodedFrames.push(std::move(frame));
|
|
|
|
return S_OK;
|
|
|
|
fail:
|
|
return hr;
|
|
}
|
|
|
|
bool H264Encoder::ProcessOutput(UINT8 **data, UINT32 *dataLength,
|
|
UINT64 *pts, UINT64 *dts, bool *keyframe, Status *status)
|
|
{
|
|
ProfileScope("H264Encoder::ProcessOutput");
|
|
|
|
HRESULT hr;
|
|
|
|
hr = ProcessOutput();
|
|
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || encodedFrames.empty()) {
|
|
*status = NEED_MORE_INPUT;
|
|
return true;
|
|
}
|
|
|
|
if (FAILED(hr) && encodedFrames.empty()) {
|
|
*status = FAILURE;
|
|
return false;
|
|
}
|
|
|
|
activeFrame = std::move(encodedFrames.front());
|
|
encodedFrames.pop();
|
|
|
|
*data = activeFrame.get()->Data();
|
|
*dataLength = activeFrame.get()->DataLength();
|
|
*pts = activeFrame.get()->Pts();
|
|
*dts = activeFrame.get()->Dts();
|
|
*keyframe = activeFrame.get()->Keyframe();
|
|
*status = SUCCESS;
|
|
|
|
return true;
|
|
}
|