Skip to content

Commit

Permalink
Add filter_swresample. (#305)
Browse files Browse the repository at this point in the history
* Add filter_swresample.

Performs channel configuration aware channel count conversion.
Also performs sample format and frequency conversion.
Set this filter as the default channelconvert normalize filter.

* Resolve review comments for filter_swresample.

* Fix applying channel_layout property.

MLT allows specifying channel_layout as a string representing the
channel layout. But avcodec expects channel_layout to be specified
as an integer which represents a channel position bitmap. This change
stops the channel_layout property from being passed to avcodec so that
the value calculated by MLT will be used instead.
  • Loading branch information
bmatherly authored and ddennedy committed Mar 12, 2018
1 parent b748231 commit 7455472
Show file tree
Hide file tree
Showing 18 changed files with 713 additions and 37 deletions.
8 changes: 8 additions & 0 deletions src/framework/mlt.vers
Expand Up @@ -502,3 +502,11 @@ MLT_6.6.0 {
mlt_log_timings_now;
mlt_service_disconnect_all_producers;
} MLT_6.4.0;

MLT_6.8.0 {
global:
mlt_channel_layout_name;
mlt_channel_layout_id;
mlt_channel_layout_channels;
mlt_channel_layout_default;
} MLT_6.6.0;
1 change: 1 addition & 0 deletions src/framework/mlt_consumer.c
Expand Up @@ -719,6 +719,7 @@ mlt_frame mlt_consumer_get_frame( mlt_consumer self )
mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
mlt_properties_set_int( frame_properties, "consumer_tff", mlt_properties_get_int( properties, "top_field_first" ) );
mlt_properties_set( frame_properties, "consumer_color_trc", mlt_properties_get( properties, "color_trc" ) );
mlt_properties_set( frame_properties, "consumer_channel_layout", mlt_properties_get( properties, "channel_layout" ) );
}

// Return the frame
Expand Down
2 changes: 2 additions & 0 deletions src/framework/mlt_consumer.h
Expand Up @@ -43,6 +43,8 @@
* \properties \em drop_max the maximum number of consecutively dropped frames, defaults to 5
* \properties \em frequency the audio sample rate to use in Hertz, defaults to 48000
* \properties \em channels the number of audio channels to use, defaults to 2
* \properties \em channel_layout the layout of the audio channels, defaults to auto.
* other options include: mono, stereo, 5.1, 7.1, etc.
* \properties \em real_time the asynchronous behavior: 1 (default) for asynchronous
* with frame dropping, -1 for asynchronous without frame dropping, 0 to disable (synchronous)
* \properties \em test_card the name of a resource to use as the test card, defaults to
Expand Down
125 changes: 125 additions & 0 deletions src/framework/mlt_frame.c
Expand Up @@ -1228,3 +1228,128 @@ int mlt_image_format_planes( mlt_image_format format, int width, int height, voi

return 0;
}

/** Get the short name for a channel configuration.
*
* You do not need to deallocate the returned string.
* \public \member of mlt_frame_s
* \param cfg a channel configuration enum
* \return a string for the name of the channel configuration
*/

const char * mlt_channel_layout_name( mlt_channel_layout layout )
{
switch ( layout )
{
case mlt_channel_auto: return "auto";
case mlt_channel_independent: return "independent";
case mlt_channel_mono: return "mono";
case mlt_channel_stereo: return "stereo";
case mlt_channel_2p1: return "2.1";
case mlt_channel_3p0: return "3.0";
case mlt_channel_3p0_back: return "3.0(back)";
case mlt_channel_3p1: return "3.1";
case mlt_channel_4p0: return "4.0";
case mlt_channel_4p1: return "4.1";
case mlt_channel_quad_back: return "quad";
case mlt_channel_quad_side: return "quad(side)";
case mlt_channel_5p0: return "5.0(side)";
case mlt_channel_5p1: return "5.1(side)";
case mlt_channel_5p0_back: return "5.0";
case mlt_channel_5p1_back: return "5.1";
case mlt_channel_6p0: return "6.0";
case mlt_channel_6p0_front: return "6.0(front)";
case mlt_channel_hexagonal: return "hexagonal";
case mlt_channel_6p1: return "6.1";
case mlt_channel_6p1_back: return "6.1(back)";
case mlt_channel_6p1_front: return "6.1(front)";
case mlt_channel_7p0: return "7.0";
case mlt_channel_7p0_front: return "7.0(front)";
case mlt_channel_7p1: return "7.1";
case mlt_channel_7p1_wide_side: return "7.1(wide-side)";
case mlt_channel_7p1_wide_back: return "7.1(wide)";
}
return "invalid";
}

/** Get the id of channel configuration from short name.
*
* \public \memberof mlt_frame_s
* \param name the channel configuration short name
* \return a channel configuration
*/

mlt_channel_layout mlt_channel_layout_id( const char * name )
{
if( name )
{
mlt_channel_layout c;
for( c = mlt_channel_auto; c <= mlt_channel_7p1_wide_back; c++ )
{
const char * v = mlt_channel_layout_name( c );
if( !strcmp( v, name ) )
return c;
}
}
return mlt_channel_auto;
}

/** Get the number of channels for a channel configuration.
*
* \public \memberof mlt_frame_s
* \param cfg a channel configuration enum
* \return the number of channels for the channel configuration
*/

int mlt_channel_layout_channels( mlt_channel_layout layout )
{
switch ( layout )
{
case mlt_channel_auto: return 0;
case mlt_channel_independent: return 0;
case mlt_channel_mono: return 1;
case mlt_channel_stereo: return 2;
case mlt_channel_2p1: return 3;
case mlt_channel_3p0: return 3;
case mlt_channel_3p0_back: return 3;
case mlt_channel_4p0: return 4;
case mlt_channel_3p1: return 4;
case mlt_channel_quad_back: return 4;
case mlt_channel_quad_side: return 4;
case mlt_channel_5p0: return 5;
case mlt_channel_5p0_back: return 5;
case mlt_channel_4p1: return 5;
case mlt_channel_5p1: return 6;
case mlt_channel_5p1_back: return 6;
case mlt_channel_6p0: return 6;
case mlt_channel_6p0_front: return 6;
case mlt_channel_hexagonal: return 6;
case mlt_channel_6p1: return 7;
case mlt_channel_6p1_back: return 7;
case mlt_channel_6p1_front: return 7;
case mlt_channel_7p0: return 7;
case mlt_channel_7p0_front: return 7;
case mlt_channel_7p1: return 8;
case mlt_channel_7p1_wide_side: return 8;
case mlt_channel_7p1_wide_back: return 8;
}
return 0;
}

/** Get a default channel configuration for a given number of channels.
*
* \public \memberof mlt_frame_s
* \param channels the number of channels
* \return the default channel configuration
*/

mlt_channel_layout mlt_channel_layout_default( int channels )
{
mlt_channel_layout c;
for( c = mlt_channel_mono; c <= mlt_channel_7p1_wide_back; c++ )
{
if( mlt_channel_layout_channels( c ) == channels )
return c;
}
return mlt_channel_independent;
}
4 changes: 4 additions & 0 deletions src/framework/mlt_frame.h
Expand Up @@ -152,6 +152,10 @@ extern int mlt_audio_format_size( mlt_audio_format format, int samples, int chan
extern void mlt_frame_write_ppm( mlt_frame frame );
extern int mlt_image_format_planes( mlt_image_format format, int width, int height, void* data, unsigned char *planes[4], int strides[4]);
extern mlt_image_format mlt_image_format_id( const char * name );
extern const char * mlt_channel_layout_name( mlt_channel_layout layout );
extern mlt_channel_layout mlt_channel_layout_id( const char * name );
extern int mlt_channel_layout_channels( mlt_channel_layout layout );
extern mlt_channel_layout mlt_channel_layout_default( int channels );

/** This macro scales RGB into the YUV gamut - y is scaled by 219/255 and uv by 224/255. */
#define RGB2YUV_601_SCALED(r, g, b, y, u, v)\
Expand Down
1 change: 1 addition & 0 deletions src/framework/mlt_tractor.c
Expand Up @@ -414,6 +414,7 @@ static int producer_get_audio( mlt_frame self, void **buffer, mlt_audio_format *
mlt_properties properties = MLT_FRAME_PROPERTIES( self );
mlt_frame frame = mlt_frame_pop_audio( self );
mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
mlt_properties_set( frame_properties, "consumer_channel_layout", mlt_properties_get( properties, "consumer_channel_layout" ) );
mlt_properties_set( frame_properties, "producer_consumer_fps", mlt_properties_get( properties, "producer_consumer_fps" ) );
mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
mlt_frame_set_audio( self, *buffer, *format, mlt_audio_format_size( *format, *samples, *channels ), NULL );
Expand Down
2 changes: 1 addition & 1 deletion src/framework/mlt_transition.c
Expand Up @@ -361,7 +361,7 @@ static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *fo
mlt_frame_set_aspect_ratio( b_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) );

mlt_properties_pass_list( b_props, a_props,
"consumer_deinterlace, deinterlace_method, consumer_tff, consumer_color_trc" );
"consumer_deinterlace, deinterlace_method, consumer_tff, consumer_color_trc, consumer_channel_layout" );

return mlt_frame_get_image( b_frame, image, format, width, height, writable );
}
Expand Down
32 changes: 32 additions & 0 deletions src/framework/mlt_types.h
Expand Up @@ -67,6 +67,38 @@ typedef enum
}
mlt_audio_format;

typedef enum
{
mlt_channel_auto = 0, /**< MLT will determine the default configuration based on channel number */
mlt_channel_independent, /**< channels are not related */
mlt_channel_mono,
mlt_channel_stereo,
mlt_channel_2p1,
mlt_channel_3p0,
mlt_channel_3p0_back,
mlt_channel_3p1,
mlt_channel_4p0,
mlt_channel_4p1,
mlt_channel_quad_back,
mlt_channel_quad_side,
mlt_channel_5p0,
mlt_channel_5p1,
mlt_channel_5p0_back,
mlt_channel_5p1_back,
mlt_channel_6p0,
mlt_channel_6p0_front,
mlt_channel_hexagonal,
mlt_channel_6p1,
mlt_channel_6p1_back,
mlt_channel_6p1_front,
mlt_channel_7p0,
mlt_channel_7p0_front,
mlt_channel_7p1,
mlt_channel_7p1_wide_side,
mlt_channel_7p1_wide_back,
}
mlt_channel_layout;

/** The time string formats */

typedef enum
Expand Down
8 changes: 7 additions & 1 deletion src/modules/avformat/Makefile
Expand Up @@ -12,7 +12,8 @@ else
TARGET = ../libmltavformat$(LIBSUF)
endif

OBJS = factory.o
OBJS = common.o \
factory.o

ifdef FILTERS
OBJS += filter_avcolour_space.o \
Expand Down Expand Up @@ -41,6 +42,11 @@ CFLAGS += -DAVFILTER
OBJS += filter_avfilter.o
endif

ifdef SWRESAMPLE
OBJS += filter_swresample.o
CFLAGS += -DSWRESAMPLE
endif

SRCS := $(OBJS:.o=.c)

all: $(TARGET)
Expand Down
132 changes: 132 additions & 0 deletions src/modules/avformat/common.c
@@ -0,0 +1,132 @@
/*
* common.h
* Copyright (C) 2018 Meltytech, LLC
* Author: Brian Matherly <code@brianmatherly.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "common.h"

#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>

int mlt_to_av_sample_format( mlt_audio_format format )
{
switch( format )
{
case mlt_audio_none:
return AV_SAMPLE_FMT_NONE;
case mlt_audio_s16:
return AV_SAMPLE_FMT_S16;
case mlt_audio_s32:
return AV_SAMPLE_FMT_S32P;
case mlt_audio_float:
return AV_SAMPLE_FMT_FLTP;
case mlt_audio_s32le:
return AV_SAMPLE_FMT_S32;
case mlt_audio_f32le:
return AV_SAMPLE_FMT_FLT;
case mlt_audio_u8:
return AV_SAMPLE_FMT_U8;
}
mlt_log_error( NULL, "[avformat] Unknown audio format: %d\n", format );
return AV_SAMPLE_FMT_NONE;
}

int64_t mlt_to_av_channel_layout( mlt_channel_layout layout )
{
switch( layout )
{
case mlt_channel_auto:
case mlt_channel_independent:
mlt_log_error( NULL, "[avformat] No matching channel layout: %s\n", mlt_channel_layout_name( layout ) );
return 0;
case mlt_channel_mono: return AV_CH_LAYOUT_MONO;
case mlt_channel_stereo: return AV_CH_LAYOUT_STEREO;
case mlt_channel_2p1: return AV_CH_LAYOUT_2POINT1;
case mlt_channel_3p0: return AV_CH_LAYOUT_SURROUND;
case mlt_channel_3p0_back: return AV_CH_LAYOUT_2_1;
case mlt_channel_3p1: return AV_CH_LAYOUT_3POINT1;
case mlt_channel_4p0: return AV_CH_LAYOUT_4POINT0;
case mlt_channel_quad_back: return AV_CH_LAYOUT_QUAD;
case mlt_channel_quad_side: return AV_CH_LAYOUT_2_2;
case mlt_channel_5p0: return AV_CH_LAYOUT_5POINT0;
case mlt_channel_5p0_back: return AV_CH_LAYOUT_5POINT0_BACK;
case mlt_channel_4p1: return AV_CH_LAYOUT_4POINT1;
case mlt_channel_5p1: return AV_CH_LAYOUT_5POINT1;
case mlt_channel_5p1_back: return AV_CH_LAYOUT_5POINT1_BACK;
case mlt_channel_6p0: return AV_CH_LAYOUT_6POINT0;
case mlt_channel_6p0_front: return AV_CH_LAYOUT_6POINT0_FRONT;
case mlt_channel_hexagonal: return AV_CH_LAYOUT_HEXAGONAL;
case mlt_channel_6p1: return AV_CH_LAYOUT_6POINT1;
case mlt_channel_6p1_back: return AV_CH_LAYOUT_6POINT1_BACK;
case mlt_channel_6p1_front: return AV_CH_LAYOUT_6POINT1_FRONT;
case mlt_channel_7p0: return AV_CH_LAYOUT_7POINT0;
case mlt_channel_7p0_front: return AV_CH_LAYOUT_7POINT0_FRONT;
case mlt_channel_7p1: return AV_CH_LAYOUT_7POINT1;
case mlt_channel_7p1_wide_side: return AV_CH_LAYOUT_7POINT1_WIDE;
case mlt_channel_7p1_wide_back: return AV_CH_LAYOUT_7POINT1_WIDE_BACK;
}
mlt_log_error( NULL, "[avformat] Unknown channel configuration: %d\n", layout );
return 0;
}

mlt_channel_layout av_channel_layout_to_mlt( int64_t layout )
{
switch( layout )
{
case 0: return mlt_channel_independent;
case AV_CH_LAYOUT_MONO: return mlt_channel_mono;
case AV_CH_LAYOUT_STEREO: return mlt_channel_stereo;
case AV_CH_LAYOUT_STEREO_DOWNMIX: return mlt_channel_stereo;
case AV_CH_LAYOUT_2POINT1: return mlt_channel_2p1;
case AV_CH_LAYOUT_SURROUND: return mlt_channel_3p0;
case AV_CH_LAYOUT_2_1: return mlt_channel_3p0_back;
case AV_CH_LAYOUT_3POINT1: return mlt_channel_3p1;
case AV_CH_LAYOUT_4POINT0: return mlt_channel_4p0;
case AV_CH_LAYOUT_QUAD: return mlt_channel_quad_back;
case AV_CH_LAYOUT_2_2: return mlt_channel_quad_side;
case AV_CH_LAYOUT_5POINT0: return mlt_channel_5p0;
case AV_CH_LAYOUT_5POINT0_BACK: return mlt_channel_5p0_back;
case AV_CH_LAYOUT_4POINT1: return mlt_channel_4p1;
case AV_CH_LAYOUT_5POINT1: return mlt_channel_5p1;
case AV_CH_LAYOUT_5POINT1_BACK: return mlt_channel_5p1_back;
case AV_CH_LAYOUT_6POINT0: return mlt_channel_6p0;
case AV_CH_LAYOUT_6POINT0_FRONT: return mlt_channel_6p0_front;
case AV_CH_LAYOUT_HEXAGONAL: return mlt_channel_hexagonal;
case AV_CH_LAYOUT_6POINT1: return mlt_channel_6p1;
case AV_CH_LAYOUT_6POINT1_BACK: return mlt_channel_6p1_back;
case AV_CH_LAYOUT_6POINT1_FRONT: return mlt_channel_6p1_front;
case AV_CH_LAYOUT_7POINT0: return mlt_channel_7p0;
case AV_CH_LAYOUT_7POINT0_FRONT: return mlt_channel_7p0_front;
case AV_CH_LAYOUT_7POINT1: return mlt_channel_7p1;
case AV_CH_LAYOUT_7POINT1_WIDE: return mlt_channel_7p1_wide_side;
case AV_CH_LAYOUT_7POINT1_WIDE_BACK: return mlt_channel_7p1_wide_back;
}
mlt_log_error( NULL, "[avformat] Unknown channel layout: %lu\n", (unsigned long)layout );
return mlt_channel_independent;
}

mlt_channel_layout get_channel_layout_or_default( const char* name, int channels )
{
mlt_channel_layout layout = mlt_channel_layout_id( name );
if( layout == mlt_channel_auto ||
( layout != mlt_channel_independent && mlt_channel_layout_channels( layout ) != channels ) )
{
layout = mlt_channel_layout_default( channels );
}
return layout;
}

0 comments on commit 7455472

Please sign in to comment.