// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Templates/SharedPointer.h" #include "Templates/PimplPtr.h" #include "Containers/Map.h" #include "Misc/Timespan.h" #include "Misc/Variant.h" class IElectraDecoderOutput { public: virtual ~IElectraDecoderOutput() = default; enum class EType { None, Video, Audio, Subtitle }; virtual EType GetType() const = 0; virtual FTimespan GetPTS() const = 0; virtual uint64 GetUserValue() const = 0; }; class IElectraDecoderDefaultOutputFormat { public: virtual ~IElectraDecoderDefaultOutputFormat() = default; }; enum class EElectraDecoderFlags : uint32 { None = 0, IsSyncSample = 0x01, IsDiscardable = 0x02, IsReplaySample = 0x04, IsLastReplaySample = 0x08, InitCSDOnly = 0x10, DoNotOutput = 0x20, InputIsProcessed = 0x100 }; ENUM_CLASS_FLAGS(EElectraDecoderFlags); struct FElectraDecoderInputAccessUnit { const void* Data = nullptr; int64 DataSize = 0; FTimespan DTS; FTimespan PTS; FTimespan Duration; uint64 UserValue = 0; EElectraDecoderFlags Flags = EElectraDecoderFlags::None; }; class IElectraDecoderBitstreamInfo { public: virtual ~IElectraDecoderBitstreamInfo() { } }; class IElectraDecoderBitstreamProcessor : public TSharedFromThis { public: virtual ~IElectraDecoderBitstreamProcessor() = default; enum class EProcessResult { Ok, CSDChanged, Error }; /** * Returns whether or not calling ProcessInputForDecoding() will modify the provided bitstream in place. * If true you may need to create a temporary copy if you want to retain the bitstream in a cache. */ virtual bool WillModifyBitstreamInPlace() = 0; /** * Clears the processor. Must be called when seeking or flushing. */ virtual void Clear() = 0; /** * Returns codec specific data (CSD) from a decoder configuration record (DCR). * DCR is provided in a container like ISO/IEC 14496-12 but some decoders need to be given the * encapsulated CSD. If the DCR cannot be parsed `Error` is returned. * Otherwise, if there is no DCR present in the parameter map (which _could_ be an error on your end if DCR should be there) * `Ok` is returned with the (possibly emptry) CSD. */ virtual EProcessResult GetCSDFromConfigurationRecord(TArray& OutCSD, const TMap& InParamsWithDCRorCSD) = 0; /** * Processes the bitstream and places sideband information in the provided IElectraDecoderBitstreamInfo for use * with a subsequent call to SetPropertiesOnOutput(). * If necessary the bitstream is modified in place. * See `WillModifyBitstreamInPlace()` * Returns `Ok` or `Error`. If the codec specific data changed `CSDChanged` is returned which is only * informational for you and is functionally the same as `Ok`. */ virtual EProcessResult ProcessInputForDecoding(TSharedPtr& OutBSI, FElectraDecoderInputAccessUnit& InOutAccessUnit, const TMap& InAccessUnitSidebandData) = 0; /** * Sets previously extracted sideband data in the provided output dictionary. */ virtual void SetPropertiesOnOutput(TMap& InOutProperties, TSharedPtr InBSI) = 0; /** * Returns an error message if either method returned `Error`. */ virtual FString GetLastError() = 0; }; class IElectraDecoder : public TSharedFromThis { public: virtual ~IElectraDecoder() = default; using FInputAccessUnit = FElectraDecoderInputAccessUnit; enum class EDecoderError { // No error, all is well. None, // End of data processed EndOfData, // No buffer available when sending access unit to decode or no output available yet (more input required) NoBuffer, // The decoder was lost due to resource sharing conflicts or when the application was suspended. LostDecoder, // An internal decoder error occurred. Call GetError() to get the error code. Error }; enum class EOutputStatus { // Output is available. Available, // Output is not available. Provide more input. NeedInput, // Output is not available right now. New input is not required, call HaveOutput() again. TryAgainLater, // All output has been provided. EndOfData, // An internal decoder error occurred. Call GetError() to get the error code. Error }; enum class ECSDCompatibility { // Current decoder configuration is fully capable to continue decoding with the new configuration. Compatible, // The decoder must be drained before it can continue with the new configuration. Drain, // The decoder must be drained and then ResetToCleanStart() before it can continue with the new configuration. DrainAndReset }; struct FError { bool IsSet() const { return Code || SdkCode || Message.Len(); } const FString& GetMessage() const { return Message; } int32 GetCode() const { return Code; } uint32 GetSdkCode() const { return SdkCode; } FError& SetMessage(const FString& InMessage) { Message = InMessage; return *this; } FError& SetCode(int32 InCode) { Code = InCode; return *this; } FError& SetSdkCode(uint32 InSdkCode) { SdkCode = InSdkCode; return *this; } FString Message; int32 Code = 0; uint32 SdkCode = 0; }; enum class EType { None, Video, Audio, Subtitle }; /** * Returns the type of this decoder instance. * This is useful to ensure that the correct type was indeed created and you did not get a different * type of decoder from the factory if the format was ambiguous. */ virtual EType GetType() const = 0; /** * Populates the provided map with decoder features or required options. * See IElectraDecoderFeature and IElectraDecoderOption. * Not all decoders will provide the same features. */ virtual void GetFeatures(TMap& OutFeatures) const = 0; /** * Returns the most recent error that you should retrieve when either method returns failure. */ virtual FError GetError() const = 0; /** * Closes the decoder instance. This must be called prior to dropping the last reference of the decoder * resulting in the call to its destructor. * This may already close internally used resources. * The decoder instance can no longer be used and all decoding and output related methods will return an error. */ virtual void Close() = 0; /** * When a change in decoder specific data is detected, call this method to determine if the decoder * can just continue decoding with the new CSD or if it needs to be drained or drained first and then reset. */ virtual ECSDCompatibility IsCompatibleWith(const TMap& CSDAndAdditionalOptions) = 0; /** * Tries to reset the decoder to a clean start state. * If this fails and 'false' is returned the decoder must be destroyed and a new instance created. */ virtual bool ResetToCleanStart() = 0; /** * Asks the decoder to provide information on the output that is expected to be produced * given the codec specific data as input. * This may be useful to check ahead of decoding what output format can be expected. * However, the decoder may still produce different output while performing actual decoding * if the output format cannot be derived from the CSD alone. * * If no output information can be provided a nullptr will be returned. */ virtual TSharedPtr GetDefaultOutputFormatFromCSD(const TMap& CSDAndAdditionalOptions) = 0; /** * Decodes one access unit. * The data sent for decoding must form one complete decodable unit. Partial data is not permitted. * You are not required to retain the access unit. The decoder will copy the data internally if required. * * Not every call will result in a new decoded output. It is possible that several input access units are * required before an output will be made available, even with complete decodable units being provided. * * The return value indicates the following: * - None : No error, the access unit was accepted successfully. * - EndOfData : SendEndOfData() was called to drain the decoder to produce output for every access unit * that was delivered. All pending output must be retrieved before sending a new access unit. * - NoBuffer : The decoder can not accept new input at the moment unless pending output has been retrieved. * If there is no pending output this indicates some abnormal condition and the decoder should * be destroyed. * - Error : An error has occurred. Get the details by calling GetError(). The decoder will need to be * destroyed and recreated. */ virtual EDecoderError DecodeAccessUnit(const FInputAccessUnit& InInputAccessUnit, const TMap& InAdditionalOptions) = 0; /** * Sends an end-of-data notification to have the decoder process all pending input and generate as * much output as possible. * All output must be retrieved by calling HaveOutput() and GetOutput() in sequence. * Once all output has been retrieved new data can be provided to the decoder. * * The return value indicates the following: * - None : No error, the call was successful. * - EndOfData : This is returned when SendEndOfData() is called more than once while the decoder is * already processing the remaining input. * - NoBuffer : This value is not returned by this method. * - Error : An error has occurred. Get the details by calling GetError(). The decoder will need to be * destroyed and recreated. */ virtual EDecoderError SendEndOfData() = 0; /** * Flushes the decoder to discard any pending input. No output will be generated. * New data can be provided to the decoder immediately. * * The return value indicates the following: * - None : No error, the call was successful. * - EndOfData : This value is not returned by this method. * - NoBuffer : This value is not returned by this method. * - Error : An error has occurred. Get the details by calling GetError(). The decoder will need to be * destroyed and recreated. */ virtual EDecoderError Flush() = 0; /** * Checks for available decoded output. * Calling DecodeAccessUnit() may not produce any output at that moment. * You need to check if output is available by calling this method. * If new output is available it can be retrieved calling GetOutput(). * After a call to SendEndOfData() you need to call this method to * perform decoding of any pending input. * * The return value indicates the following: * - Available : New output is available. It must be retrieved by calling GetOutput() * - NeedInput : There is no output available. More data must be provided to the * decoder through DecodeAccessUnit() or pending input be completed * by calling SendEndOfData(). * - EndOfData : Indicates the last output had been returned after a call to * SendEndOfData(). This return value is returned only once. The next * call will return NeedInput. * - Error : An error has occurred. Get the details by calling GetError(). The decoder will need to be * destroyed and recreated. */ virtual EOutputStatus HaveOutput() = 0; /** * Returns the next pending output. * This call should be preceded by a call to HaveOutput() to check if output is * available. * Calling without checking first will return a nullptr when no output is available. * * NOTE: Ownership of the decoded output stays with the decoder. * You may hold on to the returned pointer but it is recommended the output * is processed immediately. If the output is not returned to the decoder * (by resetting the pointer) it may be possible for the decoder to be * unable to produce additional output. */ virtual TSharedPtr GetOutput() = 0; /** * Creates a processor to pre-parse the decoder input bitstream. */ virtual TSharedPtr CreateBitstreamProcessor() = 0; virtual void Suspend() = 0; virtual void Resume() = 0; };