- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 C++ 在虚幻引擎 4 中对视频进行编码。我可以访问单独的框架。下面是读取视口(viewport)
显示像素并存储在缓冲区中的代码。
//Safely get render target resource.
FRenderTarget* RenderTarget = TextureRenderTarget->GameThread_GetRenderTargetResource();
FIntPoint Size = RenderTarget->GetSizeXY();
auto ImageBytes = Size.X* Size.Y * static_cast<int32>(sizeof(FColor));
TArray<uint8> RawData;
RawData.AddUninitialized(ImageBytes);
//Get image raw data.
if (!RenderTarget->ReadPixelsPtr((FColor*)RawData.GetData()))
{
RawData.Empty();
UE_LOG(ExportRenderTargetBPFLibrary, Error, TEXT("ExportRenderTargetAsImage: Failed to get raw data."));
return false;
}
Buffer::getInstance().add(RawData);
Unreal Engine 有 IImageWrapperModule
,您可以使用它从帧中获取图像,但注意视频编码。我想要的是为实时流媒体服务实时编码帧。
我找到了这篇文章 Encoding a screenshot into a video using FFMPEG这正是我想要的,但我在为我的案例调整这个解决方案时遇到了问题。代码已过时(例如 avcodec_encode_video
更改为具有不同参数的 avcodec_encode_video2
)。
下面是编码器的代码。
void Compressor::DoWork()
{
AVCodec* codec;
AVCodecContext* c = NULL;
//uint8_t* outbuf;
//int /*i, out_size,*/ outbuf_size;
UE_LOG(LogTemp, Warning, TEXT("encoding"));
codec = avcodec_find_encoder(AV_CODEC_ID_MPEG1VIDEO); // finding the H264 encoder
if (!codec) {
UE_LOG(LogTemp, Warning, TEXT("codec not found"));
exit(1);
}
else UE_LOG(LogTemp, Warning, TEXT("codec found"));
c = avcodec_alloc_context3(codec);
c->bit_rate = 400000;
c->width = 1280; // resolution must be a multiple of two (1280x720),(1900x1080),(720x480)
c->height = 720;
c->time_base.num = 1; // framerate numerator
c->time_base.den = 25; // framerate denominator
c->gop_size = 10; // emit one intra frame every ten frames
c->max_b_frames = 1; // maximum number of b-frames between non b-frames
c->keyint_min = 1; // minimum GOP size
c->i_quant_factor = (float)0.71; // qscale factor between P and I frames
//c->b_frame_strategy = 20; ///// find out exactly what this does
c->qcompress = (float)0.6; ///// find out exactly what this does
c->qmin = 20; // minimum quantizer
c->qmax = 51; // maximum quantizer
c->max_qdiff = 4; // maximum quantizer difference between frames
c->refs = 4; // number of reference frames
c->trellis = 1; // trellis RD Quantization
c->pix_fmt = AV_PIX_FMT_YUV420P; // universal pixel format for video encoding
c->codec_id = AV_CODEC_ID_MPEG1VIDEO;
c->codec_type = AVMEDIA_TYPE_VIDEO;
if (avcodec_open2(c, codec, NULL) < 0) {
UE_LOG(LogTemp, Warning, TEXT("could not open codec")); // opening the codec
//exit(1);
}
else UE_LOG(LogTemp, Warning, TEXT("codec oppened"));
FString FinalFilename = FString("C:/Screen/sample.mpg");
auto &PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
auto FileHandle = PlatformFile.OpenWrite(*FinalFilename, true);
if (FileHandle)
{
delete FileHandle; // remove when ready
UE_LOG(LogTemp, Warning, TEXT("file opened"));
while (true)
{
UE_LOG(LogTemp, Warning, TEXT("removing from buffer"));
int nbytes = avpicture_get_size(AV_PIX_FMT_YUV420P, c->width, c->height); // allocating outbuffer
uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes * sizeof(uint8_t));
AVFrame* inpic = av_frame_alloc();
AVFrame* outpic = av_frame_alloc();
outpic->pts = (int64_t)((float)1 * (1000.0 / ((float)(c->time_base.den))) * 90); // setting frame pts
avpicture_fill((AVPicture*)inpic, (uint8_t*)Buffer::getInstance().remove().GetData(),
AV_PIX_FMT_PAL8, c->width, c->height); // fill image with input screenshot
avpicture_fill((AVPicture*)outpic, outbuffer, AV_PIX_FMT_YUV420P, c->width, c->height); // clear output picture for buffer copy
av_image_alloc(outpic->data, outpic->linesize, c->width, c->height, c->pix_fmt, 1);
/*
inpic->data[0] += inpic->linesize[0]*(screenHeight-1);
// flipping frame
inpic->linesize[0] = -inpic->linesize[0];
// flipping frame
struct SwsContext* fooContext = sws_getContext(screenWidth, screenHeight, PIX_FMT_RGB32, c->width, c->height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
sws_scale(fooContext, inpic->data, inpic->linesize, 0, c->height, outpic->data, outpic->linesize); // converting frame size and format
out_size = avcodec_encode_video(c, outbuf, outbuf_size, outpic);
// save in file
*/
}
delete FileHandle;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Can't open file"));
}
}
有人可以解释翻转帧部分(为什么这样做?)以及如何使用avcodec_encode_video2
函数而不是avcodec_encode_video
?
最佳答案
不仅 avcodec_encode_video
已过时,avcodec_encode_video2
也已被标记为已弃用一段时间。您现在应该使用新的 avcodec_send_frame
和 avcodec_receive_packet
进行编码。
“翻转”部分对编码没有任何好处,我强烈建议不要在您的代码中这样做。如果您发现输出大小不正确,只需将 swscale
插值算法标志切换为 SWS_ACCURATE_RND
。
除了旧的avcodec_encode_video
API,还有几个潜在的风险:
AV_CODEC_ID_H264
找到它,而不是 AV_CODEC_ID_MPEG1VIDEO
,并且 ffmpeg 库应该使用 libx264
构建。
avcodec_find_encoder_by_name("h264_nvenc")
会好得多。 delete FileHandle
被执行了两次。avpicture...
函数已被弃用很长时间。请改用其他功能。如果性能至关重要,请将所有编码过程移至独立线程而不是游戏线程。
我的自定义 GameViewportClient
类中有一些用于编码 UE4 视口(viewport)输出的代码,类似于 ffmpeg 官方 muxing
和 encode_video
示例。
MyGameViewportClient.h:
UCLASS(Config=Game)
class FUSIONCUT_API UMyGameViewportClient : public UGameViewportClient
{
GENERATED_BODY()
public:
virtual void Draw(FViewport* Viewport, FCanvas* SceneCanvas) override;
void FirstTimeInit();
void InitCodec();
void TidyUp();
void SetAutoRecording(bool val);
void RecordNextFrame();
bool CanRecordNextFrame();
void SetRecording(bool val);
void SetLevelDelay(int32 delay);
void SetOver(bool val);
void SetAbandon(bool val);
void SetFilePath(FString out_file);
void SetThumbnail(FString thumbnail_file, int32 thumbnail_frame);
void SaveThumbnailImage();
private:
UPROPERTY(Config)
FString DeviceNum;
UPROPERTY(Config)
FString H264Crf;
UPROPERTY(Config)
int DeviceIndex;
UPROPERTY()
UFunction* ProgressFunc;
UPROPERTY()
UFunction* FinishFunc;
FIntPoint ViewportSize;
int count;
TArray<FColor> ColorBuffer;
TArray<uint8> IMG_Buffer;
struct OutputStream {
AVStream* Stream;
AVCodecContext* Ctx;
int64_t NextPts;
AVFrame* Frame;
struct SwsContext* SwsCtx;
};
OutputStream VideoSt = { 0 };
AVOutputFormat* Fmt;
AVFormatContext* FmtCtx;
AVCodec* VideoCodec;
AVDictionary* Opt = nullptr;
SwsContext* SwsCtx;
AVPacket Pkt;
int GotOutput;
int InLineSize[1];
bool Start;
bool Over;
bool FirstTime;
bool Abandon;
bool AutoRecording;
bool RecordingNextFrame;
double LastSendingTime;
std::string FilePath;
FString UEFilePath;
int32 LevelDelay;
void EncodeAndWrite();
void CaptureFrame();
void AddStream(enum AVCodecID CodecID);
void OpenVideo();
int WriteFrame(bool need_save_thumbnail = true);
void CloseStream();
void AllocPicture();
int FFmpegEncode(AVFrame *frame);
};
MyGameViewportClient.cpp:
void UMyGameViewportClient::InitCodec()
{
ViewportSize = Viewport->GetSizeXY();
av_register_all();
avformat_alloc_output_context2(&FmtCtx, nullptr, nullptr, FilePath.c_str());
if (!FmtCtx)
{
UE_LOG(LogTemp, Error, TEXT("cannot alloc format context"));
return;
}
Fmt = FmtCtx->oformat;
//auto codec_id = AV_CODEC_ID_H264;
const char codec_name[32] = "h264_nvenc";
//auto codec = avcodec_find_encoder(codec_id);
auto codec = avcodec_find_encoder_by_name(codec_name);
av_format_set_video_codec(FmtCtx, codec);
if (Fmt->video_codec != AV_CODEC_ID_NONE)
{
AddStream(Fmt->video_codec);
}
OpenVideo();
VideoSt.NextPts = 0;
av_dump_format(FmtCtx, 0, FilePath.c_str(), 1);
if (!(Fmt->flags & AVFMT_NOFILE))
{
auto ret = avio_open(&FmtCtx->pb, FilePath.c_str(), AVIO_FLAG_WRITE);
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("Could not open %s: %s"), *UEFilePath, *errstr);
return;
}
}
auto ret = avformat_write_header(FmtCtx, &Opt);
if (ret < 0)
{
UE_LOG(LogTemp, Error, TEXT("Error occurred when writing header to: %s"), *UEFilePath);
return;
}
InLineSize[0] = 4 * VideoSt.Ctx->width;
SwsCtx = sws_getContext(VideoSt.Ctx->width, VideoSt.Ctx->height, AV_PIX_FMT_RGBA,
VideoSt.Ctx->width, VideoSt.Ctx->height, VideoSt.Ctx->pix_fmt,
0, nullptr, nullptr, nullptr);
}
void UMyGameViewportClient::OpenVideo()
{
auto c = VideoSt.Ctx;
AVDictionary* opt = nullptr;
av_dict_copy(&opt, Opt, 0);
auto ret = avcodec_open2(c, VideoCodec, &opt);
av_dict_free(&opt);
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("Could not open video codec: %s"), *errstr);
}
AllocPicture();
if (!VideoSt.Frame)
{
UE_LOG(LogTemp, Error, TEXT("Could not allocate video frame"));
return;
}
if (avcodec_parameters_from_context(VideoSt.Stream->codecpar, c))
{
UE_LOG(LogTemp, Error, TEXT("Could not copy the stream parameters"));
}
}
void UMyGameViewportClient::AllocPicture()
{
VideoSt.Frame = av_frame_alloc();
if (!VideoSt.Frame)
{
UE_LOG(LogTemp, Error, TEXT("av_frame_alloc failed."));
return;
}
VideoSt.Frame->format = VideoSt.Ctx->pix_fmt;
VideoSt.Frame->width = ViewportSize.X;
VideoSt.Frame->height = ViewportSize.Y;
if (av_frame_get_buffer(VideoSt.Frame, 32) < 0)
{
UE_LOG(LogTemp, Error, TEXT("Could not allocate frame data"));
}
}
void UMyGameViewportClient::AddStream(enum AVCodecID CodecID)
{
VideoCodec = avcodec_find_encoder(CodecID);
if (!VideoCodec)
{
UE_LOG(LogTemp, Error, TEXT("Could not find encoder for '%s'"), ANSI_TO_TCHAR(avcodec_get_name(CodecID)));
}
VideoSt.Stream = avformat_new_stream(FmtCtx, nullptr);
if (!VideoSt.Stream)
{
UE_LOG(LogTemp, Error, TEXT("Could not allocate stream"));
}
VideoSt.Stream->id = FmtCtx->nb_streams - 1;
VideoSt.Ctx = avcodec_alloc_context3(VideoCodec);
if (!VideoSt.Ctx)
{
UE_LOG(LogTemp, Error, TEXT("Could not alloc an encoding context"));
}
VideoSt.Ctx->codec_id = CodecID;
VideoSt.Ctx->width = ViewportSize.X;
VideoSt.Ctx->height = ViewportSize.Y;
VideoSt.Stream->time_base = VideoSt.Ctx->time_base = { 1, FRAMERATE };
VideoSt.Ctx->gop_size = 10;
VideoSt.Ctx->max_b_frames = 1;
VideoSt.Ctx->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(VideoSt.Ctx->priv_data, "cq", TCHAR_TO_ANSI(*H264Crf), 0); // change `cq` to `crf` if using libx264
av_opt_set(VideoSt.Ctx->priv_data, "gpu", TCHAR_TO_ANSI(*DeviceNum), 0); // comment this line if using libx264
if (FmtCtx->oformat->flags & AVFMT_GLOBALHEADER)
VideoSt.Ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
void UMyGameViewportClient::EncodeAndWrite()
{
Pkt = { nullptr };
av_init_packet(&Pkt);
fflush(stdout);
IMG_Buffer.SetNum(ColorBuffer.Num() * 4);
uint8* DestPtr = nullptr;
for (auto i = 0; i < ColorBuffer.Num(); i++)
{
DestPtr = &IMG_Buffer[i * 4];
auto SrcPtr = ColorBuffer[i];
*DestPtr++ = SrcPtr.R;
*DestPtr++ = SrcPtr.G;
*DestPtr++ = SrcPtr.B;
*DestPtr++ = SrcPtr.A;
}
uint8* inData[1] = { IMG_Buffer.GetData() };
sws_scale(SwsCtx, inData, InLineSize, 0, VideoSt.Ctx->height, VideoSt.Frame->data, VideoSt.Frame->linesize);
VideoSt.Frame->pts = VideoSt.NextPts++;
if (FFmpegEncode(VideoSt.Frame) < 0)
UE_LOG(LogTemp, Error, TEXT("Error encoding frame %d"), count);
auto ret = WriteFrame();
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("Error while writing video frame: %s"), *errstr);
}
av_packet_unref(&Pkt);
}
int UMyGameViewportClient::WriteFrame()
{
av_packet_rescale_ts(&Pkt, VideoSt.Ctx->time_base, VideoSt.Stream->time_base);
Pkt.stream_index = VideoSt.Stream->index;
return av_interleaved_write_frame(FmtCtx, &Pkt);
}
int UMyGameViewportClient::FFmpegEncode(AVFrame *frame) {
GotOutput = 0;
auto ret = avcodec_send_frame(VideoSt.Ctx, frame);
if (ret < 0 && ret != AVERROR_EOF) {
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Warning, TEXT("error during sending frame, error : %s"), *errstr);
return -1;
}
ret = avcodec_receive_packet(VideoSt.Ctx, &Pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return 0;
if (ret < 0)
{
auto errstr = FString(av_make_error_string(ret).c_str());
UE_LOG(LogTemp, Error, TEXT("Error during receiving frame, error : %s"), *errstr);
av_packet_unref(&Pkt);
return -1;
}
GotOutput = 1;
return 0;
}
void UMyGameViewportClient::CloseStream()
{
avcodec_free_context(&VideoSt.Ctx);
av_frame_free(&VideoSt.Frame);
sws_freeContext(SwsCtx);
if (!(Fmt->flags & AVFMT_NOFILE))
{
auto ret = avio_closep(&FmtCtx->pb);
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("avio close failed: %s"), *errstr);
}
}
avformat_free_context(FmtCtx);
}
void UMyGameViewportClient::TidyUp()
{
/* get the delayed frames */
for (GotOutput = 1; GotOutput; count++)
{
fflush(stdout);
FFmpegEncode(nullptr);
if (GotOutput)
{
auto ret = WriteFrame(false);
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("Error while writing video frame: %s"), *errstr);
}
av_packet_unref(&Pkt);
}
}
auto ret = av_write_trailer(FmtCtx);
if (ret < 0)
{
auto errstr = FString(av_err2str(ret));
UE_LOG(LogTemp, Error, TEXT("writing trailer error: %s"), *errstr);
}
CloseStream();
}
void UMyGameViewportClient::Draw(FViewport* Viewport, FCanvas* SceneCanvas)
{
Super::Draw(Viewport, SceneCanvas);
if (Over) // You may need to set this in other class
{
Over = false;
TidyUp();
}
else {
CaptureFrame();
}
}
void UMyGameViewportClient::CaptureFrame()
{
if (!Viewport) {
UE_LOG(LogTemp, Error, TEXT("No viewport"));
return;
}
if (ViewportSize.X == 0 || ViewportSize.Y == 0) {
UE_LOG(LogTemp, Error, TEXT("Viewport size is 0"));
return;
}
ColorBuffer.Empty();
if (!Viewport->ReadPixels(ColorBuffer, FReadSurfaceDataFlags(),
FIntRect(0, 0, ViewportSize.X, ViewportSize.Y)))
{
UE_LOG(LogTemp, Error, TEXT("Cannot read from viewport"));
return;
}
EncodeAndWrite(); // call InitCodec() before this
}
关于c++ - 使用 ffmpeg 将帧编码为视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46035387/
我对自定义 CSS 或在将图像作为 Logo 上传到页面时使用编码 block 有疑问。我正在为我的网站使用 squarespace,我需要帮助编码我的 Logo 以使其适合每个页面。一个选项是使用自
如 encoding/json 包文档中所述, Marshal traverses the value v recursively. If an encountered value implement
我必须做一些相当于Java中的iconv -f utf8 -t sjisMS $INPUT_FILE的事情。该命令在 Unix 中 我在java中没有找到任何带有sjisMS的编码。 Java中有Sh
从 PHP 5.3 迁移到 PHP 5.6 后,我遇到了编码问题。我的 MySQL 数据库是 latin1,我的 PHP 文件是 windows-1251。现在一切都显示为“ñëåäíèòå àäðå
我有一个 RScript文件(我们称之为 main.r ),它引用了另一个文件,使用以下代码: source("functions.R") 但是,当我运行 RScript 文件时,它提示以下错误:
我无法设法从 WSDL 创建 RPC/编码风格的代码 - 有谁知道哪个框架可以做到这一点? 带有 adb 和 xmlbeans 映射的 Axis2 无法正常工作(无法处理响应中的肥皂编码)直接使用 X
安装了最新版本的Node.Js()和npm包**(1.2.10)**当我运行 Express 命令来生成项目时,它向我抛出以下错误 buffer.js:240 switch (encoding &
JavaScript中有JSON编码/解码base64编码/解码函数吗? 最佳答案 是的,btoa() 和 atob() 在某些浏览器中可以工作: var enc = btoa("this is so
>>> unicode('восстановление информации', 'utf-16') Traceback (most recent call last): File "", line
我当然熟悉 java.net.URLEncoder 和 java.net.URLDecoder 类。但是,我只需要 HTML 样式的编码。 (我不想将 ' ' 替换为 '+' 等)。我不知道任何只做
有一个非常简单的 SSIS 包: OLE DB Source 通过 View 获取数据(数据库表 nvarchar 或 nchar 中的所有字符串列)。 派生列,用于格式化现有日期并将其添加到数据集(
我正在使用一个在 Node 中进行base64编码的软件,如下所示: const enc = new Buffer('test', 'base64') console.log(enc) 显示: 我正
我试图将带有日语字符的数据插入到 oracle 数据库中。事情是保存在数据库中的是一堆倒置的问号。我该如何解决这个问题 最佳答案 见 http://www.errcode.net/blogs/?p=6
当我在 java 中解压 zip 文件时,我发现文件名中出现了带有重音字符的奇怪行为。 西索: Add File user : L'equipe Technique -- Folder : spec
在网上冲浪我找到了 ExtJS 的 Ext.Gantt 插件,该扩展有一个特殊的编码。任何人都知道如何编码那样或其他复杂的形式。 Encoded Gantt Chart 最佳答案 它似乎被 Dean
我正在用C语言做一个编码任务,我进展顺利,直到读取符号并根据表格分配相应的代码的部分。我必须连接几个代码,直到它们的长度达到 32 位,为此我必须将它们写入一个文件中。这种写入文件的方法给我带来了很多
我有一个外部链接的 javascript 文件。在那个 javascript 里面,我有这个功能: function getMonthNumber(monthName){ monthName = mo
使用mechanize,我检索到一个网页的源页面,其中包含一些非ASCII字符,比如汉字。 代码如下: #using python2.6 from mechanize import Browser b
我有一个包含字母 ø 的文件。当我用这段代码 File.ReadLines(filePath) 读取它时,我得到了一个问号而不是它。 当我像这样添加编码时 File.ReadLines(filePat
如何翻译下面的字符串 H.P. Dembinski, B. K\'{e}gl, I.C. Mari\c{s}, M. Roth, D. Veberi\v{c} 进入 H. P. Dembinski,
我是一名优秀的程序员,十分优秀!