static void Del(sout_stream_t *p_stream, sout_stream_id_sys_t *id) { sout_stream_sys_t *p_sys = p_stream->p_sys; for (size_t i=0; i<p_sys->streams.size(); i++) { if ( p_sys->streams[i] == id ) { if ( p_sys->streams[i]->p_sub_id != NULL ) sout_StreamIdDel( p_sys->p_out, p_sys->streams[i]->p_sub_id ); es_format_Clean( &p_sys->streams[i]->fmt ); free( p_sys->streams[i] ); p_sys->streams.erase( p_sys->streams.begin() + i ); p_sys->es_changed = true; break; } } if ( p_sys->streams.empty() ) { p_sys->p_intf->requestPlayerStop(); sout_StreamChainDelete( p_sys->p_out, p_sys->p_out ); p_sys->p_out = NULL; p_sys->sout = ""; } }
/***************************************************************************** * Close: *****************************************************************************/ static void Close( vlc_object_t * p_this ) { sout_stream_t *p_stream = (sout_stream_t*)p_this; sout_stream_sys_t *p_sys = p_stream->p_sys; if( p_sys->p_out ) sout_StreamChainDelete( p_sys->p_out, p_sys->p_out ); TAB_CLEAN( p_sys->i_id, p_sys->id ); free( p_sys->psz_prefix ); free( p_sys ); }
/***************************************************************************** * sout_DeleteInstance: delete a previously allocated instance *****************************************************************************/ void sout_DeleteInstance( sout_instance_t * p_sout ) { /* remove the stream out chain */ sout_StreamChainDelete( p_sout->p_stream, NULL ); /* *** free all string *** */ FREENULL( p_sout->psz_sout ); vlc_mutex_destroy( &p_sout->lock ); /* *** free structure *** */ vlc_object_release( p_sout ); }
/** * @brief Clean and release the variables in a sout_stream_sys_t structure */ static void Clean(sout_stream_t *p_stream) { sout_stream_sys_t *p_sys = p_stream->p_sys; if (p_sys->p_out) { sout_StreamChainDelete(p_sys->p_out, p_sys->p_out); } p_sys->p_intf->disconnectChromecast(); delete p_sys; }
static void DelStream(sout_stream_t *stream) { sout_stream_sys_t *sys = stream->p_sys; if (sys->stream == NULL) return; for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next) if (id->id != NULL) sout_StreamIdDel(sys->stream, id->id); sout_StreamChainDelete(sys->stream, NULL); sys->stream = NULL; }
/** * @brief Clean and release the variables in a sout_stream_sys_t structure */ static void Clean(sout_stream_t *p_stream) { sout_stream_sys_t *p_sys = p_stream->p_sys; if (p_sys->p_out) { vlc_mutex_destroy(&p_sys->lock); vlc_cond_destroy(&p_sys->loadCommandCond); sout_StreamChainDelete(p_sys->p_out, p_sys->p_out); } disconnectChromecast(p_stream); delete p_sys; }
/***************************************************************************** * Close: *****************************************************************************/ static void Close( vlc_object_t * p_this ) { sout_stream_t *p_stream = (sout_stream_t*)p_this; sout_stream_sys_t *p_sys = p_stream->p_sys; int i; msg_Dbg( p_stream, "closing a duplication" ); for( i = 0; i < p_sys->i_nb_streams; i++ ) { sout_StreamChainDelete(p_sys->pp_streams[i], p_sys->pp_last_streams[i]); free( p_sys->ppsz_select[i] ); } free( p_sys->pp_streams ); free( p_sys->pp_last_streams ); free( p_sys->ppsz_select ); free( p_sys ); }
~sout_stream_sys_t() { sout_StreamChainDelete(p_out, p_out); delete p_intf; }
int sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream ) { assert( p_stream->p_sys == this ); if ( es_changed ) { es_changed = false; bool canRemux = true; vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0; for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it) { const es_format_t *p_es = &(*it)->fmt; if (p_es->i_cat == AUDIO_ES) { if (!canDecodeAudio( p_es )) { msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec ); canRemux = false; } else if (i_codec_audio == 0) i_codec_audio = p_es->i_codec; } else if (b_has_video && p_es->i_cat == VIDEO_ES) { if (!canDecodeVideo( p_es )) { msg_Dbg( p_stream, "can't remux video track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec ); canRemux = false; } else if (i_codec_video == 0) i_codec_video = p_es->i_codec; } } std::stringstream ssout; if ( !canRemux ) { if ( i_codec_audio == 0 ) i_codec_audio = DEFAULT_TRANSCODE_AUDIO; /* avcodec AAC encoder is experimental */ if ( i_codec_audio == VLC_CODEC_MP4A || i_codec_audio == VLC_FOURCC('h', 'a', 'a', 'c') || i_codec_audio == VLC_FOURCC('l', 'a', 'a', 'c') || i_codec_audio == VLC_FOURCC('s', 'a', 'a', 'c')) i_codec_audio = DEFAULT_TRANSCODE_AUDIO; if ( i_codec_video == 0 ) i_codec_video = DEFAULT_TRANSCODE_VIDEO; /* TODO: provide audio samplerate and channels */ ssout << "transcode{acodec="; char s_fourcc[5]; vlc_fourcc_to_char( i_codec_audio, s_fourcc ); s_fourcc[4] = '\0'; ssout << s_fourcc; if ( b_has_video ) { /* TODO: provide maxwidth,maxheight */ ssout << ",vcodec="; vlc_fourcc_to_char( i_codec_video, s_fourcc ); s_fourcc[4] = '\0'; ssout << s_fourcc; } ssout << "}:"; } std::string mime; if ( !b_has_video && default_muxer == DEFAULT_MUXER ) mime = "audio/x-matroska"; else if ( i_codec_audio == VLC_CODEC_VORBIS && i_codec_video == VLC_CODEC_VP8 && default_muxer == DEFAULT_MUXER ) mime = "video/webm"; else mime = default_mime; ssout << "http{dst=:" << i_port << "/stream" << ",mux=" << default_muxer << ",access=http{mime=" << mime << "}}"; if ( sout != ssout.str() ) { if ( unlikely( p_out != NULL ) ) { sout_StreamChainDelete( p_out, p_out ); sout = ""; } p_out = sout_StreamChainNew( p_stream->p_sout, ssout.str().c_str(), NULL, NULL); if (p_out == NULL) { msg_Dbg(p_stream, "could not create sout chain:%s", ssout.str().c_str()); return VLC_EGENERIC; } sout = ssout.str(); } /* check the streams we can actually add */ for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it) { sout_stream_id_sys_t *p_sys_id = *it; p_sys_id->p_sub_id = sout_StreamIdAdd( p_out, &p_sys_id->fmt ); if ( p_sys_id->p_sub_id == NULL ) { msg_Err( p_stream, "can't handle a stream" ); streams.erase( it, it ); } } /* tell the chromecast to load the content */ p_intf->setHasInput( true, mime ); } return VLC_SUCCESS; }
static void OutputStart( sout_stream_t *p_stream ) { sout_stream_sys_t *p_sys = p_stream->p_sys; /* */ if( p_sys->b_drop ) return; /* From now on drop packet that cannot be handled */ p_sys->b_drop = true; /* Detect streams to smart select muxer */ const char *psz_muxer = NULL; const char *psz_extension = NULL; /* Look for preferred muxer * TODO we could insert transcode in a few cases like * s16l <-> s16b */ for( unsigned i = 0; i < sizeof(p_muxers) / sizeof(*p_muxers); i++ ) { bool b_ok; if( p_sys->i_id > p_muxers[i].i_es_max ) continue; b_ok = true; for( int j = 0; j < p_sys->i_id; j++ ) { es_format_t *p_fmt = &p_sys->id[j]->fmt; b_ok = false; for( int k = 0; p_muxers[i].codec[k] != 0; k++ ) { if( p_fmt->i_codec == p_muxers[i].codec[k] ) { b_ok = true; break; } } if( !b_ok ) break; } if( !b_ok ) continue; psz_muxer = p_muxers[i].psz_muxer; psz_extension = p_muxers[i].psz_extension; break; } /* If failed, brute force our demuxers and select the one that * keeps most of our stream */ if( !psz_muxer || !psz_extension ) { static const char ppsz_muxers[][2][4] = { { "avi", "avi" }, { "mp4", "mp4" }, { "ogg", "ogg" }, { "asf", "asf" }, { "ts", "ts" }, { "ps", "mpg" }, { "mkv", "mkv" }, #if 0 // XXX ffmpeg sefault really easily if you try an unsupported codec // mov and avi at least segfault { "avformat{mux=avi}", "avi" }, { "avformat{mux=mov}", "mov" }, { "avformat{mux=mp4}", "mp4" }, { "avformat{mux=nsv}", "nsv" }, { "avformat{mux=flv}", "flv" }, #endif }; int i_best = 0; int i_best_es = 0; msg_Warn( p_stream, "failed to find an adequate muxer, probing muxers" ); for( unsigned i = 0; i < sizeof(ppsz_muxers) / sizeof(*ppsz_muxers); i++ ) { char *psz_file; int i_es; psz_file = tempnam( NULL, "vlc" ); if( !psz_file ) continue; msg_Dbg( p_stream, "probing muxer %s", ppsz_muxers[i][0] ); i_es = OutputNew( p_stream, ppsz_muxers[i][0], psz_file, NULL ); if( i_es < 0 ) { vlc_unlink( psz_file ); free( psz_file ); continue; } /* */ for( int i = 0; i < p_sys->i_id; i++ ) { sout_stream_id_t *id = p_sys->id[i]; if( id->id ) sout_StreamIdDel( p_sys->p_out, id->id ); id->id = NULL; } if( p_sys->p_out ) sout_StreamChainDelete( p_sys->p_out, p_sys->p_out ); p_sys->p_out = NULL; if( i_es > i_best_es ) { i_best_es = i_es; i_best = i; if( i_best_es >= p_sys->i_id ) break; } vlc_unlink( psz_file ); free( psz_file ); } /* */ psz_muxer = ppsz_muxers[i_best][0]; psz_extension = ppsz_muxers[i_best][1]; msg_Dbg( p_stream, "using muxer %s with extension %s (%d/%d streams accepted)", psz_muxer, psz_extension, i_best_es, p_sys->i_id ); } /* Create the output */ if( OutputNew( p_stream, psz_muxer, p_sys->psz_prefix, psz_extension ) < 0 ) { msg_Err( p_stream, "failed to open output"); return; } /* Compute highest timestamp of first I over all streams */ p_sys->i_dts_start = 0; for( int i = 0; i < p_sys->i_id; i++ ) { sout_stream_id_t *id = p_sys->id[i]; block_t *p_block; if( !id->id || !id->p_first ) continue; mtime_t i_dts = id->p_first->i_dts; for( p_block = id->p_first; p_block != NULL; p_block = p_block->p_next ) { if( p_block->i_flags & BLOCK_FLAG_TYPE_I ) { i_dts = p_block->i_dts; break; } } if( i_dts > p_sys->i_dts_start ) p_sys->i_dts_start = i_dts; } /* Send buffered data */ for( int i = 0; i < p_sys->i_id; i++ ) { sout_stream_id_t *id = p_sys->id[i]; if( !id->id ) continue; block_t *p_block = id->p_first; while( p_block ) { block_t *p_next = p_block->p_next; p_block->p_next = NULL; OutputSend( p_stream, id, p_block ); p_block = p_next; } id->p_first = NULL; id->pp_last = &id->p_first; } }