Commit e89ca614 authored by François Cartegnie's avatar François Cartegnie 🤞

mux: ts: add support for JP2K (#16981)

parent b12b10d0
......@@ -20,6 +20,66 @@
#ifndef VLC_JPEG2000_H
#define VLC_JPEG2000_H
#define J2K_BOX_JP2C VLC_FOURCC('j','p','2','c')
enum j2k_profiles_e
{
J2K_PROFILE_SD = 0,
J2K_PROFILE_HD,
J2K_PROFILE_3G,
J2K_PROFILE_S3D_HD,
J2K_PROFILE_S3D_3G,
};
static inline bool j2k_is_valid_framerate( unsigned num, unsigned den )
{
const struct
{
const unsigned num;
const unsigned den;
} numdens[] = { /* table 2-99 */
{ 24000, 1001 },
{ 24, 1 },
{ 25, 1 },
{ 30000, 1001 },
{ 30, 1 },
{ 50, 1 },
{ 60000, 1001 },
{ 60, 1 },
};
for( size_t i=0; i<ARRAY_SIZE(numdens); i++ )
if( numdens[i].den == den && numdens[i].num == num )
return true;
return false;
}
static inline enum j2k_profiles_e j2k_get_profile( unsigned w, unsigned h,
unsigned num, unsigned den, bool p )
{
const uint64_t s = w * h;
const uint64_t f = num / den;
if( s <= 720*576 && f < 50 )
return J2K_PROFILE_SD; /* VSF_TR-01_2013-04-15 */
else if( s <= 1280*720 && f < 60 && p )
return J2K_PROFILE_HD;
else if( s <= 1920*1080 && f < 60 && !p )
return J2K_PROFILE_HD;
else
return J2K_PROFILE_3G;
}
static const struct
{
const uint16_t min;
const uint16_t max;
} j2k_profiles_rates[] = {
[J2K_PROFILE_SD] = { 25, 200 },
[J2K_PROFILE_HD] = { 75, 200 },
[J2K_PROFILE_3G] = { 100, 400 },
[J2K_PROFILE_S3D_HD] = { 150, 200 },
[J2K_PROFILE_S3D_3G] = { 200, 400 },
};
enum j2k_color_specs_e
{
J2K_COLOR_SPEC_UNKNOWN = 0,
......
......@@ -37,6 +37,7 @@ libmux_ts_plugin_la_SOURCES = \
mux/mpeg/streams.h \
mux/mpeg/tables.c mux/mpeg/tables.h \
mux/mpeg/tsutil.c mux/mpeg/tsutil.h \
codec/jpeg2000.h \
mux/mpeg/ts.c mux/mpeg/bits.h mux/mpeg/dvbpsi_compat.h
libmux_ts_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(DVBPSI_CFLAGS)
libmux_ts_plugin_la_LIBADD = $(DVBPSI_LIBS)
......
......@@ -42,6 +42,8 @@
#include "bits.h"
#include "pes.h"
#include "../../codec/jpeg2000.h"
#include <assert.h>
block_t *WritePSISection( dvbpsi_psi_section_t* p_section )
......@@ -504,6 +506,34 @@ void BuildPMT( dvbpsi_t *p_dvbpsi, vlc_object_t *p_object,
/* 0xa0 is private */
dvbpsi_pmt_es_descriptor_add( p_es, 0xa0, i_extra + 10, data );
}
else if( p_stream->fmt->i_codec == VLC_CODEC_JPEG2000 )
{
uint8_t *p_data = calloc( 1, 24 + p_stream->fmt->i_extra );
if( p_data )
{
const int profile = j2k_get_profile( p_stream->fmt->video.i_visible_width,
p_stream->fmt->video.i_visible_height,
p_stream->fmt->video.i_frame_rate,
p_stream->fmt->video.i_frame_rate_base, true );
p_data[0] = 0x01;
if( profile < J2K_PROFILE_HD )
p_data[1] = 0x01; /* 0x0101 */
else if( profile < J2K_PROFILE_3G )
p_data[1] = 0x02; /* 0x0102 */
else
p_data[1] = 0x04; /* 0x0104 */
SetDWBE( &p_data[2], p_stream->fmt->video.i_visible_width );
SetDWBE( &p_data[6], p_stream->fmt->video.i_visible_height );
SetWBE( &p_data[18], p_stream->fmt->video.i_frame_rate_base );
SetWBE( &p_data[20], p_stream->fmt->video.i_frame_rate );
p_data[21] = j2k_get_color_spec( p_stream->fmt->video.primaries,
p_stream->fmt->video.transfer,
p_stream->fmt->video.space );
memcpy( &p_data[24], p_stream->fmt->p_extra, p_stream->fmt->i_extra );
dvbpsi_pmt_es_descriptor_add( p_es, 0x32, 24 + p_stream->fmt->i_extra, p_data );
free(p_data);
}
}
else if( p_stream->fmt->i_codec == VLC_CODEC_DIRAC )
{
/* Dirac registration descriptor */
......@@ -741,6 +771,13 @@ int FillPMTESParams( ts_mux_standard standard, const es_format_t *fmt,
pes->i_stream_id = (PES_EXTENDED_STREAM_ID << 8) | 0x60;
ts->i_stream_type = 0xd1;
break;
case VLC_CODEC_JPEG2000:
if( !j2k_is_valid_framerate( fmt->video.i_frame_rate,
fmt->video.i_frame_rate_base ) )
return VLC_EGENERIC;
ts->i_stream_type = 0x21;
pes->i_stream_id = 0xbd;
break;
/* AUDIO */
......
......@@ -62,6 +62,8 @@
#include "tables.h"
#include "../../codec/jpeg2000.h"
/*
* TODO:
* - check PCR frequency requirement
......@@ -1111,6 +1113,84 @@ static void SetBlockDuration( sout_input_t *p_input, block_t *p_data )
}
}
static block_t *Encap_J2K( block_t *p_data, const es_format_t *p_fmt )
{
size_t i_offset = 0;
uint32_t i_box = 0;
while( p_data->i_buffer > 8 && p_data->i_buffer - i_offset > 8 )
{
const uint32_t i_size = GetDWBE( &p_data->p_buffer[i_offset] );
i_box = VLC_FOURCC( p_data->p_buffer[i_offset + 4],
p_data->p_buffer[i_offset + 5],
p_data->p_buffer[i_offset + 6],
p_data->p_buffer[i_offset + 7] );
if( p_data->i_buffer - i_offset < i_size || i_size < 8 )
{
i_box = 0;
break;
}
else if( i_box == J2K_BOX_JP2C )
{
break;
}
i_offset += i_size;
}
if( i_box != J2K_BOX_JP2C )
{
block_Release( p_data );
return NULL;
}
if( i_offset < 38 )
{
block_t *p_realloc = block_Realloc( p_data, 38 - i_offset, p_data->i_buffer );
if( unlikely(!p_realloc) )
{
block_Release( p_data );
return NULL;
}
p_data = p_realloc;
}
else
{
p_data->p_buffer += (i_offset - 38);
p_data->i_buffer -= (i_offset - 38);
}
const int profile = j2k_get_profile( p_fmt->video.i_visible_width,
p_fmt->video.i_visible_height,
p_fmt->video.i_frame_rate,
p_fmt->video.i_frame_rate_base, true );
memcpy( p_data->p_buffer, "elsmfrat", 8 );
SetWBE( &p_data->p_buffer[8], p_fmt->video.i_frame_rate_base );
SetWBE( &p_data->p_buffer[10], p_fmt->video.i_frame_rate );
memcpy( &p_data->p_buffer[12], "brat", 4 );
unsigned min = j2k_profiles_rates[profile].min * 1000000;
unsigned max = j2k_profiles_rates[profile].max * 1000000;
SetDWBE(&p_data->p_buffer[16], max );
SetDWBE(&p_data->p_buffer[20], min );
memcpy( &p_data->p_buffer[24], "tcod", 4 );
const unsigned s = p_data->i_pts / CLOCK_FREQ;
const unsigned m = s / 60;
const unsigned h = m / 60;
const uint64_t l = p_fmt->video.i_frame_rate_base * CLOCK_FREQ /
p_fmt->video.i_frame_rate;
const unsigned f = (p_data->i_pts % CLOCK_FREQ) / l;
p_data->p_buffer[28] = h;
p_data->p_buffer[29] = m % 60;
p_data->p_buffer[30] = s % 60;
p_data->p_buffer[31] = f;
memcpy( &p_data->p_buffer[32], "bcol", 4 );
p_data->p_buffer[36] = j2k_get_color_spec( p_fmt->video.primaries,
p_fmt->video.transfer,
p_fmt->video.space );
p_data->p_buffer[37] = 0;
return p_data;
}
/* returns true if needs more data */
static bool MuxStreams(sout_mux_t *p_mux )
{
......@@ -1303,6 +1383,17 @@ static bool MuxStreams(sout_mux_t *p_mux )
b_data_alignment = 1;
break;
}
else if( p_input->fmt.i_cat == VIDEO_ES )
{
if( p_input->fmt.i_codec == VLC_CODEC_JPEG2000 )
{
if( p_data->i_flags & BLOCK_FLAG_INTERLACED_MASK )
msg_Warn( p_mux, "Unsupported interlaced J2K content. Expect broken result");
p_data = Encap_J2K( p_data, &p_input->fmt );
if( !p_data )
return false;
}
}
else if( p_data->i_length < 0 || p_data->i_length > 2000000 )
{
/* FIXME choose a better value, but anyway we
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment