Commit c38a4b7d authored by Denis Charmet's avatar Denis Charmet

Add a simple raw hevc demux

parent b1c0b336
......@@ -162,6 +162,9 @@ demux_LTLIBRARIES += libes_plugin.la
libh264_plugin_la_SOURCES = demux/mpeg/h264.c
demux_LTLIBRARIES += libh264_plugin.la
libhevc_plugin_la_SOURCES = demux/mpeg/hevc.c
demux_LTLIBRARIES += libhevc_plugin.la
libmkv_plugin_la_SOURCES = \
demux/mkv/util.hpp demux/mkv/util.cpp \
demux/mkv/virtual_segment.hpp demux/mkv/virtual_segment.cpp \
......
/*****************************************************************************
* hevc.c : HEVC Video demuxer
*****************************************************************************
* Copyright (C) 2014 VLC authors and VideoLAN
* $Id$
*
* Authors: Denis Charmet <typx@videolan.org>
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include <vlc_codec.h>
#include <vlc_bits.h>
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close( vlc_object_t * );
#define FPS_TEXT N_("Frames per Second")
#define FPS_LONGTEXT N_("Desired frame rate for the stream.")
vlc_module_begin ()
set_shortname( "HEVC")
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_DEMUX )
set_description( N_("HEVC/H.265 video demuxer" ) )
set_capability( "demux", 0 )
add_float( "hevc-force-fps", 0.0, FPS_TEXT, FPS_LONGTEXT, true )
set_callbacks( Open, Close )
add_shortcut( "hevc" )
vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux( demux_t * );
static int Control( demux_t *, int, va_list );
static int32_t getFPS( demux_t *, block_t * );
struct demux_sys_t
{
mtime_t i_dts;
es_out_id_t *p_es;
float f_force_fps;
float f_fps;
decoder_t *p_packetizer;
};
#define HEVC_BLOCK_SIZE 2048
/*****************************************************************************
* Open: initializes demux structures
*****************************************************************************/
static int Open( vlc_object_t * p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys;
const uint8_t *p_peek;
es_format_t fmt;
if( stream_Peek( p_demux->s, &p_peek, 5 ) < 5 ) return VLC_EGENERIC;
if( p_peek[0] != 0x00 || p_peek[1] != 0x00 ||
p_peek[2] != 0x00 || p_peek[3] != 0x01 ||
(p_peek[4]&0xFE) != 0x40 ) /* VPS & forbidden zero bit*/
{
if( !p_demux->b_force )
{
msg_Warn( p_demux, "hevc module discarded (no startcode)" );
return VLC_EGENERIC;
}
msg_Err( p_demux, "this doesn't look like a HEVC ES stream, "
"continuing anyway" );
}
p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
if( !p_demux->p_sys )
return VLC_ENOMEM;
p_sys->p_es = NULL;
p_sys->i_dts = 0;
p_sys->f_force_fps = var_CreateGetFloat( p_demux, "hevc-force-fps" );
if( p_sys->f_force_fps != 0.0f )
{
p_sys->f_fps = ( p_sys->f_force_fps < 0.001f )? 0.001f:
p_sys->f_force_fps;
msg_Dbg( p_demux, "using %.2f fps", p_sys->f_fps );
}
else
p_sys->f_fps = 0.0f;
/* Load the hevc packetizer */
es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_HEVC );
p_sys->p_packetizer = demux_PacketizerNew( p_demux, &fmt, "hevc" );
if( !p_sys->p_packetizer )
{
free( p_sys );
return VLC_EGENERIC;
}
p_sys->p_packetizer->fmt_out.b_packetized = true;
p_sys->p_es = es_out_Add( p_demux->out, &p_sys->p_packetizer->fmt_out);
if( !p_sys->p_es )
{
demux_PacketizerDestroy( p_sys->p_packetizer );
free( p_sys );
return VLC_ENOMEM;
}
p_demux->pf_demux = Demux;
p_demux->pf_control= Control;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: frees unused data
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
demux_t *p_demux = (demux_t*)p_this;
demux_sys_t *p_sys = p_demux->p_sys;
demux_PacketizerDestroy( p_sys->p_packetizer );
free( p_sys );
}
/*****************************************************************************
* Demux: reads and demuxes data packets
*****************************************************************************
* Returns -1 in case of error, 0 in case of EOF, 1 otherwise
*****************************************************************************/
static int Demux( demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
block_t *p_block_in, *p_block_out;
if( ( p_block_in = stream_Block( p_demux->s, HEVC_BLOCK_SIZE ) ) == NULL )
{
return 0;
}
p_block_in->i_dts = VLC_TS_INVALID;
p_block_in->i_pts = VLC_TS_INVALID;
while( (p_block_out = p_sys->p_packetizer->pf_packetize( p_sys->p_packetizer, &p_block_in )) )
{
while( p_block_out )
{
block_t *p_next = p_block_out->p_next;
p_block_out->p_next = NULL;
p_block_out->i_dts = VLC_TS_0 + p_sys->i_dts;
p_block_out->i_pts = VLC_TS_INVALID;
/*Get fps from vps if available and not already forced*/
if( p_sys->f_fps == 0.0f && ( p_block_out->p_buffer[3] & 0x7E ) == 0x40 )
{
if( getFPS( p_demux, p_block_out) )
{
msg_Err(p_demux,"getFPS failed");
return 0;
}
}
/* Update DTS only on VCL NAL*/
if( ( p_block_out->p_buffer[3]&0x7E ) < 0x40 && p_sys->f_fps )
{
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_dts );
p_sys->i_dts += (int64_t)((double)1000000.0 / p_sys->f_fps);
}
es_out_Send( p_demux->out, p_sys->p_es, p_block_out );
p_block_out = p_next;
}
}
return 1;
}
/*****************************************************************************
* Control:
*****************************************************************************/
static int Control( demux_t *p_demux, int i_query, va_list args )
{
return demux_vaControlHelper( p_demux->s,
0, -1,
0, 1, i_query, args );
}
static void CreateDecodedNAL( uint8_t **pp_ret, int *pi_ret,
const uint8_t *src, int i_src )
{
const uint8_t *end = &src[i_src];
uint8_t *dst = malloc( i_src );
if( !dst )
return;
*pp_ret = dst;
if( dst )
{
while( src < end )
{
if( src < end - 3 && src[0] == 0x00 && src[1] == 0x00 &&
src[2] == 0x03 )
{
*dst++ = 0x00;
*dst++ = 0x00;
src += 3;
continue;
}
*dst++ = *src++;
}
}
*pi_ret = dst - *pp_ret;
}
static void skipProfileTiersLevel( bs_t * bs, int32_t max_sub_layer_minus1 )
{
uint8_t sub_layer_profile_present_flag[8];
uint8_t sub_layer_level_present_flag[8];
/* skipping useless fields of the VPS see https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.265-201304-I!!PDF-E&type=item */
bs_skip( bs, 2 + 1 + 5 + 32 + 1 + 1 + 1 + 1 + 44 + 8 );
for( int32_t i = 0; i < max_sub_layer_minus1; i++ )
{
sub_layer_profile_present_flag[i] = bs_read1( bs );
sub_layer_level_present_flag[i] = bs_read1( bs );
}
if(max_sub_layer_minus1 > 0)
bs_skip( bs, (8 - max_sub_layer_minus1) * 2 );
for( int32_t i = 0; i < max_sub_layer_minus1; i++ )
{
if( sub_layer_profile_present_flag[i] )
bs_skip( bs, 2 + 1 + 5 + 32 + 1 + 1 + 1 + 1 + 44 );
if( sub_layer_level_present_flag[i] )
bs_skip( bs, 8 );
}
}
static uint32_t read_ue( bs_t * bs )
{
int32_t i = 0;
while( bs_read1( bs ) == 0 && bs->p < bs->p_end && i < 32 )
i++;
return (1 << i) - 1 + bs_read( bs, i );
}
static int32_t getFPS( demux_t *p_demux, block_t * p_block )
{
demux_sys_t *p_sys = p_demux->p_sys;
bs_t bs;
uint8_t * p_decoded_nal;
int i_decoded_nal;
if( p_block->i_buffer < 5 )
return -1;
CreateDecodedNAL( &p_decoded_nal, &i_decoded_nal,
p_block->p_buffer+3, p_block->i_buffer-3 );
if( !p_decoded_nal )
return -1;
bs_init( &bs, p_decoded_nal, i_decoded_nal );
bs_skip( &bs, 12 );
int32_t max_sub_layer_minus1 = bs_read( &bs, 3 );
bs_skip( &bs, 17 );
skipProfileTiersLevel( &bs, max_sub_layer_minus1 );
int32_t vps_sub_layer_ordering_info_present_flag = bs_read1( &bs );
int32_t i = vps_sub_layer_ordering_info_present_flag? 0 : max_sub_layer_minus1;
for( ; i <= max_sub_layer_minus1; i++ )
{
read_ue( &bs );
read_ue( &bs );
read_ue( &bs );
}
uint32_t vps_max_layer_id = bs_read( &bs, 6);
uint32_t vps_num_layer_sets_minus1 = read_ue( &bs );
bs_skip( &bs, vps_max_layer_id * vps_num_layer_sets_minus1 );
if( bs_read1( &bs ))
{
uint32_t num_units_in_tick = bs_read( &bs, 32 );
uint32_t time_scale = bs_read( &bs, 32 );
if( num_units_in_tick )
{
p_sys->f_fps = ( (float) time_scale )/( (float) num_units_in_tick );
msg_Dbg(p_demux,"Using framerate %f fps from VPS", p_sys->f_fps);
}
else
{
msg_Err( p_demux, "vps_num_units_in_tick null defaulting to 25 fps");
p_sys->f_fps = 25.0f;
}
}
else
{
msg_Err( p_demux, "No timing info in VPS defaulting to 25 fps");
p_sys->f_fps = 25.0f;
}
free(p_decoded_nal);
return 0;
}
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