-
-
Save victusfate/6785585 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <core/BitStream.h> | |
#include <core/VideoEncoder.h> | |
#include <core/TestPattern.h> | |
#include <flv/FLVReader.h> | |
#define TEST_FLV_READER 0 | |
int main() { | |
#if TEST_FLV_READER | |
BitStream bs; | |
bs.loadFile("./barsandtone.flv"); | |
FLVReader flv(bs); | |
flv.parse(); | |
#endif | |
VideoSettings vs; | |
vs.width = 320; | |
vs.height = 240; | |
vs.fps = 20; | |
VideoEncoder ve; | |
if(!ve.setup(vs)) { | |
printf("error: cannot setup the VideoEncoder"); | |
return EXIT_FAILURE; | |
} | |
if(!ve.initialize()) { | |
printf("error: cannot intialize the VideoEncoder"); | |
return EXIT_FAILURE; | |
} | |
std::vector<uint8_t> test_frame; | |
TestPattern tp; | |
tp.openFile("test_frames/raw.yuv"); | |
tp.setup(vs.width, vs.height, 24); | |
FLVTag flv_tag; | |
AVPacket pkt; | |
pkt.allocate(vs.width * vs.height + (vs.width * vs.height / 4) * 2); | |
ve.openFile("test_frames/raw.h264"); | |
for(int i = 0; i < 240; ++i) { | |
tp.generateFrame(pkt.data); | |
tp.writeFrameToFile(pkt.data); | |
ve.encodePacket(&pkt, flv_tag); | |
ve.writeTagToFile(flv_tag); | |
} | |
ve.closeFile(); | |
return 1; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cmath> | |
#include <assert.h> | |
#include <core/TestPattern.h> | |
TestPattern::TestPattern() | |
:w(0) | |
,h(0) | |
,frame_num(0) | |
,duration(0) | |
{ | |
} | |
TestPattern::~TestPattern() { | |
if(ofs.is_open()) { | |
ofs.close(); | |
} | |
} | |
bool TestPattern::setup(int width, int height, int framespersec) { | |
assert(width > 0); | |
assert(height > 0); | |
assert(framespersec > 0); | |
w = width; | |
h = height; | |
fps = framespersec; | |
return true; | |
} | |
void TestPattern::generateFrame(std::vector<uint8_t>& result) { | |
int y_bytes = w * h; | |
int uv_bytes = w * h / 4; | |
result.assign( y_bytes + uv_bytes + uv_bytes, 0x00 ); | |
// Y channel | |
int POT = 3; | |
for(int i = 0; i < w; ++i) { | |
for(int j = 0; j < h; ++j) { | |
int dx = j * w + i; | |
result[dx] = (((i ^ j) >> POT) & 1) - 1; // checkerbord | |
} | |
} | |
// U channel | |
duration = float(frame_num)/fps; | |
float t = 0.5 + 0.5 * sin(duration * 6.2831); | |
int offset = y_bytes; | |
for(int i = 0; i < uv_bytes; ++i) { | |
int dx = offset + i; | |
result[dx] = t * 0xFF; | |
} | |
// V channel | |
offset = y_bytes + uv_bytes; | |
for(int i = 0; i < uv_bytes; ++i) { | |
int dx = offset + i; | |
result[dx] = (1.0 - t) * 0xFF; | |
} | |
++frame_num; | |
} | |
bool TestPattern::openFile(std::string path) { | |
ofs.open(path.c_str(), std::ios::out | std::ios::binary); | |
if(!ofs.is_open()) { | |
printf("error: cannot open: %s\n", path.c_str()); | |
return false; | |
} | |
return true; | |
} | |
bool TestPattern::writeFrameToFile(std::vector<uint8_t>& result) { | |
if(!ofs.is_open()) { | |
printf("error: the file is not opened! call openFile first.\n"); | |
return false; | |
} | |
ofs.write((char*)&result.front(), result.size()); | |
return true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# Test Pattern | |
Generates a test YUV/I420p pattern | |
When you call `openFile()` and `writeFrameToFile()` you can use avconv to | |
convert the raw yuv file into a video using: | |
````sh | |
./avconv -f rawvideo -s 320x240 -i raw.yuv -r 10 -vcodec h264 out.mov | |
```` | |
*/ | |
#ifndef ROXLU_TEST_PATTERN_H | |
#define ROXLU_TEST_PATTERN_H | |
#include <inttypes.h> | |
#include <vector> | |
#include <fstream> | |
class TestPattern { | |
public: | |
TestPattern(); | |
~TestPattern(); | |
bool setup(int width, int height, int fps); | |
bool openFile(std::string file); | |
void generateFrame(std::vector<uint8_t>& result); | |
bool writeFrameToFile(std::vector<uint8_t>& result); | |
private: | |
int w; | |
int h; | |
int fps; | |
unsigned long frame_num; | |
std::ofstream ofs; | |
float duration; | |
}; | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <core/VideoEncoder.h> | |
#include <core/Debug.h> | |
VideoEncoder::VideoEncoder() | |
:encoder(NULL) | |
,vflip(true) | |
,frame_num(0) | |
{ | |
} | |
VideoEncoder::~VideoEncoder() { | |
if(ofs.is_open()) { | |
closeFile(); | |
} | |
} | |
// @todo check if width / height are valid (multiples of 16 I believe) | |
bool VideoEncoder::setup(VideoSettings s) { | |
assert(s.width > 0); | |
assert(s.height > 0); | |
assert(s.fps > 0); | |
settings = s; | |
return true; | |
} | |
bool VideoEncoder::initialize() { | |
assert(settings.width); | |
assert(settings.height); | |
if(!initializeX264()) { | |
return false; | |
} | |
if(!initializePic()) { | |
return false; | |
} | |
return true; | |
} | |
bool VideoEncoder::initializeX264() { | |
assert(settings.width > 0); | |
assert(settings.height > 0); | |
assert(settings.fps > 0); | |
int r = 0; | |
x264_param_t* p = ¶ms; | |
r = x264_param_default_preset(p, "ultrafast", "zerolatency"); | |
if(r != 0) { | |
printf("error: cannot set the default preset on x264.\n"); | |
return false; | |
} | |
#if !defined(NDEBUG) | |
p->i_log_level = X264_LOG_DEBUG; | |
#endif | |
p->i_threads = 1; | |
p->i_width = settings.width; | |
p->i_height = settings.height; | |
p->i_fps_num = settings.fps; | |
p->i_fps_den = 1; | |
p->b_annexb = 1; | |
p->b_repeat_headers = 1; // @todo - test this, we might want SPS/PPS before every keyframe! (when we set b_repeat_headers to 0 and convert the raw .h264 to .mov (with avconv) the resulting mov is faulty) | |
p->i_keyint_max = settings.fps; // @todo - when writing raw frames to an .h264 file we need this else the video is going to be garbage | |
#if 0 | |
// tmp | |
p->rc.i_rc_method = X264_RC_CRF; | |
p->rc.f_rf_constant = 25; | |
p->rc.f_rf_constant_max = 45; | |
#endif | |
// end | |
r = x264_param_apply_profile(p, "baseline"); | |
if(r != 0) { | |
printf("error: cannot set the baseline profile on x264.\n"); | |
return false; | |
} | |
encoder = x264_encoder_open(p); | |
if(!encoder) { | |
printf("error: cannot create the encoder.\n"); | |
return false; | |
} | |
x264_encoder_parameters(encoder,¶ms); | |
print_x264_params(p); | |
return true; | |
} | |
bool VideoEncoder::initializePic() { | |
unsigned int csp = (vflip) ? X264_CSP_I420 | X264_CSP_VFLIP : X264_CSP_I420; | |
#if 0 | |
int r = x264_picture_alloc(&pic_out, csp, settings.width, settings.height); | |
if(r != 0) { | |
printf("error: cannot allocate the pic_out.\n"); | |
return false; | |
} | |
#endif | |
x264_picture_init(&pic_in); | |
pic_in.img.i_csp = csp; | |
pic_in.img.i_stride[0] = settings.width; | |
pic_in.img.i_stride[1] = settings.width >> 1; | |
pic_in.img.i_stride[2] = settings.width >> 1; | |
pic_in.img.i_plane = 3; | |
return true; | |
} | |
bool VideoEncoder::encodePacket(AVPacket* p, FLVTag& tag) { | |
assert(p); | |
assert(encoder); | |
size_t nbytes_y = settings.width * settings.height; | |
size_t nbytes_uv = nbytes_y / 4; | |
pic_in.img.plane[0] = &p->data.front(); | |
pic_in.img.plane[1] = &p->data[nbytes_y]; | |
pic_in.img.plane[2] = &p->data[nbytes_y + nbytes_uv]; | |
pic_in.i_pts = frame_num; | |
frame_num++; | |
x264_nal_t* nal = NULL; | |
int nals_count = 0; | |
int frame_size = x264_encoder_encode(encoder, &nal, &nals_count, &pic_in, &pic_out); | |
if(frame_size < 0) { | |
printf("error: x264_encoder_encode failed.\n"); | |
return false; | |
} | |
if(!nal) { | |
printf("error: x264_encoder_encode returned no valid nals.\n"); | |
return false; | |
} | |
tag.timestamp = p->timestamp; | |
tag.data = nal[0].p_payload; | |
tag.size = frame_size; | |
return true; | |
} | |
bool VideoEncoder::openFile(std::string filepath) { | |
if(ofs.is_open()) { | |
printf("error: cannot open the video encoder output file becuase it's already open.\n"); | |
return false; | |
} | |
ofs.open(filepath.c_str(), std::ios::binary | std::ios::out); | |
if(!ofs.is_open()) { | |
printf("error: cannot open the video encoder output file: %s\n", filepath.c_str()); | |
return false; | |
} | |
return true; | |
} | |
bool VideoEncoder::writeTagToFile(FLVTag& tag) { | |
if(!ofs.is_open()) { | |
printf("error: cannot write the video tag to the encoder output file because the file hasn't been opened yet.\n"); | |
return false; | |
} | |
ofs.write((char*)tag.data, tag.size); | |
return true; | |
} | |
bool VideoEncoder::closeFile() { | |
if(!ofs.is_open()) { | |
printf("error: cannot close the encoder file because it hasn't been opened yet.\n"); | |
return false; | |
} | |
ofs.close(); | |
return true; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment