Skip to content

Commit

Permalink
Now storing frame meta with timing info. EncodeDurationMs now being o…
Browse files Browse the repository at this point in the history
…utput at a generic level
  • Loading branch information
SoylentGraham committed Oct 10, 2023
1 parent 9ec84b3 commit d2ce422
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 45 deletions.
11 changes: 9 additions & 2 deletions Source/AvfEncoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -721,10 +721,17 @@ void NaluToAnnexB(std::span<uint8_t> Data,size_t LengthSize,std::function<void(s

//std::Debug << "OnNalPacket( pts=" << FrameNumber << ", dts=" << DecodeOrderNumber << ")" << std::endl;
auto FrameMeta = GetFrameMeta(FrameNumber);

FrameMeta.OnEncoded();
static bool DebugDuration = false;
if ( DebugDuration )
{
auto Duration = FrameMeta.GetEncodeDurationMs();
std::Debug << "Packet " << FrameNumber << " encode duration was " << Duration.count() << std::endl;
}

PopH264::TPacket OutputPacket;
OutputPacket.mData.reset(new std::vector<uint8_t>());
OutputPacket.mInputMeta = FrameMeta;
OutputPacket.mEncodeMeta = FrameMeta;
std::copy( Data.begin(), Data.end(), std::back_inserter(*OutputPacket.mData) );
OnOutputPacket(OutputPacket);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/TDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace PopH264
class PacketMeta_t;
class TInputNaluPacket;
typedef uint32_t FrameNumber_t; // this could be an index, or it could be time. Should essentially be a frame identifier
constexpr FrameNumber_t FrameNumberInvalid = 0;
constexpr FrameNumber_t FrameNumberInvalid = 0; // gr; should probably switch to uint32_t::max

// just shorthand names for cleaner constructors
typedef std::function<void(const SoyPixelsImpl&,FrameNumber_t,const ::json11::Json&)> OnDecodedFrame_t;
Expand Down
40 changes: 19 additions & 21 deletions Source/TEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void PopH264::TEncoder::OnOutputPacket(TPacket& Packet)
auto OutputPacket = [&](std::span<uint8_t> Data)
{
TPacket NextPacket;
NextPacket.mInputMeta = Packet.mInputMeta;
NextPacket.mEncodeMeta = Packet.mEncodeMeta;
NextPacket.mData.reset(new std::vector<uint8_t>());
std::copy( Data.begin(), Data.end(), std::back_inserter( *NextPacket.mData ) );
mOnOutputPacket(NextPacket);
Expand Down Expand Up @@ -45,31 +45,29 @@ void PopH264::TEncoder::OnFinished()



size_t PopH264::TEncoder::PushFrameMeta(const std::string& Meta)
PopH264::FrameNumber_t PopH264::TEncoder::PushFrameMeta(const std::string& Meta)
{
TEncoderFrameMeta FrameMeta;
FrameMeta.mFrameNumber = mFrameCount;
FrameMeta.mMeta = Meta;
mFrameMetas.PushBack(FrameMeta);
auto NewFrameNumber = mFrameCount;
mFrameCount++;
return FrameMeta.mFrameNumber;

TEncoderFrameMeta FrameMeta;
FrameMeta.mInputMeta = Meta;
FrameMeta.mPushTime = EventTime_t::clock::now();
mFrameMetas.insert({ NewFrameNumber, FrameMeta });

return NewFrameNumber;
}

std::string PopH264::TEncoder::GetFrameMeta(size_t FrameNumber)
PopH264::TEncoderFrameMeta PopH264::TEncoder::GetFrameMeta(FrameNumber_t FrameNumber)
{
for (auto i = 0; i < mFrameMetas.GetSize(); i++)
auto Match = mFrameMetas.find(FrameNumber);
if ( Match == mFrameMetas.end() )
{
auto& FrameMeta = mFrameMetas[i];
if (FrameMeta.mFrameNumber != FrameNumber)
continue;

// gr: for now, sometimes we get multiple packets for one frame, so we can't discard them all
//auto Meta = mFrameMetas.PopAt(i);
auto Meta = mFrameMetas[i];
return Meta.mMeta;
std::stringstream Error;
Error << "No frame meta matching frame number " << FrameNumber;
throw std::runtime_error(Error.str());
}

std::stringstream Error;
Error << "No frame meta matching frame number " << FrameNumber;
throw std::runtime_error(Error.str());

auto& Meta = Match->second;
return Meta;
}
54 changes: 38 additions & 16 deletions Source/TEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "SoyTime.h"
#include <functional>
#include <span>
#include "TDecoderInstance.h" // EventTime_t

class SoyPixelsImpl;

Expand All @@ -16,28 +17,49 @@ namespace PopH264
class TEncoderFrameMeta;
}


// as packets are popped asynchronously to input, we need to keep meta
// associated with frames we use an arbritry number for frame (presentation
// time), we can also store other encoder per-frame meta here (timing)
class PopH264::TEncoderFrameMeta
{
public:
std::string mInputMeta; // meta provided by user to keep with frame
EventTime_t mPushTime = EventTime_t::min();
EventTime_t mEncodedTime = EventTime_t::min();

// write encoded time if it hasn't been set
void OnEncoded()
{
// already set
if ( mEncodedTime != EventTime_t::min() )
return;
mEncodedTime = EventTime_t::clock::now();
}

std::chrono::milliseconds GetEncodeDurationMs()
{
if ( mPushTime == EventTime_t::min() || mEncodedTime == EventTime_t::min() )
return std::chrono::milliseconds(0);
auto Delta = mEncodedTime - mPushTime;
return std::chrono::duration_cast<std::chrono::milliseconds>( Delta );
}
};


class PopH264::TPacket
{
public:
std::span<uint8_t> GetData() { return mData ? std::span<uint8_t>( mData->data(), mData->size() ) : std::span<uint8_t>(); }
std::string_view GetInputMeta() { return mEncodeMeta.mInputMeta; }
public:
std::shared_ptr<std::vector<uint8_t>> mData;
std::string mInputMeta; // original input meta json
TEncoderFrameMeta mEncodeMeta; // includes original input meta
bool mEndOfStream = false;
std::string mError;
};


// as packets are popped asynchronously to input, we need to keep meta
// associated with frames we use an arbritry number for frame (presentation
// time)
class PopH264::TEncoderFrameMeta
{
public:
size_t mFrameNumber = 0;
std::string mMeta;
};


class PopH264::TEncoder
{
Expand All @@ -57,19 +79,19 @@ class PopH264::TEncoder
void OnFinished();

// returns frame number used as PTS and stores meta
size_t PushFrameMeta(const std::string& Meta);
FrameNumber_t PushFrameMeta(const std::string& Meta);
// gr: SOME frames will yield multiple packets (eg SPS & PPS) so some we need to keep around...
// gotta work out a way to figure out what we can discard
std::string GetFrameMeta(size_t FrameNumber);
TEncoderFrameMeta GetFrameMeta(FrameNumber_t FrameNumber);

bool HasEncodingFinished() { return mHasOutputEndOfStream || mHasOutputError; }
bool HasEncodingFinished() { return mHasOutputEndOfStream || mHasOutputError; }

private:
std::function<void(TPacket&)> mOnOutputPacket;
bool mHasOutputEndOfStream = false; // we've sent out an EOF frame
bool mHasOutputError = false;

std::mutex mFrameMetaLock;
size_t mFrameCount = 0;
Array<TEncoderFrameMeta> mFrameMetas;
FrameNumber_t mFrameCount = 0;
std::unordered_map<FrameNumber_t,TEncoderFrameMeta> mFrameMetas;
};
16 changes: 12 additions & 4 deletions Source/TEncoderInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ void PopH264::TEncoderInstance::PeekPacket(json11::Json::object& Meta)
auto& Packet0 = mPackets[0];
Packet = Packet0;
}

// write meta
auto DataSize = Packet.mData ? Packet.mData->size() : 0;
Meta[POPH264_ENCODEDFRAME_DATASIZE] = static_cast<int>(DataSize);
Expand All @@ -320,12 +320,17 @@ void PopH264::TEncoderInstance::PeekPacket(json11::Json::object& Meta)
if ( Packet.mEndOfStream )
Meta[POPH264_ENCODEDFRAME_ENDOFSTREAM] = Packet.mEndOfStream;

if ( !Packet.mInputMeta.empty() )
auto EncodeDuration = Packet.mEncodeMeta.GetEncodeDurationMs();
if ( EncodeDuration.count() != 0 )
Meta[POPH264_ENCODEDFRAME_ENCODEDURATIONMS] = static_cast<int>(EncodeDuration.count());

auto InputMeta = Packet.GetInputMeta();
if ( !InputMeta.empty() )
{
using namespace json11;
// we're expecting json, so make it an object
std::string ParseError;
auto MetaObject = Json::parse( Packet.mInputMeta, ParseError );
auto MetaObject = Json::parse( std::string(InputMeta), ParseError );

// this shouldn't come up, as we've already parsed it on input, but just in case
if (!ParseError.empty())
Expand Down Expand Up @@ -382,7 +387,10 @@ void PopH264::TEncoderInstance::OnNewPacket(TPacket& Packet)
}

{
std::lock_guard<std::mutex> Lock(mPacketsLock);
std::scoped_lock Lock(mPacketsLock);

// gr: if a packet encode duration wasn't written, write one here with a warning, so the low level encoder is reminded to add it
Packet.mEncodeMeta.OnEncoded();
mPackets.PushBack(Packet);
}

Expand Down
2 changes: 1 addition & 1 deletion Source_CSharp/PopH264.cs
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ public struct PoppedFrameMeta
public int DataSize; // bytes
public bool EndOfStream;
public EncodedFrameMeta Meta; // all the meta sent to PopH264_EncoderPushFrame
public int? EncodeDurationMs; // time it took to encode
public int EncodeDurationMs; // time it took to encode
public int? DelayDurationMs; // time spent in queue before encoding (lag)
public int OutputQueueCount; // time spent in queue before encoding (lag)
public string EncoderName; // low level encoder name
Expand Down

0 comments on commit d2ce422

Please sign in to comment.