Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

enum SegmentAlignment {
  ALIGNMENT_UNDEFINED = 0;
ALIGNMENT_NAL = 1;
 ALIGNMENT_AU
= 2;
}

enum CipherMode {
  CIPHER_MODE_UNDEFINED = 0;
 CIPHER_MODE_CENC     = 1; /* AES-CTR scheme */
 CIPHER_MODE_CBC1     = 2; /* AES-CBC scheme */
 CIPHER_MODE_CENS     = 3; /* AES-CTR subsample pattern encryption scheme */
 CIPHER_MODE_CBCS     = 4; /* AES-CBC subsample pattern encryption scheme */
}

message MediaSegmentMetadata {
     required optional uint32                 length               = 1;             /* Number of bytes in sample */
     required optional sint64                 time_position        = 2;             /* Position in stream in nanoseconds */
     required optional sint64                 sample_duration      = 3;             /* Frame/sample duration in nanoseconds */
     required optional uint32                 stream_id            = 4;             /* stream id (unique ID for ES, as defined in attachSource()) */
    optional uint32                 sample_rate          = 5;             /* Samples per second for audio segments */
    optional uint32                 channels_num         = 6;             /* Number of channels for audio segments */
    optional uint32                 width                = 7;             /* Frame width in pixels for video segments */
    optional uint32                 height               = 8;             /* Frame height in pixels for video segments */
    optional SegmentAlignment       segment_alignment    = 9;             /* Segment alignment can be specified for H264/H265, will use NAL if not set */
    optional bytes                  extra_data           = 10;            /* Buffer containing extradata */
    optional bytes                  media_key_session_id = 11;            /* Buffer containing key session ID to use for decryption */
    optional bytes                  key_id               = 12;            /* Buffer containing Key ID to use for decryption */
    optional bytes                  init_vector          = 13;            /* Buffer containing the initialization vector for decryption */
    optional uint32                 init_with_last_15    = 14;            /* initWithLast15 value for decryption */
    optional repeated SubsamplePair sub_sample_info      = 15;            /* If present, use gather/scatter decryption based on this list of clear/encrypted byte lengths. */
                                                                          /* If not present and content is encrypted then entire media segment needs decryption (unless    */
                                                                          /* cipher_mode indicates pattern encryption in which case crypt/skip byte block value specify    */
                                                                          /* the encryption pattern)                                                                       */
    optional bytes                  codec_data           = 16;            /* Buffer containing updated codec data for video segments */
    optional CipherMode             cipher_mode          = 17;            /* Block cipher mode of operation when common encryption used */
    optional uint32                 crypt_byte_block     = 18;            /* Crypt byte block value for CBCS cipher mode pattern */
    optional uint32                 skip_byte_block      = 19;            /* Skip byte block value for CBCS cipher mode pattern */
optional Fraction         frame_rate           = 20;            /* Fractional frame rate of the video segments */
}

message SubsamplePair

{

     optional required uint32_t               num_clear_bytes      = 1;             /* How many of next bytes in sequence are clear */
    optional  required uint32_t               num_encrypted_bytes  = 2;             /* How many of next bytes in sequence are encrypted */

}

...

PlantUML Macro
formatSVG
titleRender Frame
@startuml

autonumber

box "Container" #LightGreen
participant Netflix
participant DPI
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

Netflix            ->  DPI:              renderFrame()
DPI                ->  rialtoClient:     renderFrame()
rialtoClient       ->  rialtoServer:     renderFrame()
opt Frame renderable
rialtoServer       ->  GStreamer_server: Trigger rendering of frame

optrialtoServer Frame rendered successfully

note across: It is a Netflix requirement to call updatePlaybackPosition() after--> rialtoClient:     status=true
else renderFrame() called in bad state
rialtoServer       --> rialtoClient:     notifyPosition(position)status=false
end

rialtoClient       --> DPI:              notifyPosition(position)status
DPI                --> Netflix:          updatePlaybackPosition(pts)

rialtoServer       --> rialtoClient:     status=true
else
rialtoServer       --> rialtoClient:     status=false
end

else renderFrame() called in bad state
rialtoServer       --> rialtoClient:     status=false
end

rialtoClient       --> DPI:              status
DPI                --> Netflix:          status

@enduml

Media data pipeline


@enduml


Media data pipeline

Note that the data pipelines for different data sources (e.g. audio & video) should operate entirely independently. Rialto should

...

PlantUML Macro
formatSVG
titleServer only mode
@startuml

autonumber

box "Platform" #LightBlue
participant client
participant rialtoServer
end box

rialtoServer      -/  client:           notifyNeedMediaData(pipeline_session, sourceId, frameCount, maxBytes, needDataRequestId, shmInfo)
client            ->  client:           Ignore shmInfo
client            --> rialtoServer:

loop While (framesFetched < frameCount) && (addSegment() returns OK)
client            ->  client:           Get next frame & any decryption metadata
client            ->  rialtoServer:     addSegment(needDataRequestId, segment)

opt needDataRequestId is valid && enough space to write segment and its metadata to its metadata to shm region && client not trying to send too many frames
rialtoServer      ->  rialtoServer:     Copy segment & metadata to shm buffer based on shmInfo for this request ID
rialtoServer      --> client:           OK
else Not enough space in shm region &&|| client not trying to send too many frames
rialtoServer      -->  rialtoServerclient:     Copy segment & metadata to shm buffer based on shmInfo for this request IDNO_SPACE
else needDataRequestId not found
rialtoServer      --> client:           OK
else Not enough space in shm region || client trying to send too many frames
rialtoServernote right: Silently ignore calls with invalid request ID as this is possible due to race conditions
end



end

note over client
Set status following same rules as in client/server mode
end note
client            -->  clientrialtoServer:     haveData(pipeline_session, status, needDataRequestId)
note across: From this point NO_SPACE
else needDataRequestId not found
rialtoServer      --> client:           OK
note right: Silently ignore calls with invalid request ID as this is possible due to race conditions
end



end

note over client
Set status following same rules as in client/server mode
end note
client            ->  rialtoServer:     haveData(pipeline_session, status, needDataRequestId)
note across: From this point processing follows the same flow as shown in the client-server diagram.

@enduml
Note

The code for populating the shm buffer from the parameters to addSegment() will be common on the client & server side so this should be stored in a common location to be used by both implementations.

See also Rialto Client MSE Player Session Streaming State Machine for some additional clarity on how the Rialto client should manage the flow of data in particular regard to seek operations.

Rialto Server to Gstreamer server

This algorithm should be run for all attached sources. A haveData() call in the above sequence can restart the algorithm when it previously stopped due to data exhaustion.

processing follows the same flow as shown in the client-server diagram.

@enduml
Note

The code for populating the shm buffer from the parameters to addSegment() will be common on the client & server side so this should be stored in a common location to be used by both implementations.


See also Rialto Client MSE Player Session Streaming State Machine for some additional clarity on how the Rialto client should manage the flow of data in particular regard to seek operations.

Rialto Server to Gstreamer server

This algorithm should be run for all attached sources. A haveData() call in the above sequence can restart the algorithm when it previously stopped due to data exhaustion.


PlantUML Macro
formatSVG
titlePushing data to Gstreamer server pipeline
@startuml

autonumber

box "Platform" #LightGreen
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
participant Ocdm
end box

note across
Gstreamer app source uses 2 signals, need-data and enough-data,
to notify its client whether it needs more data. Rialto server
should only push data when the appsrc indicates that it is in
the need-data state.
end note


loop While appsrc needs data && appsrc data available in shm buffer

rialtoServer       ->  rialtoServer:      Extract frame's metadata from shm
opt Frame encrypted

rialtoServer	   ->  GStreamer_server:  gst_buffer_add_protection_meta(buffer, meta)

end

rialtoServer       ->  GStreamer_server:  Set width/height caps
opt new codec_data in frame
rialtoServer       ->  GStreamer_server:  Set codec_data caps
end

rialtoServer       ->  GStreamer_server:  gst_app_src_push_buffer(src, gst_buffer)
rialtoServer       ->  rialtoServer:      'Remove' frame from shm

opt First video frame pushed at start of playback / after seek
note across: Not currently implemented
PlantUML Macro
formatSVG
titlePushing data to Gstreamer server pipeline
@startuml

autonumber

box "Platform" #LightGreen
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
participant Ocdm
end box

note across
Gstreamer app source uses 2 signals, need-data and enough-data,
to notify its client whether it needs more data. Rialto server
should only push data when the appsrc indicates that it is in
the need-data state.
end note


loop While appsrc needs data && appsrc data available in shm buffer

rialtoServer       ->  rialtoServer:      Extract frame's metadata from shm
opt Frame encrypted

rialtoServer	--/ rialtoClient:      notifyFrameReady(frame_timestamp)
end

opt Appsrc data exhausted from shm
opt (status == EOS) for this appsrc
rialtoServer       ->   GStreamerGStreamer_server:    gst_buffer_add_protection_meta(buffer, meta)

end
  notify EOS
else Not EOS
rialtoServer       ->  GStreamer_server:  Set width/height caps
opt new codec_data in frame
rialtoServer    --/ rialtoClient:      notifyNeedMediaData(...)
end
end

end

@enduml


Frames are decrypted in the pipeline when they are pulled for playback.


PlantUML Macro
formatSVG
titleDecrypt Frames on Gstreamer server pipeline
@startuml

autonumber

box "Platform" #LightGreen
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
participant Ocdm
end box

GStreamer_server   ->  GStreamer_serverrialtoServer:    Set codec_data caps
end decrypt(buffer)

rialtoServer  	     ->  GStreamer GStreamer_server:  gst_appbuffer_srcget_pushprotection_meta(buffer(src, gst_buffer)
)
 
opt Protection Meta exists (Frame encrypted)
 
rialtoServer       ->  rialtoServer:      'Remove'Extract frame's from shmmetadata

opt First video frame pushed at start of playback / after seek
note across: Not currently implemented
rialtoServermedia_keys.key_system == "com.netflix.playready"
rialtoServer       ->  Ocdm:       --/ rialtoClient:      notifyFrameReady(frame_timestampopencdm_select_key_id(ocdm_session, kid)
end


opt AppsrcImplementation databefore exhaustedCBCS fromsupport shm
opt (status == EOS) for this appsrc
rialtoServeradded
rialtoServer       ->  Ocdm:              ->  GStreamer_server:      notify EOS
else Not EOSopencdm_gstreamer_session_decrypt_ex(ocdm_session, gst_buffer, subsample_info, iv, key, init_with_last_15, caps)
else Implementation after CBCS support added
rialtoServer       --/>  rialtoClientOcdm:            notifyNeedMediaData(...)
end   opencdm_gstreamer_session_decrypt_buffer(ocdm_session, gst_buffer, caps)
end

end

@enduml

Playback State

Position Reporting

The position reporting timer should be started whenever the PLAYING state is entered and stopped whenever the session moves to another playback state, i.e. stop polling during IDLE, BUFFERING, SEEKING etc.Frames are decrypted in the pipeline when they are pulled for playback.


PlantUML Macro
formatSVG
titleDecrypt Frames on Gstreamer server pipelinePosition updates
@startuml


autonumber

box "PlatformContainer" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
participant Ocdm
end box

GStreamer_server== Regular position ->update notifications rialtoServer:      decrypt(buffer)

rialtoServer	==

rialtoServer     ->   GStreamer_serverrialtoServer:  gst_buffer_get_protection_meta(buffer)
 
opt Protection MetaPosition exists (Frame encrypted)
 timer fired
rialtoServer       ->  rialtoServerGStreamer_server: Get position from pipeline
GStreamer_server --> rialtoServer: Extract frame's metadata

opt media_keys.key_system == "com.netflix.playready"
rialtoServer   Current position
rialtoServer     ->/  OcdmrialtoClient:     notifyPosition(pipeline_session, position)
rialtoClient     -/    opencdm_select_key_id(ocdmGStreamer_client: notifyPosition(pipeline_session, kidposition)
end


opt Implementation before CBCS support added
rialtoServer       ->  Ocdm:   note over GStreamer_client: Not used by Cobalt as some conformance\ntests require very high position accuracy


== Get position ==

Cobalt           opencdm_gstreamer_session_decrypt_ex(ocdm_session, gst_buffer, subsample_info, iv, key, init_with_last_15, caps)
else Implementation after CBCS support added
rialtoServer->  Starboard:        SbPlayerGetInfo2(player)
Starboard        ->  Ocdm:              opencdm_gstreamer_session_decrypt_buffer(ocdm_session, gst_buffer, caps)
end

end

@enduml

Playback State

Position Reporting

The position reporting timer should be started whenever the PLAYING state is entered and stopped whenever the session moves to another playback state, i.e. stop polling during IDLE, BUFFERING, SEEKING etc.

PlantUML Macro
formatSVG
titlePosition updates
@startuml


autonumber

box "Container" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

== Regular position update notifications ==

rialtoServer     ->  rialtoServer:     Position timer fired
rialtoServerGStreamer_client: Get position
GStreamer_client ->  rialtoClient:     getPosition(pipeline_session)
rialtoClient     ->  rialtoServer:     getPosition(pipeline_session)
rialtoServer     ->  GStreamer_server: Get position from pipeline
GStreamer_server --> rialtoServer:     position
rialtoServer     --> rialtoClient:     position
rialtoClient     --> GStreamer_client: position
GStreamer_client --> Starboard:        position
Starboard        ->  GStreamer_serverStarboard: Get position from pipeline
GStreamer_server --> rialtoServer:  Set   Currentplayer_info.pos = position
rialtoServerStarboard        --/>  rialtoClientCobalt:     notifyPosition(pipeline_session, position)
rialtoClient     -/ player_info

@enduml


End of stream

PlantUML Macro
formatSVG
titleEnd of stream
@startuml

autonumber

box "Container" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client: notifyPosition(pipeline_session, position)
note over GStreamer_client: Not used by Cobalt as some conformance\ntests require very high position accuracy


== Get position ==

Cobalt    
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

opt End of content reached
GStreamer_server   -/  rialtoServer:         GST_MESSAGE_EOS
rialtoServer       ->/  StarboardrialtoClient:         SbPlayerGetInfo2(player)
Starboard notifyPlaybackState(pipeline_session, END_OF_STREAM)
rialtoClient       ->/  GStreamer_client: Get position    notifyPlaybackState(pipeline_session, END_OF_STREAM)
note left
This should notify all attached sinks of EOS
end note
GStreamer_client   ->/  rialtoClientStarboard:     getPosition(pipeline_session)
rialtoClient     ->  rialtoServer:GST_MESSAGE_EOS
Starboard     getPosition(pipeline_session)
rialtoServer     ->/  GStreamer_serverCobalt: Get position from pipeline
GStreamer_server --> rialtoServer:     position
rialtoServer    PlayerStatus(player, kSbPlayerStateEndOfStream)
end

@enduml


Underflow

PlantUML Macro
formatSVG
titleUnderflow
@startuml

autonumber

box "Container" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

== Initialisation - register for callbacks ==

opt Video source attached
rialtoServer --> rialtoClient:     position
rialtoClient     --> GStreamer_client: position
GStreamer_client --> Starboard:        position
Starboard        ->  StarboardGStreamer_server:     g_signal_connect(video_decoder, getVideoUnderflowSignalName_soc(),  Set player_info.pos = position
Starboard        video_underflow_cb, user_data);
GStreamer_server   --> CobaltrialtoServer:           player_info

@enduml

End of stream

PlantUML Macro
formatSVG
titleEnd of stream
@startuml

autonumber

box "Container" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

opt End of content reached    video_handler_id
end

opt Audio source attached
rialtoServer       ->  GStreamer_server:     g_signal_connect(audio_decoder, getAudioUnderflowSignalName_soc(), audio_underflow_cb, user_data);
GStreamer_server   --/>  rialtoServer:         audio_handler_id
end


== Termination - unregister for callbacks ==

opt Video source GST_MESSAGE_EOSremoved
rialtoServer       -/>  rialtoClientGStreamer_server:         notifyPlaybackState(pipeline_session, END_OF_STREAM)
rialtoClientg_signal_handler_disconnect(video_decoder, video_handler_id);
GStreamer_server   --> rialtoServer:
end

opt Audio source removed
rialtoServer       -/ > GStreamer_clientserver:     notifyPlaybackState(pipeline_session, END_OF_STREAM)
note left
This should notify all attached sinks of EOS
end note
GStreamer_clientg_signal_handler_disconnect(audio_decoder, audio_handler_id);
GStreamer_server   --> rialtoServer:
end


== Underflow ==

opt Data starvation in server AV pipeline
GStreamer_server   -/  StarboardrialtoServer:         video_underflow_cb() or  GSTaudio_MESSAGE_EOS
Starboard          -/  Cobalt:               PlayerStatus(player, kSbPlayerStateEndOfStream)
end

@enduml

Underflow

PlantUML Macro
formatSVG
titleUnderflow
@startuml

autonumber

box "Container" #LightGreen
participant Cobalt
participant Starboard
participant GStreamer_client
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server
end box

== Initialisation - register for callbacks ==

opt Video source attached
rialtoServerunderflow_cb()
note across
underflow_enabled: Underflow is enabled when we're in playing state and source is attached.
underflow_cancelled: Underflow may be cancelled when haveData is called between notification from GStreamer and Underflow task handling.
end note
opt underflow_enabled && !underflow_cancelled
rialtoServer       -/  rialtoClient:         notifyBufferUnderflow(source_id)
rialtoClient       ->/  GStreamer_serverclient:      g_signal_connect(video_decoder, getVideoUnderflowSignalName_soc(), video_underflow_cb, user_data);
GStreamer_server   --> rialtoServer:  notifyBufferUnderflow(source_id)
GStreamer_client   -/  Starboard:            emit video_underflow_cb() or videoaudio_handlerunderflow_idcb()
end

opt Audio source attached
rialtoServer       ->  GStreamer_server:     g_signal_connect(audio_decoder, getAudioUnderflowSignalName_soc(), audio_underflow_cb, user_data);
GStreamer_server   --> rialtoServer:         audio_handler_id
end


== Termination - unregister for callbacks ==

opt Video source removed
rialtoServer       ->  GStreamer_server:     g_signal_handler_disconnect(video_decoder, video_handler_id);
note over Starboard, GStreamer_client
Starboard does not have any support for underflow
so the event can be ignored for this integration.
end note

end

note across
There will be one re more pending need data requests at this point which if serviced will allow playback to resume
end note

end

@enduml


Non-fatal Playback Failures

Decryption: Any encrypted frames that fail to decrypt are dropped, and an error notification is propagated to the rialto-gstreamer, at which point a decryption error is raised on the sink.

PlantUML Macro
formatSVG
titleNon-fatal Errors
@startuml

autonumber

box "Container" #LightGreen
participant Application
participant rialtoGstreamer
participant rialtoClient
end box

box "Platform" #LightBlue
participant rialtoServer
participant GStreamer_server 
end box

== Decryption ==
GStreamer_server   -->  rialtoServer:
end

opt Audio source removed
rialtoServer          decrypt(buffer)

rialtoServer	   ->  GStreamer_server:  MediaKeyErrorStatus::Fail
GStreamer_server   ->  GStreamer_server:     g_signal_handler_disconnect(audio_decoder, audio_handler_id);
GStreamer_server   --> rialtoServer:
end


== Underflow ==

opt Data starvation in server AV pipelineGST_BASE_TRANSFORM_FLOW_DROPPED 

note over GStreamer_server
Frame is dropped but playback is unaffected.
end note 

GStreamer_server   -/>  rialtoServer:         video_underflow_cb() or audio_underflow_cb()GST_MESSAGE_WARNING(src, GST_STREAM_ERROR_DECRYPT)

rialtoServer          -/  rialtoClient rialtoClient:         notifyBufferUnderflow(source_id notifyPlaybackError(MediaSourceType, PlaybackError::DECRYPTION)
rialtoClient       -/  GStreamer_client:     notifyBufferUnderflow(source_id)
GStreamer_client   -/  Starboard:            emit video_underflow_cb() or audio_underflow_cb()
 rialtoGstreamer:   notifyPlaybackError(MediaSourceType, PlaybackError::DECRYPTION)
 
note over Starboard, GStreamer_client
Starboard does not have any support for underflow
so the event can be ignored for this integrationrialtoGstreamer
Posting an error message on the sink make the\n
sink unable to continue playing back content.
end note

note across
There will be
rialtoGstreamer one re more pending-/ need Application: data  requests  at this point which if serviced will allow playback to resume
end note

end  GST_MESSAGE_ERROR(sink, GST_STREAM_ERROR_DECRYPT)

@enduml