Skip to content

Commit

Permalink
Added proper timebase extraction
Browse files Browse the repository at this point in the history
  • Loading branch information
sapharow committed Feb 21, 2017
1 parent 1243898 commit 9b6a4e6
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 29 deletions.
8 changes: 6 additions & 2 deletions include/transcode/softwareVideoTranscoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ namespace fp {
SoftwareDecoderContext(StreamType inputType);
~SoftwareDecoderContext() override;
size_t decodeFrame(const uint8_t*, size_t, Stream::Metadata*, const DecodedFrameRef&) override;

void* rawDecoder();
private:
int m_TimeBaseNum = 0;
int m_TimeBaseDen = 1;
void* m_Decoder = nullptr;
};

class SoftwareEncoderContext : public EncoderContext {
public:
SoftwareEncoderContext(const DecodedFrameRef&);
SoftwareEncoderContext(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen);
~SoftwareEncoderContext() override;
size_t encodeFrame(const DecodedFrameRef&, std::vector<uint8_t>&) override;
private:
Expand All @@ -41,7 +45,7 @@ namespace fp {
SoftwareVideoTranscoder(StreamType inputType, const VideoStreamRef& output);
protected:
DecoderContextRef createDecoder() override;
EncoderContextRef createEncoder(const DecodedFrameRef&) override;
EncoderContextRef createEncoder(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) override;
DecodedFrameRef allocateFrame() override;
};

Expand Down
76 changes: 74 additions & 2 deletions include/transcode/videoTranscoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ namespace fp {
* Return true if this frame has earlier presentation time than reference one
*/
virtual bool operator <(const DecodedFrame& reference) = 0;

/**
* Frame width
* Set by decoder
*/
uint32_t width();
void setWidth(uint32_t);

/**
* Frame height
* Set by decoder
*/
uint32_t height();
void setHeight(uint32_t);

/**
* Timebase number
* Set by decoder
*/
uint32_t timebaseNum();
void setTimebaseNum(uint32_t);

/**
* Timebase denominator
* Set by decoder
*/
uint32_t timebaseDen();
void setTimebaseDen(uint32_t);

private:
uint32_t m_Width = 0;
uint32_t m_Height = 0;
uint32_t m_TimebaseNum = 0;
uint32_t m_TimebaseDen = 1;
};

class DecoderContext {
Expand All @@ -35,15 +69,48 @@ namespace fp {

class EncoderContext {
public:
EncoderContext(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen);
virtual ~EncoderContext() = default;

/**
* Video width
*/
uint32_t width();

/**
* Video height
*/
uint32_t height();

/**
* Video stream bitrate
*/
uint32_t bitrate();

/**
* Timebase number
*/
uint32_t timebaseNum();

/**
* Timebase denominator
*/
uint32_t timebaseDen();

/**
* Routine for video frame encoding
* @param[in] frame Frame to encode
* @param[in] dest Buffer for encoded video bitstream. Shall only grow up.
* @return number of bytes allocated. Return zero if no frame produced (delayed frame)
*/
virtual size_t encodeFrame(const DecodedFrameRef& frame, std::vector<uint8_t>& dest) = 0;

private:
uint32_t m_Width;
uint32_t m_Height;
uint32_t m_Bitrate;
uint32_t m_TimebaseNum;
uint32_t m_TimebaseDen;
};

/**
Expand Down Expand Up @@ -73,9 +140,14 @@ namespace fp {
virtual DecoderContextRef createDecoder() = 0;

/**
* Create encoder context based on decoded frame
* Create encoder context
* @param[in] width Output image width
* @param[in] height Output image height
* @param[in] bitrate Output bitstream bitrate, bps
* @param[in] timebaseNum Output bitstream timebase number
* @param[in] timebaseDen Output bitstream timebase denominator
*/
virtual EncoderContextRef createEncoder(const DecodedFrameRef&) = 0;
virtual EncoderContextRef createEncoder(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen) = 0;

/**
* Allocate frame for decoding
Expand Down
12 changes: 6 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ class MyVideoStream : public fp::VideoStream {
{
// Create transcoder
fp::VideoStreamMeta outputMeta;
outputMeta.id = 0;
outputMeta.id = id();
outputMeta.type = fp::StreamType::Video_H264;
outputMeta.sync = true;
outputMeta.sync = sync();
outputMeta.width = width();
outputMeta.height = height();

Expand Down Expand Up @@ -156,10 +156,10 @@ class MySource : public fp::cap::FileSource {

fp::VideoStreamRef createVideoStream(fp::VideoStreamMeta* meta) override {
static int i = 0;
if (i) {
i++;
if (i != 2) {
return nullptr;
}
i++;
return std::make_shared<MyVideoStream>(meta);
}

Expand All @@ -172,8 +172,8 @@ class MySource : public fp::cap::FileSource {

int main(int argc, char **argv)
{
// fp::cap::FileSource src("mpt-smart-travels-classical-clip.dvb");
MySource src("football.dvb");
MySource src("mpt-smart-travels-classical-clip.dvb");
// MySource src("football.dvb");
// fp::cap::DVBSource src(DVB_ADAPTER, DVB_FRONTEND, DVB_DEMUX);

src.start();
Expand Down
50 changes: 34 additions & 16 deletions src/transcode/softwareVideoTranscoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ namespace fp {
}
}

void* SoftwareDecoderContext::rawDecoder() {
return m_Decoder;
}

size_t SoftwareDecoderContext::decodeFrame(const uint8_t* data, size_t size, Stream::Metadata* metadata, const DecodedFrameRef& outFrame) {
if (!outFrame) {
throw std::invalid_argument("Provided null output frame");
Expand All @@ -111,13 +115,23 @@ namespace fp {
int gotOutput = 0;
int nres = avcodec_decode_video2((AVCodecContext*)m_Decoder, (AVFrame*)sFrame->rawFrame(), &gotOutput, &decodePacket);
if (nres < 0) {
throw std::runtime_error("error decompressing");
throw std::runtime_error("error decoding frame");
}

if (nres) {
if (gotOutput) {
((AVFrame*)sFrame->rawFrame())->pts = ((AVFrame*)sFrame->rawFrame())->pkt_pts;
((AVFrame*)sFrame->rawFrame())->pict_type = AV_PICTURE_TYPE_S;
// Setup timebase
m_TimeBaseNum = ((AVCodecContext*)m_Decoder)->time_base.num;
m_TimeBaseDen = ((AVCodecContext*)m_Decoder)->time_base.den;

if (((AVCodecContext*)m_Decoder)->ticks_per_frame) {
m_TimeBaseNum *= ((AVCodecContext*)m_Decoder)->ticks_per_frame;
}

outFrame->setWidth(((AVFrame*)sFrame->rawFrame())->width);
outFrame->setHeight(((AVFrame*)sFrame->rawFrame())->height);
outFrame->setTimebaseNum(m_TimeBaseNum);
outFrame->setTimebaseDen(m_TimeBaseDen);
return nres;
}
}
Expand All @@ -127,28 +141,25 @@ namespace fp {

// SoftwareEncoderContext ///////////////////////////////////////////////////////////////////////////

SoftwareEncoderContext::SoftwareEncoderContext(const DecodedFrameRef& frame) {
auto sFrame = dynamic_cast<SoftwareDecodedFrame*>(frame.get());
if (!sFrame) {
throw std::runtime_error("Provided incompatible class implementation");
}

SoftwareEncoderContext::SoftwareEncoderContext(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen)
: EncoderContext(width, height, bitrate, timebaseNum, timebaseDen)
{
avcodec_register_all();
av_register_all();

auto h264 = avcodec_find_encoder(AV_CODEC_ID_H264);
if (h264) {
auto encoder = avcodec_alloc_context3(h264);
if (encoder) {
encoder->width = ((AVFrame*)sFrame->rawFrame())->width;
encoder->height = ((AVFrame*)sFrame->rawFrame())->height;
encoder->width = width;
encoder->height = height;
encoder->sample_aspect_ratio = { 1, 1 };
encoder->pix_fmt = AV_PIX_FMT_YUV420P;
encoder->bit_rate = 1024;
encoder->bit_rate = bitrate / 1024;
encoder->codec_id = AV_CODEC_ID_H264;
encoder->codec_type = AVMEDIA_TYPE_VIDEO;
encoder->time_base.num = 1001;
encoder->time_base.den = 60000;
encoder->time_base.num = timebaseNum;
encoder->time_base.den = timebaseDen;
encoder->flags |= CODEC_FLAG_CLOSED_GOP;

auto ret = avcodec_open2(encoder, h264, nullptr);
Expand Down Expand Up @@ -179,6 +190,9 @@ namespace fp {
throw std::invalid_argument("Frame can not be nullptr");
}
auto sFrame = dynamic_cast<SoftwareDecodedFrame*>(frame.get());
if (!sFrame) {
throw std::invalid_argument("Provided incompatible class implementation");
}

AVPacket encodePacket;
memset(&encodePacket, 0, sizeof(AVPacket));
Expand All @@ -192,6 +206,10 @@ namespace fp {
encodePacket.data = data.data();
encodePacket.size = data.size();

// Copy PTS data for encoder
((AVFrame*)sFrame->rawFrame())->pts = ((AVFrame*)sFrame->rawFrame())->pkt_pts;
((AVFrame*)sFrame->rawFrame())->pict_type = AV_PICTURE_TYPE_S;

int gotOutput;
auto ret = avcodec_encode_video2((AVCodecContext*)m_Encoder, &encodePacket, (AVFrame*)sFrame->rawFrame(), &gotOutput);
if (!ret && gotOutput && ((AVCodecContext*)m_Encoder)->coded_frame) {
Expand Down Expand Up @@ -225,8 +243,8 @@ namespace fp {
return std::make_shared<SoftwareDecoderContext>(inputType());
}

EncoderContextRef SoftwareVideoTranscoder::createEncoder(const DecodedFrameRef& frame) {
return std::make_shared<SoftwareEncoderContext>(frame);
EncoderContextRef SoftwareVideoTranscoder::createEncoder(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen) {
return std::make_shared<SoftwareEncoderContext>(width, height, bitrate, timebaseNum, timebaseDen);
}

DecodedFrameRef SoftwareVideoTranscoder::allocateFrame() {
Expand Down
68 changes: 65 additions & 3 deletions src/transcode/videoTranscoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,66 @@
namespace fp {
namespace trans {

uint32_t DecodedFrame::width() {
return m_Width;
}

void DecodedFrame::setWidth(uint32_t width) {
m_Width = width;
}

uint32_t DecodedFrame::height() {
return m_Height;
}

void DecodedFrame::setHeight(uint32_t height) {
m_Height = height;
}

uint32_t DecodedFrame::timebaseNum() {
return m_TimebaseNum;
}

void DecodedFrame::setTimebaseNum(uint32_t timebaseNum) {
m_TimebaseNum = timebaseNum;
}

uint32_t DecodedFrame::timebaseDen() {
return m_TimebaseDen;
}

void DecodedFrame::setTimebaseDen(uint32_t timebaseDen) {
m_TimebaseDen = timebaseDen;
}

EncoderContext::EncoderContext(uint32_t width, uint32_t height, uint32_t bitrate, uint32_t timebaseNum, uint32_t timebaseDen)
: m_Width(width)
, m_Height(height)
, m_Bitrate(bitrate)
, m_TimebaseNum(timebaseNum)
, m_TimebaseDen(timebaseDen)
{ }

uint32_t EncoderContext::width() {
return m_Width;
}

uint32_t EncoderContext::height() {
return m_Height;
}

uint32_t EncoderContext::bitrate() {
return m_Bitrate;
}

uint32_t EncoderContext::timebaseNum() {
return m_TimebaseNum;
}

uint32_t EncoderContext::timebaseDen() {
return m_TimebaseDen;
}

VideoTranscoder::VideoTranscoder(StreamType inputType, const VideoStreamRef& output)
: Transcoder(inputType, output)
{ }
Expand Down Expand Up @@ -45,7 +105,11 @@ namespace fp {
const size_t nBytes = m_DecoderContext->decodeFrame(data, size, metadata, m_DecodedFrame);
if (nBytes) {
if (!m_EncoderContext) {
auto encoder = createEncoder(m_DecodedFrame);
auto encoder = createEncoder(m_DecodedFrame->width(),
m_DecodedFrame->height(),
1024*1024,
m_DecodedFrame->timebaseNum(),
m_DecodedFrame->timebaseDen());
if (!encoder) {
throw std::runtime_error("Can not initialise encoder");
}
Expand Down Expand Up @@ -87,8 +151,6 @@ namespace fp {
} else {
insertFrame();
}
} else {
printf("no frame decompressed\n");
}
} catch (std::exception&) {
throw;
Expand Down

0 comments on commit 9b6a4e6

Please sign in to comment.