| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- #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()
- {
- HRESULT hr;
- if (!descriptor->Async() || !eventGenerator || !pendingRequests)
- return;
- // Make sure all events have finished before releasing, and drain
- // all output requests until it makes an input request.
- // If you do not do this, you risk it releasing while there's still
- // encoder activity, which can cause a crash with certain interfaces.
- while (inputRequests == 0) {
- hr = ProcessOutput();
- if (hr != MF_E_TRANSFORM_NEED_MORE_INPUT && FAILED(hr)) {
- MF_LOG_COM(LOG_ERROR, "H264Encoder::~H264Encoder: "
- "ProcessOutput()", hr);
- break;
- }
- if (inputRequests == 0)
- Sleep(1);
- }
- }
- 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, "Activating encoder: %s",
- typeNames[(int)descriptor->Type()]);
- 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));
- pendingRequests++;
- *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;
- pendingRequests--;
- return true;
- }
|