Skip to content

Commit

Permalink
IMPROVE: WIP updated ffmpeg code #124
Browse files Browse the repository at this point in the history
  • Loading branch information
spillerrec committed Aug 17, 2018
1 parent 605d697 commit 22dd81e
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 97 deletions.
9 changes: 7 additions & 2 deletions src/planes/ColorSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "../color.hpp"

#include <stdexcept>
#include <QDebug>

using namespace Overmix;

Expand All @@ -34,7 +35,9 @@ color ColorSpace::convert( color from, ColorSpace to ) const{
case Transform::YCbCr_601: from = from.rec601ToRgb(); break;
case Transform::YCbCr_709: from = from.rec709ToRgb(); break;
case Transform::JPEG: from = from.jpegToRgb(); break;
default: throw std::runtime_error( "ColorSpace::convert(): unsupported transform!" );
default:
qWarning() << "Unsupported transform: " << (int)_transform;
throw std::runtime_error( "ColorSpace::convert(): unsupported transform!" );
}

if( _transfer != to._transfer ){
Expand All @@ -48,7 +51,9 @@ color ColorSpace::convert( color from, ColorSpace to ) const{
case Transform::YCbCr_601: from = from.rgbToYcbcr( 0.299, 0.587, 0.114, gamma, true ); break;
case Transform::YCbCr_709: from = from.rgbToYcbcr( 0.2126, 0.7152, 0.0722, gamma, true ); break;
case Transform::JPEG: from = from.rgbToYcbcr( 0.299, 0.587, 0.114, gamma, false ); break;
default: throw std::runtime_error( "ColorSpace::convert(): unsupported transform!" );
default:
qWarning() << "Unsupported transform: " << (int)to._transform;
throw std::runtime_error( "ColorSpace::convert(): unsupported transform!" );
}
}

Expand Down
153 changes: 87 additions & 66 deletions src/video/VideoFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,74 +29,123 @@ extern "C" {
#include <libavutil/mathematics.h>
}

#include <QDebug>
#include <vector>


using namespace Overmix;


static unsigned read8( uint8_t *&data ){
return color::from8bit( *(data++) );
}
static unsigned read16( uint8_t *&data ){
unsigned p1 = *(data++);
unsigned p2 = *(data++);
return ( p1 + (p2 << 8) );
VideoFrame::VideoFrame( AVCodecContext &context )
: frame( av_frame_alloc() ), context( context ) { }
VideoFrame::~VideoFrame(){ av_frame_free( &frame ); }
VideoFrame::VideoFrame( VideoFrame&& other ) : frame( other.frame ), context( other.context ){
other.frame = nullptr;
}

bool VideoFrame::is_keyframe() const{ return frame->key_frame; }


VideoFrame::~VideoFrame(){ av_free( frame ); }

VideoFrame::VideoFrame( AVCodecContext &context ) {
frame = av_frame_alloc();

//Set subsampling resolution and color depth
unsigned h_sub = 2;
unsigned v_sub = 2;
struct VideoInfo{
unsigned h_sub { 2 };
unsigned v_sub { 2 };
unsigned depth { 8 };
bool planar { true };
bool rgb { false };
};

static VideoInfo getVideoInfo( AVPixelFormat pix_fmt ){
VideoInfo out;

switch( context.pix_fmt ){
//NOTE: See VideoInfo for defaults
switch( pix_fmt ){
// Half-width packed
case AV_PIX_FMT_YUYV422:
planar = false;
v_sub = 1;
out.planar = false;
out.v_sub = 1;
break;

// Quarter chroma planar
case AV_PIX_FMT_YUV420P10LE:
depth = 10;
out.depth = 10;
case AV_PIX_FMT_YUV420P: break;
case AV_PIX_FMT_YUVJ420P: break;

// Full chroma planar
case AV_PIX_FMT_YUV444P10LE:
depth = 10;
out.depth = 10;
case AV_PIX_FMT_YUV444P:
h_sub = v_sub = 1;
out.h_sub = out.v_sub = 1;
break;

case AV_PIX_FMT_GBRP:
h_sub = v_sub = 1;
rgb = true;
out.h_sub = out.v_sub = 1;
out.rgb = true;
break;

default:
throw std::runtime_error( "Unknown format: " + std::to_string( context.pix_fmt ) );
throw std::runtime_error( "Unknown format: " + std::to_string( pix_fmt ) );
}

//Initialize planes
planes.emplace_back( context.width, context.height );
planes.emplace_back( context.width/h_sub, context.height/v_sub );
planes.emplace_back( context.width/h_sub, context.height/v_sub );

return out;
};


static Transform getColorSpace( AVColorSpace colorspace ){
switch( colorspace ){
case AVCOL_SPC_UNSPECIFIED:
case AVCOL_SPC_BT709 : return Transform::YCbCr_709;
case AVCOL_SPC_SMPTE170M :
case AVCOL_SPC_SMPTE240M : return Transform::YCbCr_601;
case AVCOL_SPC_RGB : return Transform::RGB;
default: {
qWarning() << "Unknown AVColorSpace: " << colorspace;
return Transform::UNKNOWN;
}
}
}

static Transfer getColorTransfer( AVColorTransferCharacteristic color_trc ){
switch( color_trc ){
case AVCOL_TRC_UNSPECIFIED : return Transfer::REC709;
case AVCOL_TRC_SMPTE170M : return Transfer::REC709; //TODO: Correct?
case AVCOL_TRC_BT709 : return Transfer::REC709;
case AVCOL_TRC_LINEAR : return Transfer::LINEAR;
case AVCOL_TRC_IEC61966_2_1: return Transfer::SRGB;
default: {
qWarning() << "Unknown AVColorTransferCharacteristic: " << color_trc;
return Transfer::UNKNOWN;
}
}
}

bool VideoFrame::is_keyframe() const{ return frame->key_frame; }

static unsigned read8( uint8_t *&data ){
return color::from8bit( *(data++) );
}
static unsigned read16( uint8_t *&data ){
unsigned p1 = *(data++);
unsigned p2 = *(data++);
return ( p1 + (p2 << 8) ) << 4; //TODO: Adjust properly afterwards
}

void VideoFrame::prepare_planes(){
for( auto& p : planes )
ImageEx VideoFrame::toImageEx(){
if( frame->width != context.width || frame->height != context.height )
throw std::runtime_error( "VideoFrame::toImageEx - AVFrame invalid!" );

auto info = getVideoInfo( context.pix_fmt );
std::vector<Plane> planes;
qDebug() << "Color depth: " << info.depth;

//Initialize planes
planes.emplace_back( context.width, context.height );
planes.emplace_back( context.width/info.h_sub, context.height/info.v_sub );
planes.emplace_back( context.width/info.h_sub, context.height/info.v_sub );
for( auto& p : planes ) //TODO: needed?
p.fill( 0 );

if( planar ){
if( info.planar ){
auto readPlane = [=]( int index, Plane& p, unsigned (*f)( uint8_t*& ) ){
//Read an entire plane into 'p', using reader 'f'
for( unsigned iy=0; iy<p.get_height(); iy++ ){
Expand All @@ -109,10 +158,9 @@ void VideoFrame::prepare_planes(){
};

for( int i=0; i<3; i++ )
readPlane( i, planes[i], (depth <= 8) ? &read8 : &read16 );
readPlane( i, planes[i], (info.depth <= 8) ? &read8 : &read16 );
}
else{

else{
for( unsigned iy=0; iy<planes[0].get_height(); iy++ ){
auto luma = planes[0][iy];
auto row1 = planes[1][iy];
Expand All @@ -128,39 +176,12 @@ void VideoFrame::prepare_planes(){
}
}
}
}

static Transform getColorSpace( AVFrame& frame ){
switch( frame.colorspace ){
case AVCOL_SPC_BT709 : return Transform::YCbCr_709;
case AVCOL_SPC_SMPTE170M:
case AVCOL_SPC_SMPTE240M: return Transform::YCbCr_601;
case AVCOL_SPC_RGB : return Transform::RGB;
default: {
//TODO: warning
return Transform::UNKNOWN;
}
}
}

static Transfer getColorTransfer( AVFrame& frame ){
switch( frame.color_range ){
case AVCOL_TRC_SMPTE170M : return Transfer::REC709; //TODO: Correct?
case AVCOL_TRC_BT709 : return Transfer::REC709;
case AVCOL_TRC_LINEAR : return Transfer::LINEAR;
case AVCOL_TRC_IEC61966_2_1: return Transfer::SRGB;
default: {
//TODO: warning
return Transfer::UNKNOWN;
}
}
}

ImageEx VideoFrame::toImageEx(){
//TODO: Detect actual color space
ImageEx out( { getColorSpace( *frame ), getColorTransfer( *frame ) } );

//TODO: We do not get the YUV signal range with this. H264 does support full data range!
ImageEx out( { getColorSpace( context.colorspace ), getColorTransfer( context.color_trc ) } );
for( auto& p : planes )
out.addPlane( std::move( p ) );

return out;
}

12 changes: 2 additions & 10 deletions src/video/VideoFrame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,24 @@
#ifndef VIDEO_FRAME_HPP
#define VIDEO_FRAME_HPP

#include <vector>

struct AVFrame;
struct AVCodecContext;

namespace Overmix{

class Plane;
class ImageEx;

class VideoFrame{
private:
AVFrame *frame;
std::vector<Plane> planes;

unsigned depth{ 8 };
bool planar{ true };
bool rgb = false;
AVCodecContext& context;

public:
VideoFrame( AVCodecContext &context );
VideoFrame( const VideoFrame& ) = delete;
VideoFrame( VideoFrame&& ) = default;
VideoFrame( VideoFrame&& );
~VideoFrame();

void prepare_planes();
ImageEx toImageEx();

operator AVFrame*(){ return frame; }
Expand Down
40 changes: 21 additions & 19 deletions src/video/VideoStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class VideoFile{
};

VideoStream::VideoStream( QString filepath ){
av_register_all();
// av_register_all();
if( avformat_open_input( &format_context
, filepath.toLocal8Bit().constData(), nullptr, nullptr ) < 0 ){
throw std::runtime_error( "Couldn't open video file, either missing, unsupported or corrupted\n" );
Expand All @@ -83,21 +83,23 @@ VideoStream::VideoStream( QString filepath ){
throw std::runtime_error( "Couldn't find stream\n" );

//Find the first video stream (and be happy)
for( unsigned i=0; i<format_context->nb_streams; i++ ){
if( format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ){
stream_index = i;
codec_context = format_context->streams[i]->codec;
break;
}
}

if( !codec_context )
throw std::runtime_error( "Couldn't find a video stream!\n" );
AVCodec *codec = nullptr;
stream_index = av_find_best_stream( format_context, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0 );
if( stream_index < 0 )
std::runtime_error( "Could not find video stream" );

AVCodec *codec = avcodec_find_decoder( codec_context->codec_id );
auto st = format_context->streams[stream_index];
codec = avcodec_find_decoder(st->codecpar->codec_id);
if( !codec )
throw std::runtime_error( "Does not support this video codec :\\\n" );

codec_context = avcodec_alloc_context3( codec );
if( !codec_context )
throw std::runtime_error( "Could not allocate context!\n" );
//TODO: free it again

avcodec_parameters_to_context( codec_context, format_context->streams[stream_index]->codecpar );

if( avcodec_open2( codec_context, codec, nullptr ) < 0 )
throw std::runtime_error( "Couldn't open codec\n" );
}
Expand All @@ -120,7 +122,7 @@ struct StreamPacket{
StreamPacket()
{ av_init_packet( &packet ); }
~StreamPacket()
{ av_free_packet( &packet ); }
{ av_packet_unref( &packet ); }

operator AVPacket*(){ return &packet; }
};
Expand All @@ -141,13 +143,13 @@ VideoFrame VideoStream::getFrame(){
}

if( packet.packet.stream_index == stream_index || eof ){
int frame_done;
if( avcodec_decode_video2( codec_context, frame, &frame_done, packet ) < 0 )
throw std::runtime_error( "Error while decoding frame" );
if( frame_done ){
frame.prepare_planes();
avcodec_send_packet( codec_context, packet );
if( avcodec_receive_frame( codec_context, frame ) >= 0)
// int frame_done;
// if( avcodec_decode_video2( codec_context, frame, &frame_done, packet ) < 0 )
// throw std::runtime_error( "Error while decoding frame" );
// if( frame_done )
return std::move( frame );
}
}
}

Expand Down

0 comments on commit 22dd81e

Please sign in to comment.