📄 media_flow.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000-2005. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com * Bill May wmay@cisco.com */#include "mp4live.h"#include "media_flow.h"#include "audio_oss_source.h"#include "video_v4l_source.h"#include "file_mp4_recorder.h"#include "rtp_transmitter.h"#include "file_raw_sink.h"#include "mp4live_common.h"#include "video_encoder.h"#include "audio_encoder.h"#include "text_encoder.h"#include "text_encoder.h"#include "media_stream.h"#include "profile_video.h"#include "profile_audio.h"#include "profile_text.h"#include "text_source.h"CAVMediaFlow::CAVMediaFlow(CLiveConfig* pConfig){ m_pConfig = pConfig; m_started = false; m_videoSource = NULL; m_audioSource = NULL; m_textSource = NULL; m_mp4RawRecorder = NULL; m_rawSink = NULL; m_video_profile_list = NULL; m_audio_profile_list = NULL; m_text_profile_list = NULL; m_stream_list = NULL; m_video_encoder_list = NULL; m_audio_encoder_list = NULL; m_text_encoder_list = NULL; ReadStreams(); ValidateAndUpdateStreams();}CAVMediaFlow::~CAVMediaFlow() { CAVMediaFlow::Stop(); if (m_videoSource != NULL) { m_videoSource->StopThread(); delete m_videoSource; m_videoSource = NULL; } if (m_stream_list != NULL) { delete m_stream_list; m_stream_list = NULL; } if (m_video_profile_list != NULL) { delete m_video_profile_list; m_video_profile_list = NULL; } if (m_audio_profile_list != NULL) { delete m_audio_profile_list; m_audio_profile_list = NULL; } if (m_text_profile_list != NULL) { delete m_text_profile_list; m_text_profile_list = NULL; }}void CAVMediaFlow::Stop(void){ if (!m_started) { return; } //debug_message("media flow - stop"); bool oneSource = (m_audioSource == m_videoSource); // Stop the sources if (m_audioSource) { m_audioSource->RemoveAllSinks(); m_audioSource->StopThread(); delete m_audioSource; m_audioSource = NULL; } if (!m_pConfig->IsCaptureVideoSource()) { if (m_videoSource && !oneSource) { debug_message("Stopping video source thread"); m_videoSource->RemoveAllSinks(); m_videoSource->StopThread(); delete m_videoSource; } m_videoSource = NULL; } if (m_textSource) { m_textSource->RemoveAllSinks(); m_textSource->StopThread(); delete m_textSource; m_textSource = NULL; debug_message("Text stopped"); } // stop the encoders - remove the sinks from the source // This will stop the sinks, and deletes the rtp destinations CMediaCodec *mc = m_video_encoder_list, *p; while (mc != NULL) { //debug_message("stopping video profile %s", mc->GetProfileName()); if (m_videoSource != NULL) { m_videoSource->RemoveSink(mc); } // debug_message("sinks removed"); mc->StopThread(); //debug_message("thread_stopped"); mc->StopSinks(); //debug_message("sinks stopped"); p = mc; mc = mc->GetNext(); delete p; } m_video_encoder_list = NULL; mc = m_audio_encoder_list; while (mc != NULL) { // debug_message("stopping audio profile %s", mc->GetProfileName()); mc->StopThread(); //debug_message("thread stopped"); mc->StopSinks(); //debug_message("stopping sinks"); p = mc; mc = mc->GetNext(); delete p; } m_audio_encoder_list = NULL; mc = m_text_encoder_list; while (mc != NULL) { debug_message("stopping text profile %s", mc->GetProfileName()); mc->StopThread(); debug_message("thread stopped"); mc->StopSinks(); debug_message("stopping sinks"); p = mc; mc = mc->GetNext(); delete p; } m_text_encoder_list = NULL; // Now, stop the streams (stops and deletes file recorders CMediaStream *stream; stream = m_stream_list->GetHead(); while (stream != NULL) { stream->Stop(); stream = stream->GetNext(); } // Note - m_videoSource may still be active at this point // be sure to remove any sinks attached to it. // any other sinks that were added should be stopped here. if (m_rawSink) { if (m_videoSource != NULL) { m_videoSource->RemoveSink(m_rawSink); } m_rawSink->StopThread(); delete m_rawSink; m_rawSink = NULL; } if (m_mp4RawRecorder != NULL) { if (m_videoSource != NULL) { m_videoSource->RemoveSink(m_mp4RawRecorder); } m_mp4RawRecorder->StopThread(); delete m_mp4RawRecorder; m_mp4RawRecorder = NULL; } m_started = false; }void CAVMediaFlow::SetPictureControls(void){ if (m_videoSource) { m_videoSource->SetPictureControls(); }}void CAVMediaFlow::SetAudioOutput(bool mute){ static int muted = 0; static int lastVolume; const char* mixerName = m_pConfig->GetStringValue(CONFIG_AUDIO_MIXER_NAME); int mixer = open(mixerName, O_RDONLY); if (mixer < 0) { error_message("Couldn't open mixer %s", mixerName); return; } if (mute) { ioctl(mixer, SOUND_MIXER_READ_LINE, &lastVolume); ioctl(mixer, SOUND_MIXER_WRITE_LINE, &muted); } else { int newVolume; ioctl(mixer, SOUND_MIXER_READ_LINE, &newVolume); if (newVolume == 0) { ioctl(mixer, SOUND_MIXER_WRITE_LINE, &lastVolume); } } close(mixer);}bool CAVMediaFlow::GetStatus(u_int32_t valueName, void* pValue) { CMediaSource* source = NULL; if (m_videoSource) { source = m_videoSource; } else if (m_audioSource) { source = m_audioSource; } else if (m_textSource) { source = m_textSource; } switch (valueName) { case FLOW_STATUS_DONE: { bool done = true; if (m_videoSource) { done = m_videoSource->IsDone(); } if (m_audioSource) { done = (done && m_audioSource->IsDone()); } if (m_textSource) { done = (done && m_textSource->IsDone()); } *(bool*)pValue = done; } break; case FLOW_STATUS_PROGRESS: if (source) { *(float*)pValue = source->GetProgress(); } else { *(float*)pValue = 0.0; } break; default: return false; } return true;}// CheckandCreateDir - based on name, check if directory exists// if not, create it. If so, check that it is a directorystatic bool CheckandCreateDir (const char *name){ struct stat statbuf; if (stat(name, &statbuf) == 0) { if (!S_ISDIR(statbuf.st_mode)) { error_message("%s is not a directory", name); return false; } } else { if (mkdir(name, S_IRWXU) < 0) { error_message("can't create directory \"%s\" - %s", name, strerror(errno)); return false; } } return true;} /* * Check dup - checks for duplicates in media stream addresses. */static bool CheckDup (CMediaStream *orig, CMediaStream *check, config_index_t profile_ix, config_index_t orig_addr_ix, config_index_t orig_port_ix, config_index_t check_profile_ix, config_index_t check_addr_ix, config_index_t check_port_ix){ // see if the address and port match if (orig->GetIntegerValue(orig_port_ix) == check->GetIntegerValue(check_port_ix) && strcmp(orig->GetStringValue(orig_addr_ix), orig->GetStringValue(check_addr_ix)) == 0) { // we have the same address:port if (profile_ix == check_profile_ix) { // See if the profiles match - if they do, no dup if (strcmp(orig->GetStringValue(profile_ix), check->GetStringValue(check_profile_ix)) == 0) { return false; } } // we have an address:port match, and we are not on the same profile return true; } return false;}// CheckAddressForDup - check audio, video and text addresses, unless the// stream matches.bool CheckAddressForDup (CMediaStreamList *stream_list, CMediaStream *s, config_index_t profile_ix, config_index_t addr_ix, config_index_t port_ix){ CMediaStream *p = stream_list->GetHead(); while (p != NULL) { if (p != s) { if (CheckDup(s, p, profile_ix, addr_ix, port_ix, STREAM_VIDEO_PROFILE, STREAM_VIDEO_DEST_ADDR, STREAM_VIDEO_DEST_PORT)) { return true; } if (CheckDup(s, p, profile_ix, addr_ix, port_ix, STREAM_AUDIO_PROFILE, STREAM_AUDIO_DEST_ADDR, STREAM_AUDIO_DEST_PORT)) { return true; } if (CheckDup(s, p, profile_ix, addr_ix, port_ix, STREAM_TEXT_PROFILE, STREAM_TEXT_DEST_ADDR, STREAM_TEXT_DEST_PORT)) { return true; } } p = p->GetNext(); } return false;}// SetRestOfProfile - all streams that have matching profiles and do// not have fixed addresses to the same address:portvoid SetRestOfProfile (CMediaStream *s, config_index_t profile, config_index_t fixed, config_index_t addr, config_index_t port){ CMediaStream *p = s->GetNext(); while (p != NULL) { if (strcmp(p->GetStringValue(profile), s->GetStringValue(profile)) == 0) { if (p->GetBoolValue(fixed) == false) { p->SetStringValue(addr, s->GetStringValue(addr)); p->SetIntegerValue(port, s->GetIntegerValue(port)); } } p = p->GetNext(); }}// ValidateAddressAndPort does a number of things - verifies that the// address and port are in a valid range, set all streams with similiar// profiles to that address, and makes sure there are no duplicate addressesvoid ValidateIpAddressAndPort (CMediaStreamList *stream_list, CMediaStream *s, config_index_t profile_ix, config_index_t fixed_ix, config_index_t addr_ix, config_index_t port_ix){ bool corrected; debug_message("Checking %s %s %s %s:%u", s->GetName(), s->GetNameFromIndex(profile_ix), s->GetStringValue(profile_ix), s->GetStringValue(addr_ix), s->GetIntegerValue(port_ix)); do { if (ValidateIpAddress(s->GetStringValue(addr_ix)) == false) { struct in_addr in; debug_message("Stream %s %s address was invalid \"%s\"", s->GetName(), s->GetNameFromIndex(profile_ix), s->GetStringValue(addr_ix)); in.s_addr = GetRandomMcastAddress(); s->SetStringValue(addr_ix, inet_ntoa(in)); debug_message("changed to \"%s\"", s->GetStringValue(addr_ix)); } if (s->GetIntegerValue(port_ix) >= 0xffff || ValidateIpPort(s->GetIntegerValue(port_ix)) == false) { debug_message("Stream %s %s address was invalid %u", s->GetName(), s->GetNameFromIndex(profile_ix), s->GetIntegerValue(port_ix)); s->SetIntegerValue(port_ix, GetRandomPort()); debug_message("Changed to %u", s->GetIntegerValue(port_ix)); } if (s->GetBoolValue(fixed_ix) == false) { SetRestOfProfile(s, profile_ix, fixed_ix, addr_ix, port_ix); } corrected = CheckAddressForDup(stream_list, s, profile_ix, addr_ix, port_ix); if (corrected == true) { // keep address, move port - by setting port to 0, we will trigger // the above s->SetIntegerValue(port_ix, 0); } } while (corrected);}// ReadStreams - read all the profiles (audio and video), then reads// the streamsbool CAVMediaFlow::ReadStreams (void){ char base[PATH_MAX]; const char *d; d = m_pConfig->GetStringValue(CONFIG_APP_PROFILE_DIRECTORY); if (d != NULL) { strcpy(base, d); } else { GetHomeDirectory(base); strcat(base, ".mp4live_d"); } if (CheckandCreateDir(base) == false) return false; // Load video profiles Make sure there is a "default" profile char profile_dir[PATH_MAX]; snprintf(profile_dir, PATH_MAX, "%s/Video", base); if (CheckandCreateDir(profile_dir) == false) { return false; } m_video_profile_list = new CVideoProfileList(profile_dir); m_video_profile_list->Load(); if (m_video_profile_list->GetCount() == 0 || m_video_profile_list->FindProfile("default") == NULL) { if (m_video_profile_list->CreateConfig("default") == false) { error_message("Can't create video default profile"); return false; } else { debug_message("Created default video profile"); } } snprintf(profile_dir, PATH_MAX, "%s/Audio", base); if (CheckandCreateDir(profile_dir) == false) { return false; } // load audio profiles - make sure there is a default profile m_audio_profile_list = new CAudioProfileList(profile_dir); m_audio_profile_list->Load(); if (m_audio_profile_list->GetCount() == 0 ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -