static void tlocal_destructor (_libMary_VoidPtr const _tlocal) { logD_ (_func, "tlocal ", (UintPtr) _tlocal); #ifndef LIBMARY_TLOCAL #ifndef LIBMARY_PTHREAD // All gprivates are reset to NULL by glib/pthreads before tlocal_destructor() // is called. We restore the right value for tlocal gprivate, which is safe // since it doesn't have an associated destructor callback. g_private_set (LIBMARY__TLOCAL_GPRIVATE, _tlocal); #endif #endif // Exception dtors may call arbitrary code, so we're // clearing exceptions first. exc_none (); if (LibMary_ThreadLocal * const tlocal = static_cast <LibMary_ThreadLocal*> (_tlocal)) delete tlocal; #ifdef LIBMARY_TLOCAL _libMary_tlocal = NULL; #endif }
void EventService::line (ConstMemory const line, void * const _session) { Session * const session = static_cast <Session*> (_session); Ref<EventService> const self = session->weak_event_service.getRef (); if (!self) return; logD_ (_self_func, "line: ", line); }
void dumpH264AvcNalUnits (PagePool::Page *page, size_t msg_offs, size_t msg_len) { Size const msg_csum = calculateChecksumPages (page, msg_offs, msg_len); logD_ (_func, "msg_offs ", msg_offs, ", msg_len ", msg_len, ", msg_csum " _hex (msg_csum)); for (;;) { if (msg_len == 0) break; if (msg_len < 4) { logD_ (_func, "WARNING: discarding ", msg_len, " bytes"); break; } while (page->data_len <= msg_offs) { msg_offs -= page->data_len; page = page->getNextMsgPage(); } PagePool::PageListArray pl_arr (page, msg_offs, msg_len); Byte nal_len_buf [4]; pl_arr.get (/*offset=*/ 0, Memory::forObject (nal_len_buf)); Uint32 const nal_len = readBe32 (nal_len_buf); if (msg_offs + 4 < msg_offs) { logD_ (_func, "WARNING: integer overflow, msg_offs ", msg_offs); break; } msg_offs += 4; while (page->data_len <= msg_offs) { msg_offs -= page->data_len; page = page->getNextMsgPage(); } msg_len -= 4; if (nal_len > msg_len) { logD_ (_func, "WARNING: invalid nal_len ", nal_len, ", msg_len ", msg_len); break; } msg_len -= nal_len; { assert (page->data_len > msg_offs); Byte const nal_type = page->getData() [msg_offs] & 0x1f; Size const nal_csum = calculateChecksumPages (page, msg_offs, nal_len); logD_ (_func, "NAL unit: nal_type ", nal_type, ", nal_len ", nal_len, ", nal_csum 0x", fmt_hex, nal_csum); } if (msg_offs + nal_len < msg_offs) { logD_ (_func, "WARNING: integer overflow, msg_offs ", msg_offs, ", nal_len ", nal_len); break; } msg_offs += nal_len; } }
void NativeAsyncFile::pollable_processEvents (Uint32 const event_flags, void * const _self) { NativeAsyncFile * const self = static_cast <NativeAsyncFile*> (_self); if (event_flags & PollGroup::Hup) logD_ (_self_func, "Hup"); if (event_flags & PollGroup::Output) { if (self->output_frontend && self->output_frontend->processOutput) self->output_frontend.call (self->output_frontend->processOutput); } if (event_flags & PollGroup::Input || event_flags & PollGroup::Hup) { if (self->input_frontend && self->input_frontend->processInput) self->input_frontend.call (self->input_frontend->processInput); } if (event_flags & PollGroup::Error) { logD_ (_func, "0x", fmt_hex, (UintPtr) self, " Error"); if (self->input_frontend && self->input_frontend->processError) { // TODO getsockopt SO_ERROR + fill PosixException IoException io_exc; self->input_frontend.call (self->input_frontend->processError, /*(*/ &io_exc /*)*/); } } if (!(event_flags & PollGroup::Input) && !(event_flags & PollGroup::Output) && !(event_flags & PollGroup::Error) && !(event_flags & PollGroup::Hup)) { logD_ (_func, "0x", fmt_hex, (UintPtr) self, " No events"); return; } }
mt_mutex (mutex) void StreamManager::removeStream_locked (StreamKey const &stream_key) { Ref<StreamEntry> const stream_entry = stream_key.weak_stream_entry.getRef (); if (!stream_entry) return; if (!stream_entry->valid) { return; } stream_entry->valid = false; logD_ (_func, "name: ", stream_entry->entry_key.getKey(), ", " "stream 0x", fmt_hex, (UintPtr) stream_entry->stream.ptr()); StreamHash::EntryKey const hash_key = stream_entry->entry_key; StreamHashEntry * const hash_entry = *hash_key.getDataPtr(); hash_entry->stream_list.remove (stream_entry->list_el); if (hash_entry->stream_list.isEmpty()) { logD_ (_func, "last stream ", hash_key.getKey()); stream_hash.remove (hash_key); } }
void EventService::senderClosed (Exception * const exc_, void * const _session) { Session * const session = static_cast <Session*> (_session); Ref<EventService> const self = session->weak_event_service.getRef (); if (!self) return; logD_ (_func, "session 0x", fmt_hex, (UintPtr) _session, ": ", (exc_ ? ConstMemory (exc_->toString()->mem()) : ConstMemory())); self->mutex.lock (); self->destroySession (session); self->mutex.unlock (); }
void FetchAgent::reconnectTimerTick (void * const _self) { FetchAgent * const self = static_cast <FetchAgent*> (_self); logD_ (_self_func_); self->mutex.lock (); assert (self->reconnect_timer); self->timers->deleteTimer (self->reconnect_timer); self->reconnect_timer = NULL; self->mutex.unlock (); self->startNewSession (); }
void libMaryInit () { { static bool initialized = false; if (initialized) { return; } initialized = true; } // Setting numeric locale for snprintf() to behave uniformly in all cases. // Specifically, we need dot ('.') to be used as a decimal separator. if (setlocale (LC_NUMERIC, "C") == NULL) fprintf (stderr, "WARNING: Could not set LC_NUMERIC locale to \"C\"\n"); #ifndef LIBMARY_PLATFORM_WIN32 // GStreamer calls setlocale(LC_ALL, ""), which is lame. We fight this with setenv(). if (setenv ("LC_NUMERIC", "C", 1 /* overwrite */) == -1) perror ("WARNING: Could not set LC_NUMERIC environment variable to \"C\""); #endif #ifdef LIBMARY_MT_SAFE #ifdef LIBMARY__OLD_GTHREAD_API if (!g_thread_get_initialized ()) g_thread_init (NULL); #endif #endif _libMary_stat = new Stat; libMary_threadLocalInit (); libMary_platformInit (); // log*() logging is now available. if (!updateTime ()) logE_ (_func, exc->toString()); #ifdef LIBMARY_ENABLE_MWRITEV libMary_mwritevInit (); #endif randomSetSeed ((Uint32) getTime()); #ifdef LIBMARY_PLATFORM_WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); int res = WSAStartup(wVersionRequested, &wsaData); if (res != 0) { logE_ (_func_, "WSAStartup failed"); } else { if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { logE_ (_func_, "Could not find a requested version of Winsock.dll"); WSACleanup(); } else { logD_ (_func_, "The Winsock 2.2 dll was found"); } } #endif }
mt_sync_domain (readTask) bool GetFileSession::readTask (void * const _self) { GetFileSession * const self = static_cast <GetFileSession*> (_self); logD (getfile, _func_); if (self->session_state == SessionState_Header) { MOMENT_SERVER__HEADERS_DATE for (;;) { MediaReader::ReadFrameResult const res = self->media_reader->readMoreData (&read_frame_backend, self); if (res == MediaReader::ReadFrameResult_Failure) { logE_ (_func, "ReadFrameResult_Failure"); ConstMemory msg = "Data retrieval error"; self->sender->send (self->page_pool, true /* do_flush */, // TODO No cache MOMENT_SERVER__500_HEADERS (msg.len()), "\r\n", msg); if (!self->req_is_keepalive) self->sender->closeAfterFlush (); logA_ ("mod_nvr 500 ", self->req_client_addr, " ", self->req_request_line); return false /* do not reschedule */; } bool header_done = false; if (res == MediaReader::ReadFrameResult_NoData) { logD (getfile, _func, "ReadFrameResult_NoData"); if (!self->got_last_audio_ts && !self->got_last_video_ts) { ConstMemory msg = "Requested video data not found"; self->sender->send (self->page_pool, true /* do_flush */, // TODO No cache MOMENT_SERVER__404_HEADERS (msg.len()), "\r\n", msg); if (!self->req_is_keepalive) self->sender->closeAfterFlush (); logA_ ("mod_nvr 404 ", self->req_client_addr, " ", self->req_request_line); return false /* do not reschedule */; } header_done = true; } else if (res == MediaReader::ReadFrameResult_Finish) { logD (getfile, _func, "ReadFrameResult_Finish"); header_done = true; } if (header_done) { self->session_state = SessionState_Data; self->media_reader->reset (); break; } assert (res != MediaReader::ReadFrameResult_BurstLimit); assert (res == MediaReader::ReadFrameResult_Success); } PagePool::PageListInfo const mp4_header = self->mp4_muxer.pass1_complete (self->duration_sec * 1000); { self->sender->send (self->page_pool, true /* do_flush */, // TODO No cache MOMENT_SERVER__OK_HEADERS ( (!self->octet_stream_mime ? ConstMemory ("video/mp4") : ConstMemory ("application/octet-stream")), mp4_header.data_len + self->mp4_muxer.getTotalDataSize()), "\r\n"); logD_ (_func, "CONTENT-LENGTH: ", mp4_header.data_len + self->mp4_muxer.getTotalDataSize()); if (!self->req_is_keepalive) self->sender->closeAfterFlush (); logA_ ("mod_nvr 200 ", self->req_client_addr, " ", self->req_request_line); } { SenderMessageEntry_Pages * const msg_pages = SenderMessageEntry_Pages::createNew (/*header_len=*/ 0); msg_pages->init (mp4_header.first, self->page_pool, /*msg_offs=*/ 0, mp4_header.data_len); self->sender->sendMessage (msg_pages, true /* do_flush */); } self->transfer_start_time_millisec = getTimeMilliseconds(); self->bytes_transferred += mp4_header.data_len; self->sender->getEventInformer()->subscribe ( CbDesc<Sender::Frontend> (&sender_frontend, self, self)); }
void VodSource::setTimelapse (Time const /* timelapse_frame_interval_millisec */, Uint32 const /* timelapse_frames_per_second */) { logD_ (_this_func, "timelapse is not supported by this kind of VodSource"); }
mt_throws Result NativeAsyncFile::open (ConstMemory const filename, Uint32 const open_flags, FileAccessMode const access_mode) { int flags = 0; switch ((FileAccessMode::Value) access_mode) { case FileAccessMode::ReadOnly: flags = O_RDONLY; break; case FileAccessMode::WriteOnly: flags = O_WRONLY; break; case FileAccessMode::ReadWrite: flags = O_RDWR; break; default: unreachable (); } if (open_flags && FileOpenFlags::Create) flags |= O_CREAT; // TODO Specify behavior for Truncate & O_RDONLY combination. if (open_flags & FileOpenFlags::Truncate) flags |= O_TRUNC; // TODO Seek to the end of file instead. O_APPEND semantics is too complicated. if (open_flags & FileOpenFlags::Append) flags |= O_APPEND; StRef<String> const filename_str = st_grab (new (std::nothrow) String (filename)); for (;;) { /* NOTE: man 2 open does not mention EINTR as a possible return * value, while man 3 open _does_. This means that EINTR should * be handled for all invocations of open() in MyCpp (and all * over MyNC). */ fd = ::open (filename_str->cstr(), // Note that O_DIRECT affects kernel-level caching/buffering // and should not be set here. flags | O_NONBLOCK, S_IRUSR | S_IWUSR); if (fd == -1) { if (errno == EINTR) continue; if (errno == EAGAIN) { logD_ (_func, "EAGAIN"); break; } if (errno == EWOULDBLOCK) { logD_ (_func, "EWOULDBLOCK"); break; } exc_throw (PosixException, errno); exc_push_ (IoException); return Result::Failure; } break; } return Result::Success; }
mt_mutex (mutex) StreamManager::StreamKey StreamManager::addStream_locked (Stream * const stream, ConstMemory const path, bool const fire_stream_added) { logD_ (_func, "name: ", path, ", stream 0x", fmt_hex, (UintPtr) stream); StreamEntry * const stream_entry = new (std::nothrow) StreamEntry (NULL /* embed_container */, stream); assert (stream_entry); { StreamHash::EntryKey const entry_key = stream_hash.lookup (path); if (entry_key) { stream_entry->entry_key = entry_key; StRef<StreamHashEntry> * const hash_entry = entry_key.getDataPtr(); assert (!(*hash_entry)->stream_list.isEmpty()); bool stream_added = false; Stream * const old_stream = (*hash_entry)->stream_list.getFirst()->stream; Stream::MomentServerData * const old_stream_data = &old_stream->moment_data; if (old_stream_data->stream_info) { StreamInfo * const old_stream_info = static_cast <StreamInfo*> (old_stream_data->stream_info.ptr()); // If the previous stream is waiting for streamer, // then bind the previous stream to the new one. logD_ (_func, "binding old_stream 0x", fmt_hex, (UintPtr) old_stream); if (old_stream_info->waiting_for_streamer) { old_stream_info->waiting_for_streamer = false; logD_ (_func, "waiting_for_streamer complete"); old_stream->setPublishingInProgress (true); old_stream->bindToStream (stream /* bind_audio_stream */, stream /* bind_video_stream */, true /* bind_audio */, true /* bind_video */, true /* propagate_close_audio */, true /* propagate_close_video */); stream_entry->list_el = (*hash_entry)->stream_list.prepend (stream_entry); stream_added = true; } } if (!stream_added) { if (new_streams_on_top) { (*hash_entry)->stream_list.getFirst()->displaced = true; notifyDeferred_StreamClosed ((*hash_entry)->stream_list.getFirst()->stream); stream_entry->list_el = (*hash_entry)->stream_list.prepend (stream_entry); } else { stream_entry->list_el = (*hash_entry)->stream_list.append (stream_entry); } } } else { StRef<StreamHashEntry> const hash_entry = st_grab (new (std::nothrow) StreamHashEntry); stream_entry->list_el = hash_entry->stream_list.append (stream_entry); stream_entry->entry_key = stream_hash.add (path, hash_entry); } } if (fire_stream_added) { notifyDeferred_StreamAdded (stream, path); if (event_service) { StRef<String> esc_path; event_service->sendEvent (makeString ("stream add ", lineEscape (path, &esc_path))); } } return StreamKey (stream_entry); }