Skip to content

Commit

Permalink
Output: gop based segments -- overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
msg7086 committed Jul 13, 2020
1 parent d5de898 commit a1b3bce
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 88 deletions.
181 changes: 107 additions & 74 deletions source/output/gop.cpp
Original file line number Diff line number Diff line change
@@ -1,64 +1,104 @@
#include "gop.h"
/*****************************************************************************
* MIT License
*
* Copyright (c) 2018-2019 Xinyue Lu
*
* Authors: Xinyue Lu <i@7086.in>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*****************************************************************************
* The MIT License applies to this file only.
*****************************************************************************/

#define NALU_LENGTH_SIZE 4
#include "gop.h"

/*******************/
using namespace X265_NS;
using namespace std;

#define GOP_LOG_ERROR( ... ) general_log( NULL, "gop", X265_LOG_ERROR, __VA_ARGS__ )
#ifdef _MSC_VER
#include <windows.h>
#define sleep(x) Sleep((x) * 1000)
#endif
#define TIME_WAIT 30

#define GOP_FAIL_IF_ERR_R( cond, ... )\
if( cond )\
{\
GOP_LOG_ERROR( __VA_ARGS__ );\
b_fail = true;\
clean_up();\
return;\
FILE* GOPOutput::open_file_for_write(const string fname, bool retry)
{
while(true)
{
FILE* fp = fopen(fname.c_str(), "wb");
if(fp != NULL)
return fp;
if(!retry)
break;
// Retrying
general_log(NULL, getName(), X265_LOG_WARNING,
"unable to open file %s for writing, error %d %s, retrying in %d seconds.\n", fname.c_str(), errno, strerror(errno), TIME_WAIT);
sleep(TIME_WAIT);
}
// Failed
b_fail = true;
general_log(NULL, getName(), X265_LOG_ERROR,
"unable to open file %s for writing, error %d %s.\n", fname.c_str(), errno, strerror(errno));
return NULL;
}

#define GOP_FAIL_IF_ERR( cond, ... )\
if( cond )\
{\
GOP_LOG_ERROR( __VA_ARGS__ );\
b_fail = true;\
clean_up();\
return -1;\
void GOPOutput::smart_fwrite(const void* data, size_t size, FILE* file)
{
size_t written;
while(true)
{
written = fwrite(data, 1, size, file);
if(written == size)
break;
// ENOSPC
general_log(NULL, getName(), X265_LOG_WARNING,
"unable to write, error %d %s, retrying in %d seconds.\n", errno, strerror(errno), TIME_WAIT);
fseek(file, data_pos, SEEK_SET);
sleep(TIME_WAIT);
}
// Worked
data_pos += written;
}

/*******************/

using namespace std;

namespace x265 {

void GOPOutput::clean_up()
{
if(data_file) fclose(data_file);
if(gop_file) fclose(gop_file);
}

int GOPOutput::openFile(const char *gop_filename)
int GOPOutput::openFile(const char* gop_filename)
{
gop_file = fopen(gop_filename, "wb");
GOP_FAIL_IF_ERR(!gop_file, "cannot open output file `%s'.\n", gop_filename);

const char* slash = strrchr(gop_filename, '/');
if(slash == NULL) slash = strrchr(gop_filename, '\\');
if(slash == NULL) {
dir_prefix = "";
slash = gop_filename;
}
else {
int dir_len = slash - gop_filename + 2;
char* tmp_dir_prefix = new char[dir_len];
strncpy(tmp_dir_prefix, gop_filename, dir_len - 1);
tmp_dir_prefix[dir_len - 1] = 0;
dir_prefix = tmp_dir_prefix;
slash++;
gop_file = open_file_for_write(gop_filename, false);
if(!gop_file) return -1;

string gop_fn(gop_filename);
size_t pos;
if((pos = gop_fn.rfind('/')) != string::npos || (pos = gop_fn.rfind('\\')) != string::npos)
{
dir_prefix = gop_fn.substr(0, pos+1);
gop_fn = gop_fn.substr(pos+1);
}
int len = strlen(slash) - 3;
filename_prefix = new char[len];
strncpy(filename_prefix, slash, len - 1);
filename_prefix[len - 1] = 0;

if((pos = gop_fn.rfind('.')) != string::npos)
filename_prefix = gop_fn.substr(0, pos);
else
filename_prefix = gop_fn;

return 0;
}
Expand All @@ -69,13 +109,10 @@ void GOPOutput::setParam(x265_param *p_param)
p_param->bRepeatHeaders = false;
i_numframe = 0;

int len = strlen(dir_prefix) + strlen(filename_prefix) + 20;
char opt_filename[len];
sprintf(opt_filename, "%s%s.options", dir_prefix, filename_prefix);
FILE* opt_file = fopen(opt_filename, "wb");
GOP_FAIL_IF_ERR_R(!opt_file, "cannot open options file `%s'.\n", opt_filename);
FILE* opt_file = open_file_for_write(dir_prefix + filename_prefix + ".options", false);
if(!opt_file) return;

fprintf(gop_file, "#options %s.options\n", filename_prefix);
fprintf(gop_file, "#options %s.options\n", filename_prefix.c_str());

fprintf(opt_file, "b-frames %d\n", p_param->bframes);
fprintf(opt_file, "b-pyramid %d\n", p_param->bBPyramid);
Expand All @@ -89,58 +126,56 @@ void GOPOutput::setParam(x265_param *p_param)
fprintf(opt_file, "sar-height %d\n", p_param->vui.sarHeight);
fprintf(opt_file, "primaries-index %d\n", p_param->vui.colorPrimaries);
fprintf(opt_file, "transfer-index %d\n", p_param->vui.transferCharacteristics);
fprintf(opt_file, "matrix-index %d\n", p_param->vui.matrixCoeffs >= 0 ? p_param->vui.matrixCoeffs : ISOM_MATRIX_INDEX_UNSPECIFIED);
fprintf(opt_file, "matrix-index %d\n", p_param->vui.matrixCoeffs >= 0 ? p_param->vui.matrixCoeffs : GOP_ISOM_MATRIX_INDEX_UNSPECIFIED);
fprintf(opt_file, "full-range %d\n", p_param->vui.bEnableVideoFullRangeFlag >= 0 ? p_param->vui.bEnableVideoFullRangeFlag : 0);

fclose(opt_file);
}

int GOPOutput::writeHeaders(const x265_nal* p_nal, uint32_t nalcount)
{
GOP_FAIL_IF_ERR(nalcount < 3, "header should contain 3+ nals");
assert(nalcount >= 3); // header should contain 3+ nals

int len = strlen(filename_prefix) + 20;
char hdr_filename[len];
sprintf(hdr_filename, "%s%s.headers", dir_prefix, filename_prefix);
FILE* hdr_file = fopen(hdr_filename, "wb");
GOP_FAIL_IF_ERR(!hdr_file, "cannot open headers file `%s'.\n", hdr_filename);
FILE* hdr_file = open_file_for_write(dir_prefix + filename_prefix + ".headers", false);
if(!hdr_file) return -1;

fprintf(gop_file, "#headers %s.headers\n", filename_prefix);
fprintf(gop_file, "#headers %s.headers\n", filename_prefix.c_str());

for(unsigned int i = 0; i < nalcount; i++)
fwrite(p_nal[i].payload, sizeof(uint8_t), p_nal[i].sizeBytes, hdr_file);
smart_fwrite(p_nal[i].payload, p_nal[i].sizeBytes, hdr_file);

fclose(hdr_file);
return p_nal[0].sizeBytes + p_nal[1].sizeBytes + p_nal[2].sizeBytes;
}

int GOPOutput::writeFrame(const x265_nal* p_nalu, uint32_t nalcount, x265_picture& pic)
{
const bool b_keyframe = pic.sliceType == X265_TYPE_IDR;
const bool is_keyframe = pic.sliceType == X265_TYPE_IDR;
int i_size = 0;

if (b_keyframe) {
if (is_keyframe) {
if (data_file)
fclose(data_file);
int len = strlen(filename_prefix) + 20;
char data_filename[len];
sprintf(data_filename, "%s%s.data-%d", dir_prefix, filename_prefix, i_numframe);
data_file = fopen(data_filename, "wb");
GOP_FAIL_IF_ERR(!data_file, "cannot open data file `%s'.\n", data_filename);
fprintf(gop_file, "%s.data-%d\n", filename_prefix, i_numframe);
stringstream ss;
ss << filename_prefix << string("-") << std::setfill('0') << setw(6) << i_numframe << string(".hevc-gop-data");
string data_filename = ss.str();
data_file = open_file_for_write(dir_prefix + data_filename, i_numframe > 0);
if(!data_file) return -1;
data_pos = 0;
fprintf(gop_file, "%s\n", data_filename.c_str());
fflush(gop_file);
}
int8_t ts_len = 2 * sizeof(int64_t);
int8_t ts_lenx[4] = {0, 0, 0, ts_len};
fwrite(&ts_lenx, sizeof(int8_t), 4, data_file);
fwrite(&pic.pts, sizeof(int64_t), 1, data_file);
fwrite(&pic.dts, sizeof(int64_t), 1, data_file);
smart_fwrite(&ts_lenx, 4, data_file);
smart_fwrite(&pic.pts, sizeof(int64_t), data_file);
smart_fwrite(&pic.dts, sizeof(int64_t), data_file);

for(uint8_t i = 0; i < nalcount; i++)
i_size += p_nalu[i].sizeBytes;

for(uint8_t i = 0; i < nalcount; i++)
fwrite(p_nalu[i].payload, sizeof(uint8_t), p_nalu[i].sizeBytes, data_file);
smart_fwrite(p_nalu[i].payload, p_nalu[i].sizeBytes, data_file);

i_numframe++;

Expand All @@ -151,5 +186,3 @@ void GOPOutput::closeFile(int64_t, int64_t)
{
clean_up();
}

}
64 changes: 50 additions & 14 deletions source/output/gop.h
Original file line number Diff line number Diff line change
@@ -1,38 +1,75 @@
/*****************************************************************************
* MIT License
*
* Copyright (c) 2018-2019 Xinyue Lu
*
* Authors: Xinyue Lu <i@7086.in>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*****************************************************************************
* The MIT License applies to this file only.
*****************************************************************************/

#ifndef X265_HEVC_GOP_H
#define X265_HEVC_GOP_H

#include "output.h"
#include "common.h"
#include <fstream>
#include <iostream>
#include <lsmash.h>
#include <string>
// #include <fstream>
// #include <iostream>
#include <sstream>
#include <iomanip>

enum
{
GOP_ISOM_MATRIX_INDEX_UNSPECIFIED = 2
};

using namespace X265_NS;
using namespace std;

namespace x265 {
class GOPOutput : public OutputFile
{
protected:
bool b_fail;
int openFile(const char *fname);
int openFile(const char* fname);
FILE* open_file_for_write(const string fname, bool retry);
void smart_fwrite(const void* data, size_t size, FILE* file);
void clean_up();

FILE* gop_file;
FILE* data_file;
char* filename_prefix;
const char* dir_prefix;
void FixTimeScale(uint64_t &);
int64_t GetTimeScaled(int64_t);
size_t data_pos;
string filename_prefix;
string dir_prefix;
InputFileInfo info;
int i_numframe;

public:
GOPOutput(const char *fname, InputFileInfo& inputInfo)
GOPOutput(const char* fname, InputFileInfo& inputInfo)
{
info = inputInfo;
b_fail = false;
gop_file = NULL;
data_file = NULL;
if(openFile(fname) != 0)
b_fail = true;
openFile(fname);
}
bool isFail() const
{
Expand All @@ -42,7 +79,7 @@ class GOPOutput : public OutputFile
bool needPTS() const { return true; }

const char *getName() const { return "gop"; }
void setParam(x265_param *param);
void setParam(x265_param* param);
int writeHeaders(const x265_nal* nal, uint32_t nalcount);
int writeFrame(const x265_nal* nal, uint32_t nalcount, x265_picture& pic);
void closeFile(int64_t largest_pts, int64_t second_largest_pts);
Expand All @@ -51,6 +88,5 @@ class GOPOutput : public OutputFile
delete this;
}
};
}

#endif

0 comments on commit a1b3bce

Please sign in to comment.