Commit ed686b09 authored by Denis Charmet's avatar Denis Charmet

Allow hevc muxing in mp4

parent 52a59eb4
......@@ -162,7 +162,7 @@ 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
libhevc_plugin_la_SOURCES = demux/mpeg/hevc.c demux/mpeg/mpeg_parser_helpers.h
demux_LTLIBRARIES += libhevc_plugin.la
libmkv_plugin_la_SOURCES = \
......
......@@ -35,6 +35,7 @@
#include <vlc_codec.h>
#include <vlc_bits.h>
#include "mpeg_parser_helpers.h"
/*****************************************************************************
* Module descriptor
......@@ -108,7 +109,7 @@ static int Open( vlc_object_t * p_this )
return VLC_ENOMEM;
p_sys->p_es = NULL;
p_sys->i_dts = 0;
p_sys->i_dts = VLC_TS_0;
p_sys->f_force_fps = var_CreateGetFloat( p_demux, "hevc-force-fps" );
if( p_sys->f_force_fps != 0.0f )
{
......@@ -181,11 +182,13 @@ static int Demux( demux_t *p_demux)
p_block_out->p_next = NULL;
p_block_out->i_dts = VLC_TS_0 + p_sys->i_dts;
p_block_out->i_dts = p_sys->i_dts;
p_block_out->i_pts = VLC_TS_INVALID;
uint8_t nal_type = p_block_out->p_buffer[4] & 0x7E;
/*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( p_sys->f_fps == 0.0f && nal_type == 0x40 )
{
if( getFPS( p_demux, p_block_out) )
{
......@@ -195,7 +198,7 @@ static int Demux( demux_t *p_demux)
}
/* Update DTS only on VCL NAL*/
if( ( p_block_out->p_buffer[3]&0x7E ) < 0x40 && p_sys->f_fps )
if( nal_type < 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);
......@@ -221,70 +224,15 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
}
static void CreateDecodedNAL( uint8_t **pp_ret, int *pi_ret,
static uint8_t * CreateDecodedNAL( 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 );
*pi_ret = nal_decode( src, dst, i_src );
return dst;
}
static int32_t getFPS( demux_t *p_demux, block_t * p_block )
......@@ -298,8 +246,8 @@ static int32_t getFPS( demux_t *p_demux, block_t * p_block )
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 );
p_decoded_nal = CreateDecodedNAL(&i_decoded_nal,
p_block->p_buffer+4, p_block->i_buffer-4);
if( !p_decoded_nal )
return -1;
......@@ -309,7 +257,7 @@ static int32_t getFPS( demux_t *p_demux, block_t * p_block )
int32_t max_sub_layer_minus1 = bs_read( &bs, 3 );
bs_skip( &bs, 17 );
skipProfileTiersLevel( &bs, max_sub_layer_minus1 );
hevc_skip_profile_tiers_level( &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;
......
/*****************************************************************************
* packetizer_helper.h: Packetizer helpers
*****************************************************************************
* 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.
*****************************************************************************/
#ifndef MPEG_PARSER_HELPERS_H
#define MPEG_PARSER_HELPERS_H
#include <stdint.h>
#include <vlc_bits.h>
static inline void hevc_skip_profile_tiers_level( 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 inline 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 inline size_t nal_decode(uint8_t * p_src, uint8_t * p_dst, size_t i_size)
{
size_t j = 0;
for (size_t i = 0; i < i_size; i++) {
if (i < i_size - 3 &&
p_src[i] == 0 && p_src[i+1] == 0 && p_src[i+2] == 3) {
p_dst[j++] = 0;
p_dst[j++] = 0;
i += 2;
continue;
}
p_dst[j++] = p_src[i];
}
return j;
}
#endif /*MPEG_PARSER_HELPERS_H*/
SOURCES_mux_dummy = dummy.c
SOURCES_mux_avi = avi.c
SOURCES_mux_ogg = ogg.c
SOURCES_mux_mp4 = mp4.c
SOURCES_mux_mp4 = mp4.c ../demux/mpeg/mpeg_parser_helpers.h
SOURCES_mux_asf = asf.c ../demux/asf/libasf_guid.h
SOURCES_mux_wav = wav.c
SOURCES_mux_mpjpeg = mpjpeg.c
......
......@@ -40,6 +40,8 @@
#include <vlc_iso_lang.h>
#include <vlc_meta.h>
#include "../demux/mpeg/mpeg_parser_helpers.h"
/*****************************************************************************
* Module descriptor
*****************************************************************************/
......@@ -162,7 +164,7 @@ static void box_send(sout_mux_t *p_mux, bo_t *box);
static bo_t *GetMoovBox(sout_mux_t *p_mux);
static block_t *ConvertSUBT(block_t *);
static block_t *ConvertAVC1(block_t *);
static block_t *ConvertFromAnnexB(block_t *);
static const char avc1_start_code[4] = { 0, 0, 0, 1 };
......@@ -448,8 +450,9 @@ static int Mux(sout_mux_t *p_mux)
block_t *p_data;
do {
p_data = block_FifoGet(p_input->p_fifo);
if (p_stream->fmt.i_codec == VLC_CODEC_H264)
p_data = ConvertAVC1(p_data);
if (p_stream->fmt.i_codec == VLC_CODEC_H264 ||
p_stream->fmt.i_codec == VLC_CODEC_HEVC)
p_data = ConvertFromAnnexB(p_data);
else if (p_stream->fmt.i_codec == VLC_CODEC_SUBT)
p_data = ConvertSUBT(p_data);
} while (!p_data);
......@@ -580,7 +583,7 @@ static block_t *ConvertSUBT(block_t *p_block)
return p_block;
}
static block_t *ConvertAVC1(block_t *p_block)
static block_t *ConvertFromAnnexB(block_t *p_block)
{
uint8_t *last = p_block->p_buffer; /* Assume it starts with 0x00000001 */
uint8_t *dat = &p_block->p_buffer[4];
......@@ -759,13 +762,233 @@ static bo_t *GetD263Tag(void)
return d263;
}
static void hevcParseVPS(uint8_t * p_buffer, size_t i_buffer, uint8_t *general,
uint8_t * numTemporalLayer, bool * temporalIdNested)
{
const size_t i_decoded_nal_size = 512;
uint8_t p_dec_nal[i_decoded_nal_size];
size_t i_size = (i_buffer < i_decoded_nal_size)?i_buffer:i_decoded_nal_size;
nal_decode(p_buffer, p_dec_nal, i_size);
/* first two bytes are the NAL header, 3rd and 4th are:
vps_video_parameter_set_id(4)
vps_reserved_3_2bis(2)
vps_max_layers_minus1(6)
vps_max_sub_layers_minus1(3)
vps_temporal_id_nesting_flags
*/
*numTemporalLayer = ((p_dec_nal[3] & 0x0E) >> 1) + 1;
*temporalIdNested = (bool)(p_dec_nal[3] & 0x01);
/* 5th & 6th are reserved 0xffff */
/* copy the first 12 bytes of profile tier */
memcpy(general, &p_dec_nal[6], 12);
}
static void hevcParseSPS(uint8_t * p_buffer, size_t i_buffer, uint8_t * chroma_idc,
uint8_t *bit_depth_luma_minus8, uint8_t *bit_depth_chroma_minus8)
{
const size_t i_decoded_nal_size = 512;
uint8_t p_dec_nal[i_decoded_nal_size];
size_t i_size = (i_buffer < i_decoded_nal_size)?i_buffer-2:i_decoded_nal_size;
nal_decode(p_buffer+2, p_dec_nal, i_size);
bs_t bs;
bs_init(&bs, p_dec_nal, i_size);
/* skip vps id */
bs_skip(&bs, 4);
uint32_t sps_max_sublayer_minus1 = bs_read(&bs, 3);
/* skip nesting flag */
bs_skip(&bs, 1);
hevc_skip_profile_tiers_level(&bs, sps_max_sublayer_minus1);
/* skip sps id */
(void) read_ue( &bs );
*chroma_idc = read_ue(&bs);
if (*chroma_idc == 3)
bs_skip(&bs, 1);
/* skip width and heigh */
(void) read_ue( &bs );
(void) read_ue( &bs );
uint32_t conformance_window_flag = bs_read1(&bs);
if (conformance_window_flag) {
/* skip offsets*/
(void) read_ue(&bs);
(void) read_ue(&bs);
(void) read_ue(&bs);
(void) read_ue(&bs);
}
*bit_depth_luma_minus8 = read_ue(&bs);
*bit_depth_chroma_minus8 = read_ue(&bs);
}
static bo_t *GetHvcCTag(mp4_stream_t *p_stream)
{
/* Generate hvcC box matching iso/iec 14496-15 3rd edition */
bo_t *hvcC = box_new("hvcC");
if(!p_stream->fmt.i_extra)
return hvcC;
struct nal {
size_t i_buffer;
uint8_t * p_buffer;
};
/* According to the specification HEVC stream can have
* 16 vps id and an "unlimited" number of sps and pps id using ue(v) id*/
struct nal p_vps[16], *p_sps = NULL, *p_pps = NULL, *p_sei = NULL,
*p_nal = NULL;
size_t i_vps = 0, i_sps = 0, i_pps = 0, i_sei = 0;
uint8_t i_num_arrays = 0;
uint8_t * p_buffer = p_stream->fmt.p_extra;
size_t i_buffer = p_stream->fmt.i_extra;
uint8_t general_configuration[12] = {0};
uint8_t i_numTemporalLayer;
uint8_t i_chroma_idc = 1;
uint8_t i_bit_depth_luma_minus8 = 0;
uint8_t i_bit_depth_chroma_minus8 = 0;
bool b_temporalIdNested;
uint32_t cmp = 0xFFFFFFFF;
while (i_buffer) {
/* look for start code 0X0000001 */
while (i_buffer) {
cmp = (cmp << 8) | *p_buffer;
if((cmp ^ UINT32_C(0x100)) <= UINT32_C(0xFF))
break;
p_buffer++;
i_buffer--;
}
if (p_nal)
p_nal->i_buffer = p_buffer - p_nal->p_buffer - ((i_buffer)?3:0);
switch (*p_buffer & 0x72) {
/* VPS */
case 0x40:
p_nal = &p_vps[i_vps++];
p_nal->p_buffer = p_buffer;
/* Only keep the general profile from the first VPS
* if there are several (this shouldn't happen so soon) */
if (i_vps == 1) {
hevcParseVPS(p_buffer, i_buffer, general_configuration,
&i_numTemporalLayer, &b_temporalIdNested);
i_num_arrays++;
}
break;
/* SPS */
case 0x42: {
struct nal * p_tmp = realloc(p_sps, sizeof(struct nal) * (i_sps + 1));
if (!p_tmp)
break;
p_sps = p_tmp;
p_nal = &p_sps[i_sps++];
p_nal->p_buffer = p_buffer;
if (i_sps == 1 && i_buffer > 15) {
/* Get Chroma_idc and bitdepths */
hevcParseSPS(p_buffer, i_buffer, &i_chroma_idc,
&i_bit_depth_luma_minus8, &i_bit_depth_chroma_minus8);
i_num_arrays++;
}
break;
}
/* PPS */
case 0x44: {
struct nal * p_tmp = realloc(p_pps, sizeof(struct nal) * (i_pps + 1));
if (!p_tmp)
break;
p_pps = p_tmp;
p_nal = &p_pps[i_pps++];
p_nal->p_buffer = p_buffer;
if (i_pps == 1)
i_num_arrays++;
break;
}
/* SEI */
case 0x4E:
case 0x50: {
struct nal * p_tmp = realloc(p_sei, sizeof(struct nal) * (i_sei + 1));
if (!p_tmp)
break;
p_sei = p_tmp;
p_nal = &p_sei[i_sei++];
p_nal->p_buffer = p_buffer;
if(i_sei == 1)
i_num_arrays++;
break;
}
default:
p_nal = NULL;
break;
}
}
bo_add_8(hvcC, 0x01);
bo_add_mem(hvcC, 12, general_configuration);
/* Don't set min spatial segmentation */
bo_add_16be(hvcC, 0xF000);
/* Don't set parallelism type since segmentation isn't set */
bo_add_8(hvcC, 0xFC);
bo_add_8(hvcC, (0xFC | (i_chroma_idc & 0x03)));
bo_add_8(hvcC, (0xF8 | (i_bit_depth_luma_minus8 & 0x07)));
bo_add_8(hvcC, (0xF8 | (i_bit_depth_chroma_minus8 & 0x07)));
/* Don't set framerate */
bo_add_16be(hvcC, 0x0000);
/* Force NAL size of 4 bytes that replace the startcode */
bo_add_8(hvcC, (((i_numTemporalLayer & 0x07) << 3) |
(b_temporalIdNested << 2) | 0x03));
bo_add_8(hvcC, i_num_arrays);
if (i_vps)
{
/* Write VPS without forcing array_completeness */
bo_add_8(hvcC, 32);
bo_add_16be(hvcC, i_vps);
for (size_t i = 0; i < i_vps; i++) {
p_nal = &p_vps[i];
bo_add_16be(hvcC, p_nal->i_buffer);
bo_add_mem(hvcC, p_nal->i_buffer, p_nal->p_buffer);
}
}
if (i_sps) {
/* Write SPS without forcing array_completeness */
bo_add_8(hvcC, 33);
bo_add_16be(hvcC, i_sps);
for (size_t i = 0; i < i_sps; i++) {
p_nal = &p_sps[i];
bo_add_16be(hvcC, p_nal->i_buffer);
bo_add_mem(hvcC, p_nal->i_buffer, p_nal->p_buffer);
}
}
if (p_stream->fmt.i_extra > 0)
bo_add_mem(hvcC, p_stream->fmt.i_extra, p_stream->fmt.p_extra);
if (i_pps) {
/* Write PPS without forcing array_completeness */
bo_add_8(hvcC, 34);
bo_add_16be(hvcC, i_pps);
for (size_t i = 0; i < i_pps; i++) {
p_nal = &p_pps[i];
bo_add_16be(hvcC, p_nal->i_buffer);
bo_add_mem(hvcC, p_nal->i_buffer, p_nal->p_buffer);
}
}
if (i_sei) {
/* Write SEI without forcing array_completeness */
bo_add_8(hvcC, 39);
bo_add_16be(hvcC, i_sei);
for (size_t i = 0; i < i_sei; i++) {
p_nal = &p_sei[i];
bo_add_16be(hvcC, p_nal->i_buffer);
bo_add_mem(hvcC, p_nal->i_buffer, p_nal->p_buffer);
}
}
return hvcC;
}
......
......@@ -124,7 +124,7 @@ static int Open(vlc_object_t *p_this)
packetizer_Init(&p_dec->p_sys->packetizer,
p_hevc_startcode, sizeof(p_hevc_startcode),
NULL, 0, 0,
p_hevc_startcode, 1, 5,
PacketizeReset, PacketizeParse, PacketizeValidate, p_dec);
/* Copy properties */
......@@ -185,7 +185,7 @@ static block_t *PacketizeParse(void *p_private, bool *pb_ts_used, block_t *p_blo
p_block->i_buffer--;
bs_t bs;
bs_init(&bs, p_block->p_buffer+3, p_block->i_buffer-3);
bs_init(&bs, p_block->p_buffer+4, p_block->i_buffer-4);
/* Get NALU type */
uint32_t forbidden_zero_bit = bs_read1(&bs);
......
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