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
}
Beispiel #2
0
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);
}
Beispiel #3
0
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);
    }
}
Beispiel #6
0
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 ();
}
Beispiel #7
0
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 ();
}
Beispiel #8
0
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));
    }
Beispiel #10
0
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;
}
Beispiel #12
0
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);
}