📄 mp4.c
字号:
/***************************************************************************** * mp4.c: mp4/mov muxer ***************************************************************************** * Copyright (C) 2001, 2002, 2003 VideoLAN * $Id: mp4.c 10101 2005-03-02 16:47:31Z robux4 $ * * Authors: Laurent Aimar <fenrir@via.ecp.fr> * Gildas Bazin <gbazin at videolan dot org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#include <stdlib.h>#include <string.h>#include <vlc/vlc.h>#include <vlc/input.h>#include <vlc/sout.h>#ifdef HAVE_TIME_H#include <time.h>#endif#include "iso_lang.h"#include "vlc_meta.h"/***************************************************************************** * Module descriptor *****************************************************************************/#define FASTSTART_TEXT N_("Create \"Fast start\" files")#define FASTSTART_LONGTEXT N_( \ "When this option is turned on, \"Fast start\" files will be created. " \ "(\"Fast start\" files are optimized for download, allowing the user " \ "to start previewing the file while it is downloading).")static int Open ( vlc_object_t * );static void Close ( vlc_object_t * );#define SOUT_CFG_PREFIX "sout-mp4-"vlc_module_begin(); set_description( _("MP4/MOV muxer") ); set_category( CAT_SOUT ); set_subcategory( SUBCAT_SOUT_MUX ); set_shortname( "MP4" ); add_bool( SOUT_CFG_PREFIX "faststart", 1, NULL, FASTSTART_TEXT, FASTSTART_LONGTEXT, VLC_TRUE ); set_capability( "sout mux", 5 ); add_shortcut( "mp4" ); add_shortcut( "mov" ); add_shortcut( "3gp" ); set_callbacks( Open, Close );vlc_module_end();/***************************************************************************** * Exported prototypes *****************************************************************************/static const char *ppsz_sout_options[] = { "faststart", NULL};static int Control( sout_mux_t *, int, va_list );static int AddStream( sout_mux_t *, sout_input_t * );static int DelStream( sout_mux_t *, sout_input_t * );static int Mux ( sout_mux_t * );/***************************************************************************** * Local prototypes *****************************************************************************/typedef struct{ uint64_t i_pos; int i_size; mtime_t i_pts_dts; mtime_t i_length; unsigned int i_flags;} mp4_entry_t;typedef struct{ es_format_t fmt; int i_track_id; /* index */ unsigned int i_entry_count; unsigned int i_entry_max; mp4_entry_t *entry; int64_t i_length_neg; /* stats */ int64_t i_dts_start; int64_t i_duration; /* for later stco fix-up (fast start files) */ uint64_t i_stco_pos; vlc_bool_t b_stco64; /* for h264 */ struct { int i_profile; int i_level; int i_sps; uint8_t *sps; int i_pps; uint8_t *pps; } avc; /* for spu */ int64_t i_last_dts;} mp4_stream_t;struct sout_mux_sys_t{ vlc_bool_t b_mov; vlc_bool_t b_3gp; vlc_bool_t b_64_ext; vlc_bool_t b_fast_start; uint64_t i_mdat_pos; uint64_t i_pos; int64_t i_dts_start; int i_nb_streams; mp4_stream_t **pp_streams;};typedef struct bo_t{ vlc_bool_t b_grow; int i_buffer_size; int i_buffer; uint8_t *p_buffer;} bo_t;static void bo_init ( bo_t *, int , uint8_t *, vlc_bool_t );static void bo_add_8 ( bo_t *, uint8_t );static void bo_add_16be ( bo_t *, uint16_t );static void bo_add_24be ( bo_t *, uint32_t );static void bo_add_32be ( bo_t *, uint32_t );static void bo_add_64be ( bo_t *, uint64_t );static void bo_add_fourcc(bo_t *, char * );static void bo_add_bo ( bo_t *, bo_t * );static void bo_add_mem ( bo_t *, int , uint8_t * );static void bo_add_descr( bo_t *, uint8_t , uint32_t );static void bo_fix_32be ( bo_t *, int , uint32_t );static bo_t *box_new ( char *fcc );static bo_t *box_full_new( char *fcc, uint8_t v, uint32_t f );static void box_fix ( bo_t *box );static void box_free ( bo_t *box );static void box_gather ( bo_t *box, bo_t *box2 );static void box_send( sout_mux_t *p_mux, bo_t *box );static block_t *bo_to_sout( sout_instance_t *p_sout, bo_t *box );static bo_t *GetMoovBox( sout_mux_t *p_mux );static block_t *ConvertSUBT( sout_mux_t *, mp4_stream_t *, block_t *);static void ConvertAVC1( sout_mux_t *, mp4_stream_t *, block_t * );/***************************************************************************** * Open: *****************************************************************************/static int Open( vlc_object_t *p_this ){ sout_mux_t *p_mux = (sout_mux_t*)p_this; sout_mux_sys_t *p_sys; bo_t *box; msg_Dbg( p_mux, "Mp4 muxer opend" ); sout_CfgParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg ); p_mux->pf_control = Control; p_mux->pf_addstream = AddStream; p_mux->pf_delstream = DelStream; p_mux->pf_mux = Mux; p_mux->p_sys = p_sys = malloc( sizeof( sout_mux_sys_t ) ); p_sys->i_pos = 0; p_sys->i_nb_streams = 0; p_sys->pp_streams = NULL; p_sys->i_mdat_pos = 0; p_sys->b_mov = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" ); p_sys->b_3gp = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "3gp" ); p_sys->i_dts_start = 0; if( !p_sys->b_mov ) { /* Now add ftyp header */ box = box_new( "ftyp" ); if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp4" ); else bo_add_fourcc( box, "isom" ); bo_add_32be ( box, 0 ); if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp4" ); else bo_add_fourcc( box, "mp41" ); box_fix( box ); p_sys->i_pos += box->i_buffer; p_sys->i_mdat_pos = p_sys->i_pos; box_send( p_mux, box ); } /* FIXME FIXME * Quicktime actually doesn't like the 64 bits extensions !!! */ p_sys->b_64_ext = VLC_FALSE; /* Now add mdat header */ box = box_new( "mdat" ); bo_add_64be ( box, 0 ); // enough to store an extended size p_sys->i_pos += box->i_buffer; box_send( p_mux, box ); return VLC_SUCCESS;}/***************************************************************************** * Close: *****************************************************************************/static void Close( vlc_object_t * p_this ){ sout_mux_t *p_mux = (sout_mux_t*)p_this; sout_mux_sys_t *p_sys = p_mux->p_sys; block_t *p_hdr; bo_t bo, *moov; vlc_value_t val; int i_trak; uint64_t i_moov_pos; msg_Dbg( p_mux, "Close" ); /* Update mdat size */ bo_init( &bo, 0, NULL, VLC_TRUE ); if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) ) { /* Extended size */ bo_add_32be ( &bo, 1 ); bo_add_fourcc( &bo, "mdat" ); bo_add_64be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos ); } else { bo_add_32be ( &bo, 8 ); bo_add_fourcc( &bo, "wide" ); bo_add_32be ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 ); bo_add_fourcc( &bo, "mdat" ); } p_hdr = bo_to_sout( p_mux->p_sout, &bo ); free( bo.p_buffer ); sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos ); sout_AccessOutWrite( p_mux->p_access, p_hdr ); /* Create MOOV header */ i_moov_pos = p_sys->i_pos; moov = GetMoovBox( p_mux ); /* Check we need to create "fast start" files */ var_Get( p_this, SOUT_CFG_PREFIX "faststart", &val ); p_sys->b_fast_start = val.b_bool; while( p_sys->b_fast_start ) { /* Move data to the end of the file so we can fit the moov header * at the start */ block_t *p_buf; int64_t i_chunk, i_size = p_sys->i_pos - p_sys->i_mdat_pos; int i_moov_size = moov->i_buffer; while( i_size > 0 ) { i_chunk = __MIN( 32768, i_size ); p_buf = block_New( p_mux, i_chunk ); sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size - i_chunk ); if( sout_AccessOutRead( p_mux->p_access, p_buf ) < i_chunk ) { msg_Warn( p_this, "read() not supported by acces output, " "won't create a fast start file" ); p_sys->b_fast_start = VLC_FALSE; block_Release( p_buf ); break; } sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size + i_moov_size - i_chunk ); sout_AccessOutWrite( p_mux->p_access, p_buf ); i_size -= i_chunk; } if( !p_sys->b_fast_start ) break; /* Fix-up samples to chunks table in MOOV header */ for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ ) { mp4_stream_t *p_stream = p_sys->pp_streams[i_trak]; unsigned int i; int i_chunk; moov->i_buffer = p_stream->i_stco_pos; for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ ) { if( p_stream->b_stco64 ) bo_add_64be( moov, p_stream->entry[i].i_pos + i_moov_size); else bo_add_32be( moov, p_stream->entry[i].i_pos + i_moov_size); while( i < p_stream->i_entry_count ) { if( i + 1 < p_stream->i_entry_count && p_stream->entry[i].i_pos + p_stream->entry[i].i_size != p_stream->entry[i + 1].i_pos ) { i++; break; } i++; } } } moov->i_buffer = i_moov_size; i_moov_pos = p_sys->i_mdat_pos; p_sys->b_fast_start = VLC_FALSE; } /* Write MOOV header */ sout_AccessOutSeek( p_mux->p_access, i_moov_pos ); box_send( p_mux, moov ); /* Clean-up */ for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ ) { mp4_stream_t *p_stream = p_sys->pp_streams[i_trak]; es_format_Clean( &p_stream->fmt ); if( p_stream->avc.i_sps ) free( p_stream->avc.sps ); if( p_stream->avc.i_pps ) free( p_stream->avc.pps ); free( p_stream->entry ); free( p_stream ); } if( p_sys->i_nb_streams ) free( p_sys->pp_streams ); free( p_sys );}/***************************************************************************** * Control: *****************************************************************************/static int Control( sout_mux_t *p_mux, int i_query, va_list args ){ vlc_bool_t *pb_bool; switch( i_query ) { case MUX_CAN_ADD_STREAM_WHILE_MUXING: pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * ); *pb_bool = VLC_FALSE; return VLC_SUCCESS; case MUX_GET_ADD_STREAM_WAIT: pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t * ); *pb_bool = VLC_TRUE; return VLC_SUCCESS; case MUX_GET_MIME: /* Not needed, as not streamable */ default: return VLC_EGENERIC; }}/***************************************************************************** * AddStream: *****************************************************************************/static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input ){ sout_mux_sys_t *p_sys = p_mux->p_sys; mp4_stream_t *p_stream; switch( p_input->p_fmt->i_codec ) { case VLC_FOURCC( 'm', 'p', '4', 'a' ): case VLC_FOURCC( 'm', 'p', '4', 'v' ): case VLC_FOURCC( 'm', 'p', 'g', 'a' ): case VLC_FOURCC( 'm', 'p', 'g', 'v' ): case VLC_FOURCC( 'M', 'J', 'P', 'G' ): case VLC_FOURCC( 'm', 'j', 'p', 'b' ): case VLC_FOURCC( 'S', 'V', 'Q', '1' ): case VLC_FOURCC( 'S', 'V', 'Q', '3' ): case VLC_FOURCC( 'h', '2', '6', '4' ): break; case VLC_FOURCC( 's', 'u', 'b', 't' ): msg_Warn( p_mux, "subtitle track added like in .mov (even when creating .mp4)" ); break; default: msg_Err( p_mux, "unsupported codec %4.4s in mp4", (char*)&p_input->p_fmt->i_codec ); return VLC_EGENERIC; } p_stream = malloc( sizeof( mp4_stream_t ) ); es_format_Copy( &p_stream->fmt, p_input->p_fmt ); p_stream->i_track_id = p_sys->i_nb_streams + 1; p_stream->i_length_neg = 0; p_stream->i_entry_count = 0; p_stream->i_entry_max = 1000; p_stream->entry = calloc( p_stream->i_entry_max, sizeof( mp4_entry_t ) ); p_stream->i_dts_start = 0; p_stream->i_duration = 0; p_stream->avc.i_profile = 77; p_stream->avc.i_level = 51; p_stream->avc.i_sps = 0; p_stream->avc.sps = NULL; p_stream->avc.i_pps = 0; p_stream->avc.pps = NULL; p_input->p_sys = p_stream; msg_Dbg( p_mux, "adding input" ); TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, p_stream ); return VLC_SUCCESS;}/***************************************************************************** * DelStream: *****************************************************************************/static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input ){ msg_Dbg( p_mux, "removing input" ); return VLC_SUCCESS;}static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts ){ mtime_t i_dts; int i_stream; int i; for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ ) { block_fifo_t *p_fifo = p_mux->pp_inputs[i]->p_fifo; block_t *p_buf; if( p_fifo->i_depth <= 1 ) { if( p_mux->pp_inputs[i]->p_fmt->i_cat != SPU_ES ) { return -1; // wait that all fifo have at least 2 packets } /* For SPU, we wait only 1 packet */ continue; } p_buf = block_FifoShow( p_fifo ); if( i_stream < 0 || p_buf->i_dts < i_dts ) { i_dts = p_buf->i_dts; i_stream = i; } } if( pi_stream ) { *pi_stream = i_stream; } if( pi_dts ) { *pi_dts = i_dts; } return i_stream;}/***************************************************************************** * Mux: *****************************************************************************/static int Mux( sout_mux_t *p_mux ){ sout_mux_sys_t *p_sys = p_mux->p_sys; for( ;; ) { sout_input_t *p_input; int i_stream; mp4_stream_t *p_stream; block_t *p_data; mtime_t i_dts; if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 ) { return( VLC_SUCCESS ); } p_input = p_mux->pp_inputs[i_stream]; p_stream = (mp4_stream_t*)p_input->p_sys;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -