[vlc-commits] chromecast: Split each message namespace in its own method
Hugo Beauzée-Luyssen
git at videolan.org
Tue Feb 21 14:00:57 CET 2017
vlc | branch: master | Hugo Beauzée-Luyssen <hugo at beauzee.fr> | Fri Feb 17 16:58:07 2017 +0100| [03cb1b720970ba0724f582ac87c012d8904cfa7a] | committer: Hugo Beauzée-Luyssen
chromecast: Split each message namespace in its own method
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=03cb1b720970ba0724f582ac87c012d8904cfa7a
---
modules/stream_out/chromecast/chromecast.h | 5 +
modules/stream_out/chromecast/chromecast_ctrl.cpp | 525 +++++++++++-----------
2 files changed, 277 insertions(+), 253 deletions(-)
diff --git a/modules/stream_out/chromecast/chromecast.h b/modules/stream_out/chromecast/chromecast.h
index 5f1df17..4ff504c 100644
--- a/modules/stream_out/chromecast/chromecast.h
+++ b/modules/stream_out/chromecast/chromecast.h
@@ -180,6 +180,11 @@ private:
double getPlaybackPosition() const;
void mainLoop();
+ void processAuthMessage( const castchannel::CastMessage& msg );
+ void processHeartBeatMessage( const castchannel::CastMessage& msg );
+ void processReceiverMessage( const castchannel::CastMessage& msg );
+ void processMediaMessage( const castchannel::CastMessage& msg );
+ void processConnectionMessage( const castchannel::CastMessage& msg );
private:
static void* ChromecastThread(void* p_data);
diff --git a/modules/stream_out/chromecast/chromecast_ctrl.cpp b/modules/stream_out/chromecast/chromecast_ctrl.cpp
index da32488..084355d 100644
--- a/modules/stream_out/chromecast/chromecast_ctrl.cpp
+++ b/modules/stream_out/chromecast/chromecast_ctrl.cpp
@@ -175,320 +175,339 @@ void intf_sys_t::processMessage(const castchannel::CastMessage &msg)
#endif
if (namespace_ == NAMESPACE_DEVICEAUTH)
+ processAuthMessage( msg );
+ else if (namespace_ == NAMESPACE_HEARTBEAT)
+ processHeartBeatMessage( msg );
+ else if (namespace_ == NAMESPACE_RECEIVER)
+ processReceiverMessage( msg );
+ else if (namespace_ == NAMESPACE_MEDIA)
+ processMediaMessage( msg );
+ else if (namespace_ == NAMESPACE_CONNECTION)
+ processConnectionMessage( msg );
+ else
{
- castchannel::DeviceAuthMessage authMessage;
- authMessage.ParseFromString(msg.payload_binary());
+ msg_Err( m_module, "Unknown namespace: %s", msg.namespace_().c_str());
+ }
+}
- if (authMessage.has_error())
- {
- msg_Err( m_module, "Authentification error: %d", authMessage.error().error_type());
- }
- else if (!authMessage.has_response())
- {
- msg_Err( m_module, "Authentification message has no response field");
- }
- else
- {
- vlc_mutex_locker locker(&m_lock);
- setConnectionStatus(CHROMECAST_AUTHENTICATED);
- m_communication.msgConnect(DEFAULT_CHOMECAST_RECEIVER);
- m_communication.msgReceiverGetStatus();
- }
+
+
+/*****************************************************************************
+ * Chromecast thread
+ *****************************************************************************/
+void* intf_sys_t::ChromecastThread(void* p_data)
+{
+ intf_sys_t *p_sys = reinterpret_cast<intf_sys_t*>(p_data);
+ p_sys->mainLoop();
+ return NULL;
+}
+
+void intf_sys_t::mainLoop()
+{
+ vlc_interrupt_set( m_ctl_thread_interrupt );
+
+ vlc_mutex_lock(&m_lock);
+ setConnectionStatus(CHROMECAST_TLS_CONNECTED);
+ vlc_mutex_unlock(&m_lock);
+
+ m_communication.msgAuth();
+
+ while ( !vlc_killed() && handleMessages() )
+ ;
+}
+
+void intf_sys_t::processAuthMessage( const castchannel::CastMessage& msg )
+{
+ castchannel::DeviceAuthMessage authMessage;
+ if ( authMessage.ParseFromString(msg.payload_binary()) == false )
+ {
+ msg_Warn( m_module, "Failed to parse the payload" );
+ return;
}
- else if (namespace_ == NAMESPACE_HEARTBEAT)
+
+ if (authMessage.has_error())
+ {
+ msg_Err( m_module, "Authentification error: %d", authMessage.error().error_type());
+ }
+ else if (!authMessage.has_response())
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str());
- std::string type((*p_data)["type"]);
+ msg_Err( m_module, "Authentification message has no response field");
+ }
+ else
+ {
+ vlc_mutex_locker locker(&m_lock);
+ setConnectionStatus(CHROMECAST_AUTHENTICATED);
+ m_communication.msgConnect(DEFAULT_CHOMECAST_RECEIVER);
+ m_communication.msgReceiverGetStatus();
+ }
+}
- if (type == "PING")
- {
- msg_Dbg( m_module, "PING received from the Chromecast");
- m_communication.msgPong();
- }
- else if (type == "PONG")
- {
- msg_Dbg( m_module, "PONG received from the Chromecast");
- }
- else
- {
- msg_Warn( m_module, "Heartbeat command not supported: %s", type.c_str());
- }
+void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage& msg )
+{
+ json_value *p_data = json_parse(msg.payload_utf8().c_str());
+ std::string type((*p_data)["type"]);
- json_value_free(p_data);
+ if (type == "PING")
+ {
+ msg_Dbg( m_module, "PING received from the Chromecast");
+ m_communication.msgPong();
}
- else if (namespace_ == NAMESPACE_RECEIVER)
+ else if (type == "PONG")
+ {
+ msg_Dbg( m_module, "PONG received from the Chromecast");
+ }
+ else
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str());
- std::string type((*p_data)["type"]);
+ msg_Warn( m_module, "Heartbeat command not supported: %s", type.c_str());
+ }
- if (type == "RECEIVER_STATUS")
- {
- json_value applications = (*p_data)["status"]["applications"];
- const json_value *p_app = NULL;
+ json_value_free(p_data);
+}
- vlc_mutex_locker locker(&m_lock);
- for (unsigned i = 0; i < applications.u.array.length; ++i)
- {
- std::string appId(applications[i]["appId"]);
- if (appId == APP_ID)
- {
- const char *pz_transportId = applications[i]["transportId"];
- if (pz_transportId != NULL)
- {
- m_appTransportId = std::string(pz_transportId);
- p_app = &applications[i];
- }
- break;
- }
- }
+void intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
+{
+ json_value *p_data = json_parse(msg.payload_utf8().c_str());
+ std::string type((*p_data)["type"]);
- if ( p_app )
- {
- if (!m_appTransportId.empty()
- && m_conn_status == CHROMECAST_AUTHENTICATED)
- {
- m_communication.msgConnect( m_appTransportId );
- setPlayerStatus(NO_CMD_PENDING);
- setConnectionStatus(CHROMECAST_APP_STARTED);
- }
- }
- else
+ if (type == "RECEIVER_STATUS")
+ {
+ json_value applications = (*p_data)["status"]["applications"];
+ const json_value *p_app = NULL;
+
+ vlc_mutex_locker locker(&m_lock);
+ for (unsigned i = 0; i < applications.u.array.length; ++i)
+ {
+ std::string appId(applications[i]["appId"]);
+ if (appId == APP_ID)
{
- switch( m_conn_status )
+ const char *pz_transportId = applications[i]["transportId"];
+ if (pz_transportId != NULL)
{
- /* If the app is no longer present */
- case CHROMECAST_APP_STARTED:
- msg_Warn( m_module, "app is no longer present. closing");
- m_communication.msgReceiverClose(m_appTransportId);
- setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
- break;
-
- case CHROMECAST_AUTHENTICATED:
- msg_Dbg( m_module, "Chromecast was running no app, launch media_app");
- m_appTransportId = "";
- m_mediaSessionId = ""; // this session is not valid anymore
- m_receiverState = RECEIVER_IDLE;
- m_communication.msgReceiverLaunchApp();
- break;
-
- default:
- break;
+ m_appTransportId = std::string(pz_transportId);
+ p_app = &applications[i];
}
-
+ break;
}
}
- else if (type == "LAUNCH_ERROR")
+
+ if ( p_app )
{
- json_value reason = (*p_data)["reason"];
- msg_Err( m_module, "Failed to start the MediaPlayer: %s",
- (const char *)reason);
+ if (!m_appTransportId.empty()
+ && m_conn_status == CHROMECAST_AUTHENTICATED)
+ {
+ m_communication.msgConnect( m_appTransportId );
+ setPlayerStatus(NO_CMD_PENDING);
+ setConnectionStatus(CHROMECAST_APP_STARTED);
+ }
}
else
{
- msg_Warn( m_module, "Receiver command not supported: %s",
- msg.payload_utf8().c_str());
- }
+ switch( m_conn_status )
+ {
+ /* If the app is no longer present */
+ case CHROMECAST_APP_STARTED:
+ msg_Warn( m_module, "app is no longer present. closing");
+ m_communication.msgReceiverClose(m_appTransportId);
+ setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
+ break;
+
+ case CHROMECAST_AUTHENTICATED:
+ msg_Dbg( m_module, "Chromecast was running no app, launch media_app");
+ m_appTransportId = "";
+ m_mediaSessionId = ""; // this session is not valid anymore
+ m_receiverState = RECEIVER_IDLE;
+ m_communication.msgReceiverLaunchApp();
+ break;
+
+ default:
+ break;
+ }
- json_value_free(p_data);
+ }
}
- else if (namespace_ == NAMESPACE_MEDIA)
+ else if (type == "LAUNCH_ERROR")
+ {
+ json_value reason = (*p_data)["reason"];
+ msg_Err( m_module, "Failed to start the MediaPlayer: %s",
+ (const char *)reason);
+ }
+ else
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str());
- std::string type((*p_data)["type"]);
+ msg_Warn( m_module, "Receiver command not supported: %s",
+ msg.payload_utf8().c_str());
+ }
- if (type == "MEDIA_STATUS")
- {
- json_value status = (*p_data)["status"];
- msg_Dbg( m_module, "Player state: %s sessionId:%d",
- status[0]["playerState"].operator const char *(),
- (int)(json_int_t) status[0]["mediaSessionId"]);
+ json_value_free(p_data);
+}
- vlc_mutex_locker locker(&m_lock);
- receiver_state oldPlayerState = m_receiverState;
- std::string newPlayerState = status[0]["playerState"].operator const char *();
- std::string idleReason = status[0]["idleReason"].operator const char *();
+void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
+{
+ json_value *p_data = json_parse(msg.payload_utf8().c_str());
+ std::string type((*p_data)["type"]);
- if (newPlayerState == "IDLE")
- m_receiverState = RECEIVER_IDLE;
- else if (newPlayerState == "PLAYING")
- m_receiverState = RECEIVER_PLAYING;
- else if (newPlayerState == "BUFFERING")
- m_receiverState = RECEIVER_BUFFERING;
- else if (newPlayerState == "PAUSED")
- m_receiverState = RECEIVER_PAUSED;
- else if (!newPlayerState.empty())
- msg_Warn( m_module, "Unknown Chromecast state %s", newPlayerState.c_str());
-
- if (m_receiverState == RECEIVER_IDLE)
+ if (type == "MEDIA_STATUS")
+ {
+ json_value status = (*p_data)["status"];
+ msg_Dbg( m_module, "Player state: %s sessionId:%d",
+ status[0]["playerState"].operator const char *(),
+ (int)(json_int_t) status[0]["mediaSessionId"]);
+
+ vlc_mutex_locker locker(&m_lock);
+ receiver_state oldPlayerState = m_receiverState;
+ std::string newPlayerState = status[0]["playerState"].operator const char *();
+ std::string idleReason = status[0]["idleReason"].operator const char *();
+
+ if (newPlayerState == "IDLE")
+ m_receiverState = RECEIVER_IDLE;
+ else if (newPlayerState == "PLAYING")
+ m_receiverState = RECEIVER_PLAYING;
+ else if (newPlayerState == "BUFFERING")
+ m_receiverState = RECEIVER_BUFFERING;
+ else if (newPlayerState == "PAUSED")
+ m_receiverState = RECEIVER_PAUSED;
+ else if (!newPlayerState.empty())
+ msg_Warn( m_module, "Unknown Chromecast state %s", newPlayerState.c_str());
+
+ if (m_receiverState == RECEIVER_IDLE)
+ {
+ m_mediaSessionId = ""; // this session is not valid anymore
+ }
+ else
+ {
+ char session_id[32];
+ if( snprintf( session_id, sizeof(session_id), "%" PRId64, (json_int_t) status[0]["mediaSessionId"] ) >= (int)sizeof(session_id) )
{
- m_mediaSessionId = ""; // this session is not valid anymore
+ msg_Err( m_module, "snprintf() truncated string for mediaSessionId" );
+ session_id[sizeof(session_id) - 1] = '\0';
}
- else
- {
- char session_id[32];
- if( snprintf( session_id, sizeof(session_id), "%" PRId64, (json_int_t) status[0]["mediaSessionId"] ) >= (int)sizeof(session_id) )
- {
- msg_Err( m_module, "snprintf() truncated string for mediaSessionId" );
- session_id[sizeof(session_id) - 1] = '\0';
- }
- if (session_id[0] && m_mediaSessionId != session_id) {
- if (!m_mediaSessionId.empty())
- msg_Warn( m_module, "different mediaSessionId detected %s was %s", session_id, this->m_mediaSessionId.c_str());
- m_mediaSessionId = session_id;
- }
+ if (session_id[0] && m_mediaSessionId != session_id) {
+ if (!m_mediaSessionId.empty())
+ msg_Warn( m_module, "different mediaSessionId detected %s was %s", session_id, this->m_mediaSessionId.c_str());
+ m_mediaSessionId = session_id;
}
+ }
- if (m_receiverState != oldPlayerState)
- {
+ if (m_receiverState != oldPlayerState)
+ {
#ifndef NDEBUG
- msg_Dbg( m_module, "change Chromecast player state from %d to %d", oldPlayerState, m_receiverState );
+ msg_Dbg( m_module, "change Chromecast player state from %d to %d", oldPlayerState, m_receiverState );
#endif
- switch( m_receiverState )
+ switch( m_receiverState )
+ {
+ case RECEIVER_BUFFERING:
+ if ( double(status[0]["currentTime"]) == 0.0 )
{
- case RECEIVER_BUFFERING:
- if ( double(status[0]["currentTime"]) == 0.0 )
- {
- m_receiverState = oldPlayerState;
- msg_Dbg( m_module, "Invalid buffering time, keep previous state %d", oldPlayerState);
- }
- else
- {
- m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
- msg_Dbg( m_module, "Playback pending with an offset of %" PRId64, m_chromecast_start_time);
- }
- m_time_playback_started = VLC_TS_INVALID;
- break;
-
- case RECEIVER_PLAYING:
- /* TODO reset demux PCR ? */
- if (unlikely(m_chromecast_start_time == VLC_TS_INVALID)) {
- msg_Warn( m_module, "start playing without buffering" );
- m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
- }
- setPlayerStatus(CMD_PLAYBACK_SENT);
- m_time_playback_started = mdate();
+ m_receiverState = oldPlayerState;
+ msg_Dbg( m_module, "Invalid buffering time, keep previous state %d", oldPlayerState);
+ }
+ else
+ {
+ m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
+ msg_Dbg( m_module, "Playback pending with an offset of %" PRId64, m_chromecast_start_time);
+ }
+ m_time_playback_started = VLC_TS_INVALID;
+ break;
+
+ case RECEIVER_PLAYING:
+ /* TODO reset demux PCR ? */
+ if (unlikely(m_chromecast_start_time == VLC_TS_INVALID)) {
+ msg_Warn( m_module, "start playing without buffering" );
+ m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
+ }
+ setPlayerStatus(CMD_PLAYBACK_SENT);
+ m_time_playback_started = mdate();
#ifndef NDEBUG
- msg_Dbg( m_module, "Playback started with an offset of %" PRId64 " now:%" PRId64 " i_ts_local_start:%" PRId64, m_chromecast_start_time, m_time_playback_started, m_ts_local_start);
+ msg_Dbg( m_module, "Playback started with an offset of %" PRId64 " now:%" PRId64 " i_ts_local_start:%" PRId64, m_chromecast_start_time, m_time_playback_started, m_ts_local_start);
#endif
- break;
+ break;
- case RECEIVER_PAUSED:
- m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
+ case RECEIVER_PAUSED:
+ m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
#ifndef NDEBUG
- msg_Dbg( m_module, "Playback paused with an offset of %" PRId64 " date_play_start:%" PRId64, m_chromecast_start_time, m_time_playback_started);
+ msg_Dbg( m_module, "Playback paused with an offset of %" PRId64 " date_play_start:%" PRId64, m_chromecast_start_time, m_time_playback_started);
#endif
- if ( m_time_playback_started != VLC_TS_INVALID && oldPlayerState == RECEIVER_PLAYING )
- {
- /* this is a pause generated remotely, adjust the playback time */
- m_ts_local_start += mdate() - m_time_playback_started;
+ if ( m_time_playback_started != VLC_TS_INVALID && oldPlayerState == RECEIVER_PLAYING )
+ {
+ /* this is a pause generated remotely, adjust the playback time */
+ m_ts_local_start += mdate() - m_time_playback_started;
#ifndef NDEBUG
- msg_Dbg( m_module, "updated i_ts_local_start:%" PRId64, m_ts_local_start);
+ msg_Dbg( m_module, "updated i_ts_local_start:%" PRId64, m_ts_local_start);
#endif
- }
- m_time_playback_started = VLC_TS_INVALID;
- break;
-
- case RECEIVER_IDLE:
- if ( m_has_input )
- setPlayerStatus(NO_CMD_PENDING);
- m_time_playback_started = VLC_TS_INVALID;
- break;
}
- }
-
- if (m_receiverState == RECEIVER_BUFFERING && m_seek_request_time != VLC_TS_INVALID)
- {
- msg_Dbg( m_module, "Chromecast seeking possibly done");
- vlc_cond_signal( &m_seekCommandCond );
- }
+ m_time_playback_started = VLC_TS_INVALID;
+ break;
- if ( (m_cmd_status != CMD_LOAD_SENT || idleReason == "CANCELLED") && m_receiverState == RECEIVER_IDLE && m_has_input )
- {
- msg_Dbg( m_module, "the device missed the LOAD command");
- m_ts_local_start = VLC_TS_0;
- m_communication.msgPlayerLoad( m_appTransportId, m_streaming_port, m_title, m_artwork, m_mime );
- setPlayerStatus(CMD_LOAD_SENT);
+ case RECEIVER_IDLE:
+ if ( m_has_input )
+ setPlayerStatus(NO_CMD_PENDING);
+ m_time_playback_started = VLC_TS_INVALID;
+ break;
}
}
- else if (type == "LOAD_FAILED")
- {
- msg_Err( m_module, "Media load failed");
- vlc_mutex_locker locker(&m_lock);
- /* close the app to restart it */
- if ( m_conn_status == CHROMECAST_APP_STARTED )
- m_communication.msgReceiverClose(m_appTransportId);
- else
- m_communication.msgReceiverGetStatus();
- }
- else if (type == "LOAD_CANCELLED")
- {
- msg_Dbg( m_module, "LOAD canceled by another command");
- }
- else if (type == "INVALID_REQUEST")
+
+ if (m_receiverState == RECEIVER_BUFFERING && m_seek_request_time != VLC_TS_INVALID)
{
- msg_Dbg( m_module, "We sent an invalid request reason:%s", (*p_data)["reason"].operator const char *());
+ msg_Dbg( m_module, "Chromecast seeking possibly done");
+ vlc_cond_signal( &m_seekCommandCond );
}
- else
+
+ if ( (m_cmd_status != CMD_LOAD_SENT || idleReason == "CANCELLED") && m_receiverState == RECEIVER_IDLE && m_has_input )
{
- msg_Warn( m_module, "Media command not supported: %s",
- msg.payload_utf8().c_str());
+ msg_Dbg( m_module, "the device missed the LOAD command");
+ m_ts_local_start = VLC_TS_0;
+ m_communication.msgPlayerLoad( m_appTransportId, m_streaming_port, m_title, m_artwork, m_mime );
+ setPlayerStatus(CMD_LOAD_SENT);
}
-
- json_value_free(p_data);
}
- else if (namespace_ == NAMESPACE_CONNECTION)
+ else if (type == "LOAD_FAILED")
{
- json_value *p_data = json_parse(msg.payload_utf8().c_str());
- std::string type((*p_data)["type"]);
- json_value_free(p_data);
-
- if (type == "CLOSE")
- {
- msg_Warn( m_module, "received close message");
- setHasInput( false );
- vlc_mutex_locker locker(&m_lock);
- setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
- // make sure we unblock the demuxer
- m_seek_request_time = VLC_TS_INVALID;
- vlc_cond_signal(&m_seekCommandCond);
- }
+ msg_Err( m_module, "Media load failed");
+ vlc_mutex_locker locker(&m_lock);
+ /* close the app to restart it */
+ if ( m_conn_status == CHROMECAST_APP_STARTED )
+ m_communication.msgReceiverClose(m_appTransportId);
else
- {
- msg_Warn( m_module, "Connection command not supported: %s",
- type.c_str());
- }
+ m_communication.msgReceiverGetStatus();
+ }
+ else if (type == "LOAD_CANCELLED")
+ {
+ msg_Dbg( m_module, "LOAD canceled by another command");
+ }
+ else if (type == "INVALID_REQUEST")
+ {
+ msg_Dbg( m_module, "We sent an invalid request reason:%s", (*p_data)["reason"].operator const char *());
}
else
{
- msg_Err( m_module, "Unknown namespace: %s", msg.namespace_().c_str());
+ msg_Warn( m_module, "Media command not supported: %s",
+ msg.payload_utf8().c_str());
}
-}
-
-
-/*****************************************************************************
- * Chromecast thread
- *****************************************************************************/
-void* intf_sys_t::ChromecastThread(void* p_data)
-{
- intf_sys_t *p_sys = reinterpret_cast<intf_sys_t*>(p_data);
- p_sys->mainLoop();
- return NULL;
+ json_value_free(p_data);
}
-void intf_sys_t::mainLoop()
+void intf_sys_t::processConnectionMessage( const castchannel::CastMessage& msg )
{
- vlc_interrupt_set( m_ctl_thread_interrupt );
-
- vlc_mutex_lock(&m_lock);
- setConnectionStatus(CHROMECAST_TLS_CONNECTED);
- vlc_mutex_unlock(&m_lock);
-
- m_communication.msgAuth();
+ json_value *p_data = json_parse(msg.payload_utf8().c_str());
+ std::string type((*p_data)["type"]);
+ json_value_free(p_data);
- while ( !vlc_killed() && handleMessages() )
- ;
+ if (type == "CLOSE")
+ {
+ msg_Warn( m_module, "received close message");
+ setHasInput( false );
+ vlc_mutex_locker locker(&m_lock);
+ setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
+ // make sure we unblock the demuxer
+ m_seek_request_time = VLC_TS_INVALID;
+ vlc_cond_signal(&m_seekCommandCond);
+ }
+ else
+ {
+ msg_Warn( m_module, "Connection command not supported: %s",
+ type.c_str());
+ }
}
bool intf_sys_t::handleMessages()
More information about the vlc-commits
mailing list