如何解决如何使用x264 C API强制IDR帧?
我正在尝试使用外部布尔信号使用x264 C API强制将下一个编码帧转换为IDR帧。我将“基线”配置文件与“超快”和“零延迟”预设一起使用。我尝试在编码之前使用输入图片设置,如本代码片段所示,但这没有用。我的类Open()和Encode()方法在这里显示。任何帮助将不胜感激。
int X264Encoder::Open(void)
{
if (_x264VideoEncoder != NULL)
Close();
// Set up default parameters.
if (x264_param_default_preset(&_param,_encoderSpeedPreset.c_str(),"zerolatency") < 0) // 0=success,-1=Failed
{ _errStr = "X264Encoder::Open: Default parameter preset Failed with " + _encoderSpeedPreset; return(0); }
// Set non-default params.
_param.i_bitdepth = 8;
_param.i_csp = _colourSpaceMapping[_colourSpace]; // Input colour space
if(!_param.i_csp)
{ _errStr = "X264Encoder::Open: Incompatible colour space " + to_string(_colourSpace); return(0); }
_param.i_width = _width;
_param.i_height = _height;
_param.i_fps_num = _videoRateNumerator;
_param.i_fps_den = _videoRateDenominator;
_param.rc.i_bitrate = _avgBitsPerSecond / 1000; // bitrate units are in kbits/s
_param.i_threads = 1;
_param.b_vfr_input = 0; // VFR input. If 1,use timebase and timestamps for ratecontrol purposes. If 0,use fps only.
_param.b_repeat_headers = 1; // Put SPS/PPS before each keyframe
_param.b_annexb = 1; // If set,place start codes (4 bytes) before NAL units,otherwise place size (4 bytes) before NAL units.
// Apply profile restrictions.
if (x264_param_apply_profile(&_param,_profile.c_str()) < 0) // 0=success,-1=Failed
{ _errStr = "X264Encoder::Open: Unable to set profile " + _profile; return(0); }
// Initialise the encoder input pic buffer.
if (x264_picture_alloc(&_picIn,_param.i_csp,_param.i_width,_param.i_height) < 0)
{ _errStr = "X264Encoder::Open: Unable to alloc input picture buffer"; return(0); }
_inPicIsAllocated = true;
// Instantiate the encoder.
_x264VideoEncoder = x264_encoder_open(&_param);
if (_x264VideoEncoder == NULL)
{
_errStr = "X264Encoder::Open: Unable to instantiate the encoder";
// Clean up before exit.
x264_picture_clean(&_picIn);
_inPicIsAllocated = false;
return(0);
}//end if !_x264VideoEncoder...
// Frame counting for pts timestamps.
_frameNum = 0;
_lastPicType = 0; // IDR-frame
d.clear();
return(1);
}//end Open.
int X264Encoder::Encode(void* pSrc,void* pCmp,void* codeParameter)
{
_encodedFrameSize = 0;
// Validation house work.
if(!Ready())
{ _errStr = "X264Encoder::Encode: Not ready"; return(0); }
if(!pSrc || !pCmp)
{ _errStr = "X264Encoder::Encode: Invalid function parameter list"; return(0); }
// Load input image.
if(_param.i_csp != X264_CSP_I420) // Can only process I420 input colour space.
{ _errStr = "X264Encoder::Encode: I420 colour space required"; return(0); }
uint32_t lumSize = _width * _height;
uint32_t chrSize = lumSize / 4;
// Transfer the input source image into the x264 picture img structure.
uint8_t* pImg = static_cast<uint8_t*>(pSrc);
memcpy_s(_picIn.img.plane[0],lumSize,pImg,lumSize);
pImg += lumSize;
memcpy_s(_picIn.img.plane[1],chrSize,chrSize);
pImg += chrSize;
memcpy_s(_picIn.img.plane[2],chrSize);
// Encode single frame
_picIn.i_pts = _frameNum;
if (_idrFramerequired)
{
_picIn.i_type = X264_TYPE_IDR;
//... and clear the signal.
_idrFramerequired = false;
}//end if _idrFramerequired...
else
_picIn.i_type = X264_TYPE_AUTO;
_encodedFrameSize = x264_encoder_encode(_x264VideoEncoder,&_nal,&_nalCnt,&_picIn,&_picOut);
if (_encodedFrameSize > 0)
{
// Write the encoded stream to the output.
uint8_t* pOut = static_cast<uint8_t*>(pCmp);
memcpy_s(pOut,_encodedFrameSize,_nal->p_payload,_encodedFrameSize);
}//end else if _encodedFrameSize...
else
{ _errStr = "X264Encoder::Encode: Encode process Failed"; return(0); }
_lastPicType = 1; // Non-IDR
if (_picOut.i_type == X264_TYPE_IDR)
_lastPicType = 0; // IDR
d.push_back({ _encodedFrameSize,_lastPicType });
_frameNum++;
return(1);
}//end Encode...
解决方法
使用x264包装器类使用通用接口ICodecv3来一致地使用其他编解码器可能对其他人有用。这是类的定义和实现:
// Class to encapsulate the X264 encoder.
#pragma once
#include <inttypes.h>
#include <stdint.h>
#include <x264.h>
#include <ICodecv3.h>
#include <string>
#include <vector>
#include <chrono>
#include <unordered_map>
class X264Encoder : public ICodecv3
{
public:
X264Encoder(void);
~X264Encoder(void);
//--------------- ICodecv3 Interface ----------------------------------------------------
int GetParameter(const std::string type,std::string& value);
int SetParameter(const std::string type,const std::string& value);
void GetParameterName(int ordinal,std::string& name) {}
std::string GetErrorStrEx(void) { std::string errStr = _errStr; _errStr.clear(); return(errStr); } // Clear after use
int Ready(void) { if (_x264VideoEncoder != NULL) return(1); return(0); }
long GetCompressedBitLengthL(void) { return(_encodedFrameSize*8); }
long GetCompressedByteLengthL(void) { return(_encodedFrameSize); }
void* GetReference(int refNum) { return(NULL); }
void Restart(void) { _idrFrameRequired = true; }
int Open(void);
int Close(void);
int Encode(void* pSrc,void* pCmp,void* codeParameter);
int Decode(void* pCmp,int bitLength,void* pDst) { return(0); } // Decoding is not implemented.
//--------------- ICodecv2 Interface ----------------------------------------------------
// Version 2 has been superceded by version 3 and therefore none are implemented. For
// backward compatibility only.
int GetParameter(const char* type,int* length,void* value) { return(0); }
void GetParameterName(int ordinal,const char** name,int* length) {}
int SetParameter(const char* type,const char* value) { return(0); }
char* GetErrorStr(void) { return(NULL); }
int GetCompressedBitLength(void) { return(0); }
int GetCompressedByteLength(void) { return(0); }
int Code(void* pSrc,int codeParameter) { return(0); }
//--------------- Implementation specific Interface --------------------------------------
private:
public:
private:
x264_t* _x264VideoEncoder;
x264_param_t _param;
x264_picture_t _picIn;
bool _inPicIsAllocated;
x264_picture_t _picOut;
int _frameNum; // Used for internal pts timestamp incremented after each encode.
long _encodedFrameSize;
bool _idrFrameRequired; // Signal an IDR-frame to the Encode() method
x264_nal_t* _nal;
int _nalCnt;
std::string _errStr;
uint32_t _colourSpace;
uint32_t _width;
uint32_t _height;
uint32_t _videoRateNumerator;
uint32_t _videoRateDenominator;
uint32_t _avgBitsPerSecond;
uint32_t _lastPicType; // 0=IDR-frame,1=P/D-frame
// Implementation specific params.
std::string _encoderSpeedPreset;
std::string _profile;
std::unordered_map<std::string,void*> _parameters = // Hold all the settings for the encoder.
{
{ "incolour",&_colourSpace },{ "width",&_width },{ "height",&_height },{ "frame rate numerator",&_videoRateNumerator },{ "frame rate denominator",&_videoRateDenominator },{ "avg bits per second",&_avgBitsPerSecond },{ "encoder speed preset",&_encoderSpeedPreset },{ "profile",&_profile },{ "last pic coding type",&_lastPicType } // Read only.
};
std::unordered_map<uint32_t,int> _colourSpaceMapping = // Translate external "incolour" to x264 csp
{
{ 17,X264_CSP_I420 },{ 18,X264_CSP_NV12 },{ 0,X264_CSP_BGR },{ 1,X264_CSP_BGRA }
};
};//end X264Encoder.
// Class to encapsulate the X264 encoder.
#include <X264Encoder.h>
#include <memory.h>
using namespace std;
X264Encoder::X264Encoder(void) :
_x264VideoEncoder(NULL),_nal(NULL),_nalCnt(0),_errStr(""),_width(0),_height(0),_videoRateNumerator(25),_videoRateDenominator(1),_avgBitsPerSecond(0),_frameNum(0),_encodedFrameSize(0),_idrFrameRequired(true),_lastPicType(0),_inPicIsAllocated(false),_encoderSpeedPreset("fast"),_profile("baseline")
{
// Initialise the _param member with something.
x264_param_default_preset(&_param,_encoderSpeedPreset.c_str(),NULL);
}//end constructor.
X264Encoder::~X264Encoder(void)
{
Close();
}//end destructor.
int X264Encoder::GetParameter(const string type,string& value)
{
// _parameters map is never empty so no check required.
void* vp = _parameters[type];
if (vp != NULL)
{
if ((type == "encoder speed preset") || (type == "profile"))
value = *((string*)vp);
// else if (type == "timestamp units") // int32_t type
// value = to_string(*((int32_t*)vp));
// else if (type == "timestamp offset") // int64_t type
// value = to_string(*((int64_t*)vp));
else
value = to_string(*((uint32_t*)vp)); // For parameter values that are all uint32_t
}//end if vp...
else
return(0);
return(1);
}//end GetParameter.
int X264Encoder::SetParameter(const string type,const string& value)
{
if (value.empty()) return(0);
// _parameters is never empty so no check required.
void* vp = _parameters[type];
if (vp != NULL)
{
if ((type == "encoder speed preset") || (type == "profile"))
*((string*)vp) = value;
// else if (type == "timestamp units") // int32_t type
// *((int32_t*)vp) = stoi(value);
// else if (type == "timestamp offset") // int64_t type
// *((int64_t*)vp) = stoi(value);
else if (type == "last pic coding type") // Read only - do not set.
return(1);
else
*((uint32_t*)vp) = stoi(value);
}//end if vp...
else
return(0);
return(1);
}//end SetParameter.
// Open the X264 encoder.
// Any non-default parameters must be set prior to calling this method. Create a default set and
// modify them with the new parameters. then the encoder is instantiated.
// Return 1=success,0=failure.
int X264Encoder::Open(void)
{
if (_x264VideoEncoder != NULL)
Close();
// Set up default parameters.
if (x264_param_default_preset(&_param,"zerolatency") < 0) // 0=success,-1=failed
{ _errStr = "X264Encoder::Open: Default parameter preset failed with " + _encoderSpeedPreset; return(0); }
// Set non-default params.
_param.i_bitdepth = 8;
_param.i_csp = _colourSpaceMapping[_colourSpace]; // Input colour space
if(!_param.i_csp)
{ _errStr = "X264Encoder::Open: Incompatible colour space " + to_string(_colourSpace); return(0); }
_param.i_width = _width;
_param.i_height = _height;
_param.i_fps_num = _videoRateNumerator;
_param.i_fps_den = _videoRateDenominator;
_param.rc.i_bitrate = _avgBitsPerSecond / 1000; // bitrate units are in kbits/s
_param.i_threads = 1;
_param.b_vfr_input = 0; // VFR input. If 1,use timebase and timestamps for ratecontrol purposes. If 0,use fps only.
_param.b_repeat_headers = 1; // Put SPS/PPS before each keyframe
_param.b_annexb = 1; // If set,place start codes (4 bytes) before NAL units,otherwise place size (4 bytes) before NAL units.
// Apply profile restrictions.
if (x264_param_apply_profile(&_param,_profile.c_str()) < 0) // 0=success,-1=failed
{ _errStr = "X264Encoder::Open: Unable to set profile " + _profile; return(0); }
// Initialise the encoder input pic buffer.
if (x264_picture_alloc(&_picIn,_param.i_csp,_param.i_width,_param.i_height) < 0)
{ _errStr = "X264Encoder::Open: Unable to alloc input picture buffer"; return(0); }
_inPicIsAllocated = true;
// Instantiate the encoder.
_x264VideoEncoder = x264_encoder_open(&_param);
if (_x264VideoEncoder == NULL)
{
_errStr = "X264Encoder::Open: Unable to instantiate the encoder";
// Clean up before exit.
x264_picture_clean(&_picIn);
_inPicIsAllocated = false;
return(0);
}//end if !_x264VideoEncoder...
// Frame counting for pts timestamps.
_frameNum = 0;
_lastPicType = 0; // IDR-frame
return(1);
}//end Open.
// Close the encoder.
int X264Encoder::Close(void)
{
if (_x264VideoEncoder != NULL)
{
x264_encoder_close(_x264VideoEncoder);
_x264VideoEncoder = NULL;
}//end if _x264VideoEncoder...
// Input pic structure.
if (_inPicIsAllocated)
x264_picture_clean(&_picIn);
_inPicIsAllocated = false;
return(1);
}//end Close.
int X264Encoder::Encode(void* pSrc,void* codeParameter)
{
_encodedFrameSize = 0;
// Validation house work.
if(!Ready())
{ _errStr = "X264Encoder::Encode: Not ready"; return(0); }
if(!pSrc || !pCmp)
{ _errStr = "X264Encoder::Encode: Invalid function parameter list"; return(0); }
// Load input image.
if(_param.i_csp != X264_CSP_I420) // Can only process I420 input colour space.
{ _errStr = "X264Encoder::Encode: I420 colour space required"; return(0); }
uint32_t lumSize = _width * _height;
uint32_t chrSize = lumSize / 4;
// Transfer the input source image into the x264 picture img structure.
uint8_t* pImg = static_cast<uint8_t*>(pSrc);
memcpy_s(_picIn.img.plane[0],lumSize,pImg,lumSize);
pImg += lumSize;
memcpy_s(_picIn.img.plane[1],chrSize,chrSize);
pImg += chrSize;
memcpy_s(_picIn.img.plane[2],chrSize);
// Encode single frame
_picIn.i_pts = _frameNum;
if (_idrFrameRequired)
{
_picIn.i_type = X264_TYPE_IDR;
//... and clear the signal.
_idrFrameRequired = false;
}//end if _idrFrameRequired...
else
_picIn.i_type = X264_TYPE_AUTO;
_encodedFrameSize = x264_encoder_encode(_x264VideoEncoder,&_nal,&_nalCnt,&_picIn,&_picOut);
if (_encodedFrameSize > 0)
{
// Write the encoded stream to the output.
uint8_t* pOut = static_cast<uint8_t*>(pCmp);
memcpy_s(pOut,_encodedFrameSize,_nal->p_payload,_encodedFrameSize);
}//end else if _encodedFrameSize...
else
{ _errStr = "X264Encoder::Encode: Encode process failed"; return(0); }
_lastPicType = 1; // Non-IDR
if (_picOut.i_type == X264_TYPE_IDR)
_lastPicType = 0; // IDR
_frameNum++;
return(1);
}//end Encode...
我希望这对某人有用...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。