top

API Usage

This topic describes how to implement a NaCl multimedia player for Samsung Smart TV.

The NaCl (Native Client) Player API allows you to implement various multimedia playback modes in your application, depending on the source of the multimedia data. You can play content from a multimedia resource URL, or from elementary stream packets demuxed by the application. Both modes can handle DRM-protected content, as well as subtitles.

Prerequisites

To enable additional functionality for NaCl Player media playback:

  • To access content on the Internet, the application has to request permission by adding the following privilege to the "config.xml" file:

    <tizen:privilege name='http://tizen.org/privilege/internet'></tizen:privilege> 
    
  • To play PlayReady-protected content, the application has to request permission by adding the following privilege to the "config.xml" file:

    <tizen:privilege name='http://developer.samsung.com/privilege/drmplay'></tizen:privilege>
    

Initializing the Media Player

To implement playback using the NaCl Player API, you must initialize the following classes:

  1. The MediaPlayer class is a representation of the platform player, which allows you to control it.
    Creating the MediaPlayer object is parameter-free:

    // Managing class holds a pointer to the player 
    std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_; 
    
    // Create the player object 
    player_ = std::make_shared<Samsung::NaClPlayer::MediaPlayer>(); 
    
  2. The MediaDataSource class, specialized as either a URLDataSource class or an ESDataSource class, is an abstract representation of a data source. Each specialization has a different initialization procedure. For more information, see Configuring Media Data Sources.

  3. The initialized MediaDataSource object must be attached to the MediaPlayer object:

    // Managing class holds a pointer to the data source
    std::shared_ptr<Samsung::NaClPlayer::MediaDataSource> data_source_ =
      std::make_shared</* MediaDataSource-derived type */>(/* args... */);
    
    // Application-defined function to initialize the MediaDataSource object
    InitializeDataSource(data_source_);
    
    // Attach the initialized data source
    player_->AttachDataSource(*data_source_);
    

Registering Listeners

To receive notifications about NaCl Player events, you must implement listeners. The NaCl Player listener classes do not provide event handling by default; event handlers must be implemented by the application.

To receive notifications from the NaCl Player:

  1. Override the appropriate listener class, and handle the virtual function associated with the event.

    • To receive playback notifications from the NaCl Player, override the MediaEventsListener class:

      class MyMediaEventsListener : public Samsung::NaClPlayer::MediaEventsListener {
        public:
          // During playback, provides the current playback time
          void OnTimeUpdate(Samsung::NaClPlayer::TimeTicks time) override {
            // Handle the event...
          }
      
          // Triggers when playback has finished
          void OnEnded() override {
            // Handle the event...
          }
      
          // Triggers when a player error occurs
          void OnError(Samsung::NaClPlayer::MediaPlayerError) override {
            // Handle the event...
          }
      };
      
    • To receive buffering notifications from the NaCl Player, override the BufferingListener class:

      class MyBufferingListener : public Samsung::NaClPlayer::BufferingListener {
        public:
          // Triggers when the NaCl player starts buffering data
          void OnBufferingStart() override {
            // Handle the event...
          }
      
          // Provides the buffering progress
          void OnBufferingProgress(uint32_t percent) override {
            // Handle the event...
          }
      
          // Triggers when the NaCl Player finishes buffering data
          void OnBufferingComplete() override {
            // Handle the event...
          }
      };
      

      The meaning of the buffering events depends on the data source.

      Table 1. Buffering events
      Event Elementary stream source URL source
      OnBufferingStart() This event is fired when a data source is attached to the MediaPlayer object.
      The NaCl Player data buffer is ready to be filled with elementary stream packets.
      This event is fired when a data source is attached to the MediaPlayer object and during seek.
      The NaCl Player has started buffering.
      OnBufferingProgress() - Sends information about the buffering completion percentage.
      OnBufferingComplete() The NaCl Player data buffer has received enough packets to start playback. The NaCl Player has finished buffering. Playback can be started, or resumed after seeking.
    • To receive DRM-related notifications from the NaCl Player, override the DRMListener class:

      class MyDRMListener : public Samsung::NaClPlayer::DRMListener {
        public:
          DRMListener(const std::shared_ptr<Samsung::NaClPlayer::MediaPlayer>& player) 
            : player_(player) {
          }
        // Notifies when the DRM initialization data is loaded
        void OnInitdataLoaded(
          Samsung::NaClPlayer::DRMType drm_type, uint32_t init_data_size,
            const void *init_data) override {
          }
      
          // Notifies that a license is required to play protected content
          // The request parameter contains challenge data for obtaining the license
          void OnLicenseRequest(uint32_t request_size, const void *request) override {
            // Connect to a license server and download the license
            std::string license;
            // Fill the license...
      
            // Install the obtained license
            if (auto player = player_.lock()) {
              player->SetDRMSpecificData(
                DRMType_*, DRMOperation_InstallLicense,
                license.size(), license.data());
            }
          }
        private:
          std::weak_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
      };
      

      When protected content needs to be decrypted, and there is no installed license that can decrypt it, the OnLicenseRequest() event is triggered. Generate the license request and install the license with the MediaPlayer::SetDRMSpecificData() function. If multiple tracks are protected by the same license, install it only once.

    • To receive subtitle change notifications from the NaCl Player, override the SubtitleListener class. The listener only notifies about subtitle changes in the currently-selected text track.

      class SubtitleListener : public Samsung::NaClPlayer::SubtitleListener {
        public:
          // Provide the subtitle text and duration
          void OnShowSubtitle(
            Samsung::NaClPlayer::TimeDelta duration, const char *text) override {
              // Show the subtitle...
            }
        };
      
      Note

      You must implement subtitle display in the application separately.

  2. Register each listener in the MediaPlayer object, using the corresponding SetxxxListener() function:

    // Managing class holds pointers 
    std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
    std::shared_ptr<MyxxxListener> events_listener_; 
    
    // Create a listener and register it in the player 
    events_listener_ = std::make_shared<MyxxxListener>(); 
    player_->SetxxxListener(events_listener_); 
    
    Note

    You can only register 1 listener of each type. Registering a new listener of the same type automatically deregisters the previous one.

  3. To unsubscribe from events, deregister the applicable listener:

    player_->SetxxxListener(nullptr); 
    
    Note

    Because the MediaPlayer object holds a shared reference to the listener, the application code does not need to store the reference. The listener is dereferenced automatically when the MediaPlayer object is destroyed.

Configuring Media Data Sources

Media data sources are configured in different ways, depending on whether the source is provided as a URL or an elementary stream.

URL Data Sources

The URLDataSource class allows you to play media from a specific URL source.

To create a URLDataSource object, call its constructor and attach it to the MediaPlayer object:

// Managing class holds pointers
std::shared_ptr<Samsung::NaClPlayer::MediaDataSource> data_source_;
std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
 
// Create a media data source for the URL and attach it to the player
data_source_ = std::make_shared<Samsung::NaClPlayer::URLDataSource>(media_content_url);
player_->AttachDataSource(*data_source_);

Elementary Stream Data Sources

The ESDataSource class allows you to play media using demuxed elementary stream packets.

To create and configure a ESDataSource object:

  1. Instantiate the ESDataSource class:

    // Managing class holds pointers 
    std::shared_ptr<Samsung::NaClPlayer::MediaDataSource>  es_data_source_;
    std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
    std::shared_ptr<Samsung::NaClPlayer::AudioElementaryStream> audio_stream_;
    std::shared_ptr<Samsung::NaClPlayer::VideoElementaryStream> video_stream_;
    std::shared_ptr<ESListener> audio_listener_;
    std::shared_ptr<ESListener> video_listener_;
    
    // Create the media data source
    es_data_source_ = std::make_shared<Samsung::NaClPlayer::ESDataSource>();
    
  2. Implement event handling using the ElementaryStreamListener class:

    class ESListener : public Samsung::NaClPlayer::ElementaryStreamListener {
     public:
      // Triggers when NaCl Player needs ES packets
      void OnNeedData(int32_t bytes_max) {
        // Send packets
      }
    
      // Triggers when the NaCl Player ES packet buffer is full
      void OnEnoughData() {
        // Stop sending packets
      }
    
      // Triggers when the NaCl Player performs seek operations
      void OnSeekData(Samsung::NaClPlayer::TimeTicks new_position) {
        // Adjust the time position from which packets are sent
      }
    }
    
  3. Add elementary streams to the ESDataSource object and associate them with the listener:

    // Add VideoElementaryStream
    video_listener_ = std::make_shared<ESListener>();
    video_stream_ = std::make_shared<Samsung::NaClPlayer::VideoElementaryStream>();
    es_data_source_->AddStream(*video_stream, video_listener_);
    
    // Add AudioElementaryStream
    audio_listener_ = std::make_shared<ESListener>();
    audio_stream_ = std::make_shared<Samsung::NaClPlayer::AudioElementaryStream>();
    es_data_source_->AddStream(*audio_stream, audio_listener_);
    
  4. Configure each elementary stream:

    • To configure an AudioElementaryStream object:

      1. Define the parameters in the AudioConfig struct:
        AudioConfig audio_config;
        // Fill audio_config with the appropriate information
        
        audio_stream_->SetAudioCodecType(audio_config.codec_type);
        audio_stream_->SetAudioCodecProfile(audio_config.codec_profile);
        audio_stream_->SetSampleFormat(audio_config.sample_format);
        audio_stream_->SetChannelLayout(audio_config.channel_layout);
        audio_stream_->SetBitsPerChannel(audio_config.bits_per_channel);
        audio_stream_->SetSamplesPerSecond(audio_config.samples_per_second);
        audio_stream_->SetCodecExtraData(audio_config.extra_data.size(),
                                         &audio_config.extra_data.front());
        
      2. Confirm and save the configuration using the ElementaryStream::InitializeDone() function:
        audio_stream_->InitializeDone();
        
    • To configure a VideoElementaryStream object:

      1. Define the parameters in the VideoConfig struct:

        VideoConfig video_config;
        // Fill video_config with the appropriate information
        
        video_stream_->SetVideoCodecType(video_config.codec_type);
        video_stream_->SetVideoCodecProfile(video_config.codec_profile);
        video_stream_->SetVideoFrameFormat(video_config.frame_format);
        video_stream_->SetVideoFrameSize(video_config.size);
        video_stream_->SetFrameRate(video_config.frame_rate);
        video_stream_->SetCodecExtraData(video_config.extra_data.size(),
                                         &video_config.extra_data.front());
        
      2. Confirm and save the configuration using the ElementaryStream::InitializeDone() function:

        video_stream_->InitializeDone();
        
  5. When the elementary streams have been configured, attach the ESDataSource object to the MediaPlayer object:

    player_->AttachDataSource(*es_data_source_);
    

    The application can receive elementary stream events and start appending elementary stream packets.

  6. When the application receives audio and video NeedData events, start appending packets to the buffer:

    audio_stream_->AppendPacket(es_pkt);
    video_stream_->AppendPacket(es_pkt);
    

    When the MediaPlayer object sends the BufferingListener::OnBufferingComplete() event, the application can begin playback.

Playing DRM-protected Content

The NaCl Player can handle multimedia content protected by DRM technology. The logic for handling DRM-protected content varies depending on the data source.

DRM for URL Data Sources

In the URL data source scenario, the DRM is configured automatically by the platform, based on the DRM configuration stored in a file at a specific URL (for example, a DASH manifest file).

DRM for Elementary Stream Data Sources

In the elementary stream source scenario, the DRM engine must be configured by the application during stream configuration. When the DRM license is requested, the license information must be retrieved and installed.

Note

An application can also use a pre-installed license, instead of waiting for a license request. In this situation, the DRM initialization data simply needs to be set during stream configuration.

  1. Define the DRM listener.
    The DRMListener class listens for the OnLicenseRequest() event and reacts to it by installing the license required to play the DRM-protected content:

    class MyDRMListener : public Samsung::NaClPlayer::DRMListener {
      public:
        DRMListener(const std::shared_ptr<Samsung::NaClPlayer::MediaPlayer>& player)
          : player_(player) {
      }
    
      // Notify that DRM protection data has been loaded
      void OnInitdataLoaded(
        Samsung::NaClPlayer::DRMType drm_type, uint32_t init_data_size,
        const void *init_data) override;
    
      // Signal that a license is required to play the current content
      void OnLicenseRequest(uint32_t request_size, const void *request) override;
    
      private:
      // OnLicenseRequest calls this function when it downloads a license
        void InstallLicense(std:: string license);
    
      std::weak_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
    };
    
  2. Register the listener in the MediaPlayer object:

    // Managing class holds pointers
    std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
    std::shared_ptr<MyDRMListener> drm_listener_;
    
    // Create the listener and register it in the player
    drm_listener_ = std::make_shared<MyDRMListener>(player_);
    player_->SetDRMListener(drm_listener_);
    

    For more information, see Registering Listeners.

  3. Configure the elementary streams.
    The DRM information for each elementary stream must be configured at the same time as the stream's other parameters, using the ElementaryStream::SetDRMInitData() function:

    // Managing class holds pointers
    std::shared_ptr<Samsung::NaClPlayer::MediaPlayer> player_;
    std::shared_ptr<Samsung::NaClPlayer::ESDataSource> es_data_source_;
    // ...
    std::shared_ptr<Samsung::NaClPlayer::ElementaryStreamListener> video_listener_;
    
    // Configure the ESDataSource
    void PlayerManager::ConfigureStreams(...) {
      // ...
      // Add a video elementary stream
      video_listener_ = std::make_shared<ESListener>();
      Samsung::NaClPlayer::VideoElementaryStream video_stream;
      es_data_source_->AddStream(video_stream, video_listener_);
      // Configure the video stream
      // ...
      video_stream.SetDRMInitData(encryption_type, init_data_size, init_data_ptr);
      video_stream.InitializeDone();
    
      // Repeat the above for an audio elementary stream...
      // ...
    }
    
  4. Request and install the DRM license.
    If the license required to play the current DRM-protected content is not available, the OnLicenseRequest() event is triggered. The DRMListener() event handler must download the license information from the license server and pass it to the NaCl Player.

    // Download a license asynchronously and call the license_downloaded_cb() functor
    void DownloadLicense(
      std::string license_server_url, uint32_t request_size, const void *request,
      std::function<std::string> license_downloaded_cb);
    
    void MyDRMListener::OnLicenseRequest(
      uint32_t request_size, const void *request) {
      // Download a valid license from the license server
      // For application responsiveness, download the license asynchronously
      DownloadLicense("license server URL", request_size, request, std::bind(
        &MyDRMListener::InstallLicense, this, std::placeholders::_1));
    }
    
    void MyDRMListener::InstallLicense(std::string license) {
      int32_t result = player_->SetDRMSpecificData(
        Samsung::NaClPlayer::DRMType_*,
        Samsung::NaClPlayer::DRMOperation_InstallLicense,
        license.size(), license.data());
      if (result < Samsung::NaClPlayer::ErrorCodes::Success) {
        // Handle the error...
      }
    }
    
    Note

    The location of the license server URL depends on the usage scenario. For example, a DASH stream with CENC protection stores the license server URL in a manifest file.

  5. Interact with the DRM system using the MediaPlayer::SetDRMSpecificData()function.

    Note

    Not all DRM systems support all available NaCl Player DRM operations.