- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 OpenH264作为编码器,我想使用 libmp4v2 将其输出复用到可播放的 mp4 中
生成的 .mp4
只能部分工作。可在VLC播放和 MPC-HC但不在 Windows Media Player 或 Windows 10“电影和电视”应用程序中。
我的目标是该文件适用于所有这些播放器。
两个 Windows 播放器都告诉我他们不知道编解码器,所以他们无法播放:
这不是真的,因为我可以通过使用 FFmpeg 使用相同的 h264 比特流播放手动多路复用文件。从命令行:
ffmpeg -i "testenc.h264" -c:v copy -f mp4 "output.mp4"
根据这些知识,我认为我的编码过程工作正常,问题出在 muxing 过程中。
编辑:感谢 Rudolfs Bundulis 的回答,他指出 SPS/PPS 数据丢失,我得以重组我的代码。它现在尝试通过分析编码器比特流并在必要时调用 MP4AddH264SequenceParameterSet
或 MP4AddH264PictureParameterSet
来包含丢失的数据。但仍然没有成功。
我的完整代码:
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <chrono>
#include "mp4v2/mp4v2.h"
#include "codec_api.h"
#define WIDTH 1280
#define HEIGHT 960
#define DURATION MP4_INVALID_DURATION
#define NAL_SPS 1
#define NAL_PPS 2
#define NAL_I 3
#define NAL_P 4
using namespace std;
using namespace chrono;
/* Just some dummy data to see artifacts ect */
void prepareFrame(int i, SSourcePicture* pic) {
for (int y = 0; y<HEIGHT; y++) {
for (int x = 0; x<WIDTH; x++) {
pic->pData[0][y * WIDTH + x] = x + y + i * 3;
}
}
for (int y = 0; y<HEIGHT / 2; y++) {
for (int x = 0; x<WIDTH / 2; x++) {
pic->pData[1][y * (WIDTH / 2) + x] = 128 + y + i * 2;
pic->pData[2][y * (WIDTH / 2) + x] = 64 + x + i * 5;
}
}
pic->uiTimeStamp = (i + 1) * 1000 / 75;
}
void printHex(const unsigned char* arr, int len) {
for (int i = 0; i < len; i++) {
if (arr[i] < 16) {
cout << "0";
}
cout << hex << (int)arr[i] << " ";
}
cout << endl;
}
void mp4Encode(MP4FileHandle mp4Handle, MP4TrackId track, uint8_t * bitstream, int length) {
int index = -1;
if (bitstream[0] == 0 && bitstream[1] == 0 && bitstream[2] == 0 && bitstream[3] == 1 && bitstream[4] == 0x67) {
index = NAL_SPS;
}
if (bitstream[0] == 0 && bitstream[1] == 0 && bitstream[2] == 0 && bitstream[3] == 1 && bitstream[4] == 0x68) {
index = NAL_PPS;
}
if (bitstream[0] == 0 && bitstream[1] == 0 && bitstream[2] == 0 && bitstream[3] == 1 && bitstream[4] == 0x65) {
index = NAL_I;
}
if (bitstream[0] == 0 && bitstream[1] == 0 && bitstream[2] == 0 && bitstream[3] == 1 && bitstream[4] == 0x61) {
index = NAL_P;
}
switch (index) {
case NAL_SPS:
cout << "Detected SPS" << endl;
MP4AddH264SequenceParameterSet(mp4Handle, track, bitstream + 4, length - 4);
break;
case NAL_PPS:
cout << "Detected PPS" << endl;
MP4AddH264PictureParameterSet(mp4Handle, track, bitstream + 4, length - 4);
break;
case NAL_I:
{
cout << "Detected I" << endl;
uint8_t * IFrameData = (uint8_t *) malloc(length + 1);
IFrameData[0] = (length - 3) >> 24;
IFrameData[1] = (length - 3) >> 16;
IFrameData[2] = (length - 3) >> 8;
IFrameData[3] = (length - 3) & 0xff;
memcpy(IFrameData + 4, bitstream + 3, length - 3);
if (!MP4WriteSample(mp4Handle, track, IFrameData, length + 1, DURATION, 0, 1)) {
cout << "Error when writing sample" << endl;
system("pause");
exit(1);
}
free(IFrameData);
break;
}
case NAL_P:
{
cout << "Detected P" << endl;
bitstream[0] = (length - 4) >> 24;
bitstream[1] = (length - 4) >> 16;
bitstream[2] = (length - 4) >> 8;
bitstream[3] = (length - 4) & 0xff;
if (!MP4WriteSample(mp4Handle, track, bitstream, length, DURATION, 0, 1)) {
cout << "Error when writing sample" << endl;
system("pause");
exit(1);
}
break;
}
}
if (index == -1) {
cout << "Could not detect nal type" << endl;
system("pause");
exit(1);
}
}
int main()
{
//just to measure performance
high_resolution_clock::time_point time = high_resolution_clock::now();
//Create MP4
MP4FileHandle mp4Handle = MP4Create("test.mp4", 0);
MP4SetTimeScale(mp4Handle, 90000);
//Create filestream for binary h264 output for testing
FILE* targetFile;
targetFile = fopen("testenc.h264", "wb");
if (!targetFile) {
cout << "failed to create file" << endl;
system("pause");
return 1;
}
ISVCEncoder *encoder;
int rv = WelsCreateSVCEncoder(&encoder);
//Encoder params
SEncParamExt param;
encoder->GetDefaultParams(¶m);
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
param.fMaxFrameRate = 75.f;
param.iLtrMarkPeriod = 75;
param.iPicWidth = WIDTH;
param.iPicHeight = HEIGHT;
param.iTargetBitrate = 40000000;
param.bEnableDenoise = false;
param.iSpatialLayerNum = 1;
param.bUseLoadBalancing = false;
param.bEnableSceneChangeDetect = false;
param.bEnableBackgroundDetection = false;
param.bEnableAdaptiveQuant = false;
param.bEnableFrameSkip = false;
param.iMultipleThreadIdc = 16;
//param.uiIntraPeriod = 10;
for (int i = 0; i < param.iSpatialLayerNum; i++) {
param.sSpatialLayers[i].iVideoWidth = WIDTH >> (param.iSpatialLayerNum - 1 - i);
param.sSpatialLayers[i].iVideoHeight = HEIGHT >> (param.iSpatialLayerNum - 1 - i);
param.sSpatialLayers[i].fFrameRate = 75.f;
param.sSpatialLayers[i].iSpatialBitrate = param.iTargetBitrate;
param.sSpatialLayers[i].uiProfileIdc = PRO_BASELINE;
param.sSpatialLayers[i].uiLevelIdc = LEVEL_4_2;
param.sSpatialLayers[i].iDLayerQp = 42;
SSliceArgument sliceArg;
sliceArg.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
sliceArg.uiSliceNum = 16;
param.sSpatialLayers[i].sSliceArgument = sliceArg;
}
param.uiMaxNalSize = 1500;
param.iTargetBitrate *= param.iSpatialLayerNum;
encoder->InitializeExt(¶m);
int videoFormat = videoFormatI420;
encoder->SetOption(ENCODER_OPTION_DATAFORMAT, &videoFormat);
MP4TrackId track = MP4AddH264VideoTrack(mp4Handle, 90000, 90000/25, WIDTH, HEIGHT, 66, 192, 42, 3);
MP4SetVideoProfileLevel(mp4Handle, 0x7f);
SFrameBSInfo info;
memset(&info, 0, sizeof(SFrameBSInfo));
SSourcePicture pic;
memset(&pic, 0, sizeof(SSourcePicture));
pic.iPicWidth = WIDTH;
pic.iPicHeight = HEIGHT;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = pic.iPicWidth;
pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;
int frameSize = WIDTH * HEIGHT * 3 / 2;
pic.pData[0] = new unsigned char[frameSize];
pic.pData[1] = pic.pData[0] + WIDTH * HEIGHT;
pic.pData[2] = pic.pData[1] + (WIDTH * HEIGHT >> 2);
for (int num = 0; num<75; num++) {
cout << "-------FRAME " << dec << num << "-------" << endl;
prepareFrame(num, &pic);
rv = encoder->EncodeFrame(&pic, &info);
if (!rv == cmResultSuccess) {
cout << "encode failed" << endl;
continue;
}
if (info.eFrameType != videoFrameTypeSkip) {
for (int i = 0; i < info.iLayerNum; ++i) {
int len = 0;
const SLayerBSInfo& layerInfo = info.sLayerInfo[i];
for (int j = 0; j < layerInfo.iNalCount; ++j) {
cout << "Layer: " << dec << i << "| Nal: " << j << endl << "Hex: ";
printHex(info.sLayerInfo[i].pBsBuf + len, 20);
mp4Encode(mp4Handle, track, info.sLayerInfo[i].pBsBuf + len, layerInfo.pNalLengthInByte[j]);
len += layerInfo.pNalLengthInByte[j];
}
//mp4Encode(mp4Handle, track, info.sLayerInfo[i].pBsBuf, len);
}
//fwrite(info.sLayerInfo[0].pBsBuf, 1, len, targetFile);
}
}
int res = 0;
encoder->GetOption(ENCODER_OPTION_PROFILE, &res);
cout << res << endl;
fflush(targetFile);
fclose(targetFile);
encoder->Uninitialize();
WelsDestroySVCEncoder(encoder);
//Close MP4
MP4Close(mp4Handle);
cout << "done in: ";
cout << duration_cast<milliseconds>(high_resolution_clock::now() - time).count() << endl;
system("pause");
return 0;
}
最佳答案
您可以使用来自 GPAC 的 MP4Box分析两个文件的 MP4 框布局。 如此处所示,坏文件缺少 avcC 框中的 SPS/PPS 数据。相同的 NAL 单元很可能也存储在 NAL 单元中,但规范要求它们也存在于 avcC 盒中(一些播放器处理流中内联的 SPS/PPS,但这是一种不好的做法,因为它破坏了寻找和什么不是,因为您不知道哪些样本组引用了哪些参数预先设置)。
在谷歌上快速搜索 libmp4v2 给了我这个 example这显示了如何实际调用 MP4AddH264SequenceParameterSet
/MP4AddH264PictureParameterSet
提供 SPS/PPS,而您只需调用 MP4WriteSample
这可能是问题所在。
我的主观意见——我从未使用过 libmp4v2,但如果你也不知道如何使用它,只需使用 ffmpeg——更多的例子和更大的社区。将 H.264 Muxing 成 mp4 非常简单,网上也有很多例子。
总结
avcC
中box - 如果将这些单元与样本放在一起,一些播放器可能能够解码流,但为了符合规范,应该始终具有 avcC
框存在,否则播放器可以自由播放流。P4AddH264SequenceParameterSet/MP4AddH264PictureParameterSet
.要获得 SPS/PPS 数据,应该解析比特流。这取决于比特流格式(如果使用具有起始代码的附件 b 格式或具有交错长度的 avcc 格式 - 请参阅 this 了解更多信息)。提取 SPS/PPS 信息后,应将其传递给 muxing 库。stsd
流描述框,然后引用它们,但据我所知,Windows Media Player 对此处理不佳,因此如果可能的话,请坚持使用单个 SPS/PPS 集。人们应该能够将编码器配置为不在每个关键帧上发出重复的 SPS/PPS 条目。关于c++ - 使用 libmp4v2 和 OpenH264 将 h264 混合为 mp4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49397904/
我尝试在安装了多类型 MFC 库的 visual studio 2015 MFC 上运行以前编写的 MFC c++ 代码。 但是,我这里仍然有 12 个关于缺少函数的错误: IntelliSense:
我正在学习 OOP 并且有疑问。假设我有一个包含 ClassB.h 的文件 ClassA.h,并且在某些时候我的 ClassB.h 需要包含 ClassA .h。 这会产生一个错误,我想我明白为什么会
我开始使用 CUDA 进行编程,在一些示例中我找到了包含文件 cuda.h、cuda_runtime.h 和 cuda_runtime_api.h 包含在代码中。有人可以向我解释一下这些文件之间的区别
我有一些生成正则表达式的代码。那么下面的表达式实际上是: ^(?:\s*((exclude|include|hide|show|protect|risk|dir-merge|merge)),\s*((
我一直在查看一些源代码,以更好地了解我们使用的这款游戏的核心,并编写更可靠、更快速的插件。然后我发现了这段奇怪的代码...... public void setMaxH(double amount)
通常我们会使用标准类型作为 std::unordered_map 的键和值.但现在我需要自定义我自己的键和值类。 键类在block_cache_key.h 中定义如下: #ifndef BLOCK_C
例如,我想要两个头文件,它们可以依赖于另一个头文件中的函数。 //Header1.h file #include Header2.h void h1(){ //... func1(); } v
我正在研究来自 Sedgewick 的 Shell 排序 Algorithms in C part 1-4在第 172 页。 我使用 size (数组的长度),而不是 l和 r (开始和结束);所以我
我在 macOS BigSur 上通过 VMWare 使用 Ubuntu 20.04.2 LTS。我安装了最新版本的 tcl、tcl-dev、tk 和 tk-dev - 版本 8.6。我想编译 Arc
我用我的 glu 和 gl 头文件构建了一个 OpenGL 程序,默认包含在 windows 7 专业版中。现在,我买了一本描述 OpenGL 游戏开发的书。这本书的作者说,我必须在我的项目中包含 g
我想在 token 中保留特殊字符,同时仍对特殊字符进行 token 化。说我有话 "H&R Blocks" 我想将其标记为 "H", "R", "H&R", "Blocks" 我读了http://w
关于 hash 作为 trans 参数的另一个问题。在下面的代码中,简单地使用 hash 会给出不正确的结果,但是将其替换为 keys 和 values 会使其正确。怎么了? my @alph1 =
我已经编写了一个 C 程序,它获取屏幕像素的 RGB 值 (0-255),并知道其位置 (x,y)。它可以在 Linux 中运行,但是当我尝试在 Visual Studio (Windows) 中编译
我已经使用 Windows 7 专业版中默认包含的 glu 和 gl 头文件构建了一个 OpenGL 程序。现在,我买了一本描述 OpenGL 游戏开发的书。这本书的作者说,我必须将glew head
#include using namespace std; #include //#include int main() { initscr();
h:messages h:form 内的组件还显示与外部组件相关的消息。 如何限制它只显示与包含 h:form 内的组件相关的消息? 我不喜欢用单独的h:message来使我的代码膨胀。每个输入组件的
我下载了示例代码和 cpp 文件,其中包含 list.h、queue.h 和 vector.h 等头文件,如果我尝试构建,我会收到“ fatal error :没有这样的文件或目录编译终止”我想我应该
我有一个编译成功的桌面项目,但是在我向项目添加新配置以支持 Windows Mobile 平台后,我收到以下错误: error C2146: syntax error : missing ';' be
有很多关于这个错误的帖子,但我无法解决它,我希望你能拿出解决方案。我在 Ubuntu 机器上。 ~/graphmap2$ 在这个文件夹中,我下载了 zlib。可以看图 经过一番谷歌搜索后,我还注意到没
是否可以在 Visual C++ 中使用以下 header : 图.h dos.h bios.h 最佳答案 据我所知,无法在 Visual C++ 中使用它, 与此同时,我希望您关注 Open Wat
我是一名优秀的程序员,十分优秀!