📄 mp4.c
字号:
/***************************************************************************** * mp4.c : MP4 file input module for vlc ***************************************************************************** * Copyright (C) 2001-2004 VideoLAN * $Id: mp4.c,v 1.59 2004/02/07 13:26:24 fenrir Exp $ * Authors: Laurent Aimar <fenrir@via.ecp.fr> * * 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> /* malloc(), free() */#include <vlc/vlc.h>#include <vlc/input.h>#include <vlc_playlist.h>#include "iso_lang.h"#include "libmp4.h"#include "mp4.h"#include "drms.h"/***************************************************************************** * Module descriptor *****************************************************************************/static int Open ( vlc_object_t * );static void Close( vlc_object_t * );vlc_module_begin(); set_description( _("MP4 stream demuxer") ); set_capability( "demux", 242 ); set_callbacks( Open, Close );vlc_module_end();/* TODO: * - DEMUX_GET_META and meta parsing *//***************************************************************************** * Local prototypes *****************************************************************************/static int Demux ( input_thread_t * );static int DemuxRef( input_thread_t *p_input ){ return 0;}static int Seek ( input_thread_t *, mtime_t );static int Control ( input_thread_t *, int, va_list );/***************************************************************************** * Declaration of local function *****************************************************************************/static void MP4_TrackCreate ( input_thread_t *, mp4_track_t *, MP4_Box_t *);static void MP4_TrackDestroy( input_thread_t *, mp4_track_t * );static int MP4_TrackSelect ( input_thread_t *, mp4_track_t *, mtime_t );static void MP4_TrackUnselect(input_thread_t *, mp4_track_t * );static int MP4_TrackSeek ( input_thread_t *, mp4_track_t *, mtime_t );static uint64_t MP4_TrackGetPos ( mp4_track_t * );static int MP4_TrackSampleSize( mp4_track_t * );static int MP4_TrackNextSample( input_thread_t *, mp4_track_t * );static void MP4_TrackSetELST( input_thread_t *, mp4_track_t *, int64_t );/* Return time in 祍 of a track */static inline int64_t MP4_TrackGetPTS( input_thread_t *p_input, mp4_track_t *p_track ){ unsigned int i_sample; unsigned int i_index; int64_t i_dts; i_sample = p_track->i_sample - p_track->chunk[p_track->i_chunk].i_sample_first; i_dts = p_track->chunk[p_track->i_chunk].i_first_dts; i_index = 0; while( i_sample > 0 ) { if( i_sample > p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] ) { i_dts += p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index] * p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index]; i_sample -= p_track->chunk[p_track->i_chunk].p_sample_count_dts[i_index]; i_index++; } else { i_dts += i_sample * p_track->chunk[p_track->i_chunk].p_sample_delta_dts[i_index]; i_sample = 0; break; } } /* now handle elst */ if( p_track->p_elst ) { demux_sys_t *p_sys = p_input->p_demux_data; MP4_Box_data_elst_t *elst = p_track->p_elst->data.p_elst; /* convert to offset */ if( ( elst->i_media_rate_integer[p_track->i_elst] > 0 || elst->i_media_rate_fraction[p_track->i_elst] > 0 ) && elst->i_media_time[p_track->i_elst] > 0 ) { i_dts -= elst->i_media_time[p_track->i_elst]; } /* add i_elst_time */ i_dts += p_track->i_elst_time * p_track->i_timescale / p_sys->i_timescale; if( i_dts < 0 ) i_dts = 0; } return (int64_t)1000000 * i_dts / p_track->i_timescale;}static inline int64_t MP4_GetMoviePTS(demux_sys_t *p_sys ){ return (int64_t)1000000 * p_sys->i_time / p_sys->i_timescale;}#define FREE( p ) if( p ) { free( p ); (p) = NULL;}/***************************************************************************** * Open: check file and initializes MP4 structures *****************************************************************************/static int Open( vlc_object_t * p_this ){ input_thread_t *p_input = (input_thread_t *)p_this; demux_sys_t *p_sys; uint8_t *p_peek; MP4_Box_t *p_ftyp; MP4_Box_t *p_rmra; MP4_Box_t *p_mvhd; MP4_Box_t *p_trak; unsigned int i; vlc_bool_t b_seekable; /* a little test to see if it could be a mp4 */ if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 ) { msg_Warn( p_input, "MP4 plugin discarded (cannot peek)" ); return VLC_EGENERIC; } switch( VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) ) { case FOURCC_ftyp: case FOURCC_moov: case FOURCC_foov: case FOURCC_moof: case FOURCC_mdat: case FOURCC_udta: case FOURCC_free: case FOURCC_skip: case FOURCC_wide: case VLC_FOURCC( 'p', 'n', 'o', 't' ): break; default: msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" ); return VLC_EGENERIC; } /* I need to seek */ stream_Control( p_input->s, STREAM_CAN_SEEK, &b_seekable ); if( !b_seekable ) { msg_Warn( p_input, "MP4 plugin discarded (unseekable)" ); return VLC_EGENERIC; } /*Set exported functions */ p_input->pf_demux = Demux; p_input->pf_demux_control = Control; /* create our structure that will contains all data */ p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) ); memset( p_sys, 0, sizeof( demux_sys_t ) ); /* Now load all boxes ( except raw data ) */ if( ( p_sys->p_root = MP4_BoxGetRoot( p_input ) ) == NULL ) { msg_Warn( p_input, "MP4 plugin discarded (not a valid file)" ); goto error; } MP4_BoxDumpStructure( p_input, p_sys->p_root ); if( ( p_ftyp = MP4_BoxGet( p_sys->p_root, "/ftyp" ) ) ) { switch( p_ftyp->data.p_ftyp->i_major_brand ) { case( FOURCC_isom ): msg_Dbg( p_input, "ISO Media file (isom) version %d.", p_ftyp->data.p_ftyp->i_minor_version ); break; default: msg_Dbg( p_input, "unrecognized major file specification (%4.4s).", (char*)&p_ftyp->data.p_ftyp->i_major_brand ); break; } } else { msg_Dbg( p_input, "file type box missing (assuming ISO Media file)" ); } /* the file need to have one moov box */ if( MP4_BoxCount( p_sys->p_root, "/moov" ) <= 0 ) { MP4_Box_t *p_foov = MP4_BoxGet( p_sys->p_root, "/foov" ); if( !p_foov ) { msg_Err( p_input, "MP4 plugin discarded (no moov box)" ); goto error; } /* we have a free box as a moov, rename it */ p_foov->i_type = FOURCC_moov; } if( ( p_rmra = MP4_BoxGet( p_sys->p_root, "/moov/rmra" ) ) ) { playlist_t *p_playlist; int i_count = MP4_BoxCount( p_rmra, "rmda" ); int i; msg_Dbg( p_input, "detected playlist mov file (%d ref)", i_count ); p_playlist = (playlist_t *)vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); if( p_playlist ) { //p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE; for( i = 0; i < i_count; i++ ) { MP4_Box_t *p_rdrf = MP4_BoxGet( p_rmra, "rmda[%d]/rdrf", i ); char *psz_ref; uint32_t i_ref_type; if( !p_rdrf || !( psz_ref = p_rdrf->data.p_rdrf->psz_ref ) ) { continue; } i_ref_type = p_rdrf->data.p_rdrf->i_ref_type; msg_Dbg( p_input, "new ref=`%s' type=%4.4s", psz_ref, (char*)&i_ref_type ); if( i_ref_type == VLC_FOURCC( 'u', 'r', 'l', ' ' ) ) { if( strstr( psz_ref, "qt5gateQT" ) ) { msg_Dbg( p_input, "ignoring pseudo ref =`%s'", psz_ref ); continue; } if( !strncmp( psz_ref, "http://", 7 ) || !strncmp( psz_ref, "rtsp://", 7 ) ) { msg_Dbg( p_input, "adding ref = `%s'", psz_ref ); playlist_Add( p_playlist, psz_ref, psz_ref, PLAYLIST_APPEND, PLAYLIST_END ); } else { /* msg dbg relative ? */ char *psz_absolute = alloca( strlen( p_input->psz_source ) + strlen( psz_ref ) + 1); char *end = strrchr( p_input->psz_source, '/' ); if( end ) { int i_len = end + 1 - p_input->psz_source; strncpy( psz_absolute, p_input->psz_source, i_len); psz_absolute[i_len] = '\0'; } else { strcpy( psz_absolute, "" ); } strcat( psz_absolute, psz_ref ); msg_Dbg( p_input, "adding ref = `%s'", psz_absolute ); playlist_Add( p_playlist, psz_absolute, psz_absolute, PLAYLIST_APPEND, PLAYLIST_END ); } } else { msg_Err( p_input, "unknown ref type=%4.4s FIXME (send a bug report)", (char*)&p_rdrf->data.p_rdrf->i_ref_type ); } } vlc_object_release( p_playlist ); } else { msg_Err( p_input, "can't find playlist" ); } } if( !(p_mvhd = MP4_BoxGet( p_sys->p_root, "/moov/mvhd" ) ) ) { if( !p_rmra ) { msg_Err( p_input, "cannot find /moov/mvhd" ); goto error; } else { msg_Warn( p_input, "cannot find /moov/mvhd (pure ref file)" ); p_input->pf_demux = DemuxRef; return VLC_SUCCESS; } } else { p_sys->i_timescale = p_mvhd->data.p_mvhd->i_timescale; p_sys->i_duration = p_mvhd->data.p_mvhd->i_duration; } if( !( p_sys->i_tracks = MP4_BoxCount( p_sys->p_root, "/moov/trak" ) ) ) { msg_Err( p_input, "cannot find any /moov/trak" ); goto error; } msg_Dbg( p_input, "find %d track%c", p_sys->i_tracks, p_sys->i_tracks ? 's':' ' ); /* create one program */ vlc_mutex_lock( &p_input->stream.stream_lock ); if( input_InitStream( p_input, 0 ) == -1) { vlc_mutex_unlock( &p_input->stream.stream_lock ); msg_Err( p_input, "cannot init stream" ); goto error; } if( p_sys->i_duration/p_sys->i_timescale > 0 ) { p_input->stream.i_mux_rate = p_input->stream.p_selected_area->i_size / 50 / ( p_sys->i_duration / p_sys->i_timescale ); } else { p_input->stream.i_mux_rate = 0; } vlc_mutex_unlock( &p_input->stream.stream_lock ); /* allocate memory */ p_sys->track = calloc( p_sys->i_tracks, sizeof( mp4_track_t ) ); memset( p_sys->track, 0, p_sys->i_tracks * sizeof( mp4_track_t ) ); /* now process each track and extract all usefull informations */ for( i = 0; i < p_sys->i_tracks; i++ ) { p_trak = MP4_BoxGet( p_sys->p_root, "/moov/trak[%d]", i ); MP4_TrackCreate( p_input, &p_sys->track[i], p_trak ); if( p_sys->track[i].b_ok ) { char *psz_cat; switch( p_sys->track[i].fmt.i_cat ) { case( VIDEO_ES ): psz_cat = "video"; break; case( AUDIO_ES ): psz_cat = "audio"; break; default: psz_cat = "unknown"; break; } msg_Dbg( p_input, "adding track[Id 0x%x] %s (%s) language %s", p_sys->track[i].i_track_ID, psz_cat, p_sys->track[i].b_enable ? "enable":"disable", p_sys->track[i].fmt.psz_language ? p_sys->track[i].fmt.psz_language : "undef" ); } else { msg_Dbg( p_input, "ignoring track[Id 0x%x]", p_sys->track[i].i_track_ID ); } } return VLC_SUCCESS;error: if( p_sys->p_root ) { MP4_BoxFree( p_input, p_sys->p_root ); } free( p_sys ); return VLC_EGENERIC;}/***************************************************************************** * Demux: read packet and send them to decoders ***************************************************************************** * TODO check for newly selected track (ie audio upt to now ) *****************************************************************************/static int Demux( input_thread_t *p_input ){ demux_sys_t *p_sys = p_input->p_demux_data; unsigned int i_track; unsigned int i_track_selected; vlc_bool_t b_play_audio; /* check for newly selected/unselected track */ for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks; i_track++ ) { mp4_track_t *tk = &p_sys->track[i_track]; if( tk->b_selected && tk->i_sample >= tk->i_sample_count ) { msg_Warn( p_input, "track[0x%x] will be disabled", tk->i_track_ID ); MP4_TrackUnselect( p_input, tk); } else if( tk->b_ok ) { vlc_bool_t b; es_out_Control( p_input->p_es_out, ES_OUT_GET_ES_STATE, tk->p_es, &b ); if( tk->b_selected && !b ) { MP4_TrackUnselect( p_input, tk ); } else if( !tk->b_selected && b) { MP4_TrackSelect( p_input, tk, MP4_GetMoviePTS( p_sys ) ); } if( tk->b_selected ) { i_track_selected++; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -