Video Playback and Recording

Tizen enables your application to manage video content and provides control functions for using video resources. To play and record video files, use the Player (in mobile and wearable applications) and Recorder (in mobile and wearable applications) APIs. Managing video files is very similar to managing audio content, but it requires more consideration to display the visual content.

Tizen supports various video formats, including WMV, ASF, MP4, 3GP, AVI, MKV, and OGG. The available formats depend on the target device.

Playing Video

To play video files stored on the device, use the Player API (in mobile and wearable applications). The Player API also provides interfaces for getting media content information and controlling media operations, such as play, pause, resume, and stop.

Video content requires a drawing area for visual display, so you have to get an overlay region and set it to the player instance to display the drawing area:

  1. Get the overlay region or Evas from the Graphics module. You can display video on a UI layer with the Evas object as well as on an overlay layer.
  2. Create the player instance.
  3. Set the surface type and display a handle to the player using the player_set_display() function.

The following figure illustrates the player state changes.

Figure: Player state changes

Player state changes

Initializing the Video Player

You can create a player by calling the player_create() function, which returns a player handle on success. You need the player handle in setting which files to play and with which configuration. You also have to register appropriate callback functions to handle notifications about interruptions, ends, and errors during playback.

To prepare the player for playback, and to define the necessary callbacks to handle playback events:

  1. To use the data types and functions of the Player API (in mobile and wearable applications), include the <player.h> header file in your application:

    #include <player.h>
    
  2. Define a variable for the player handle, and create the handle:

    struct appdata {
        player_h player;
    };
    typedef struct appdata appdata_s;
    
    static void
    init_base_player(appdata_s *ad)
    {
        int error_code = 0;
        error_code = player_create(&ad->player);
        if (error_code != PLAYER_ERROR_NONE)
            dlog_print(DLOG_ERROR, LOG_TAG, "failed to create");
    
        /*
           Perform more playback configuration, such as setting callbacks,
           setting the source file URI, and preparing the player
        */
    }
    
  3. Set configurations, such as the source file URI.

    To set a specific path for video files to play, retrieve the default internal storage paths. To access and use internal storage, include the <storage.h> header file in your application.

    #include <storage.h>
    
    #define MP4_SAMPLE "SampleVideo.mp4";
    
    int internal_storage_id;
    char *video_storage_path = NULL;
    char *video_path = NULL;
    
    static bool
    storage_cb(int storage_id, storage_type_e type, storage_state_e state, const char *path, void *user_data)
    {
        if (type == STORAGE_TYPE_INTERNAL) {
            internal_storage_id = storage_id;
    
            return false;
        }
    
        return true;
    }
    
    void
    _get_storage_path()
    {
        int error_code = 0;
        char *path = NULL;
    
        error_code = storage_foreach_device_supported(storage_cb, NULL);
    
        error_code = storage_get_directory(internal_storage_id, STORAGE_DIRECTORY_VIDEOS, &path);
        if (error_code != STORAGE_ERROR_NONE) {
            video_storage_path = strdup(path);
            free(path);
        }
    }
    
    void
    _set_test_path()
    {
        int path_len = 0;
    
        path_len = strlen(video_storage_path) + strlen(MP4_SAMPLE) + 1;
        video_path = malloc(path_len);
        memset(video_path, 0x0, path_len);
    
        strncat(video_path, video_storage_path, strlen(video_storage_path));
        strncat(video_path, MP4_SAMPLE, strlen(MP4_SAMPLE));
    }
    

    Once the storage path is set, you can specify the video file to play using the player_set_uri() function with the player handle:

    error_code = player_set_uri(ad->player, video_path);
    if (error_code != PLAYER_ERROR_NONE)
        dlog_print(DLOG_ERROR, LOG_TAG, "failed to set URI: error code = %d", error_code);
    
  4. Create UI buttons and add callback functions for your application to control the playback:

    static void
    create_base_gui(appdata_s *ad)
    {
        /* Create a window */
        /* Create a button */
        /* Add a callback to the button */
        evas_object_smart_callback_add(button_init, "clicked", init_base_player, ad);
        evas_object_smart_callback_add(button_end, "clicked", release_base_player, ad);
        /* Create an Evas image object for the video surface */
    }
    
    static void
    app_create(void *data)
    {
        appdata_s *ad = data;
        create_base_gui(ad);
    
        return true;
    }
    
  5. Register callback functions.

    To receive notifications, register and define appropriate callback functions for interruption, playback ending, and error events:

    • Interruption notifications:

      static void
      _player_interrupted_cb(player_interrupted_code_e code, void *data)
      {
          appdata_s *ad = data;
          player_state_e state;
      
          /*
             All the interrupted_code_e is deprecated since Tizen 3.0
             except PLAYER_INTERRUPTED_BY_RESOURCE_CONFLICT
          */
          player_get_state(ad->player, &state);
          dlog_print(DLOG_INFO, LOG_TAG, "current player state = %d", state);
          /* If the state is PLAYER_STATE_PAUSED, update the UI (for example, button) */
      }
      
      static void
      init_base_player(appdata_s *ad)
      {
          /* Set an interruption callback if the application wants to know the reason */
          error_code = player_set_interrupted_cb(ad->player, _player_interrupted_cb, ad);
          if (error_code != PLAYER_ERROR_NONE)
              dlog_print(DLOG_ERROR, LOG_TAG, "failed to set interrupt cb");
      }
      
    • End notifications:

      static void
      _player_completed_cb(void *_data)
      {
          dlog_print(DLOG_INFO, LOG_TAG, "Playback End");
      }
      
      static void
      init_base_player(appdata_s *ad)
      {
          error_code = player_set_completed_cb(ad->player, _player_completed_cb, ad);
          if (error_code != PLAYER_ERROR_NONE)
              dlog_print(DLOG_ERROR, LOG_TAG, "failed to set completed cb");
      }
      
    • Error notifications:

      static void
      _player_error_cb(int error_code, void *user_data)
      {
          dlog_print(DLOG_ERROR, LOG_TAG, "playback failed, error = %x", error_code);
      }
      
      static void
      init_base_player(appdata_s *ad)
      {
          error_code = player_set_error_cb(ad->player, _player_error_cb, NULL);
          if (error_code != PLAYER_ERROR_NONE)
              dlog_print(DLOG_ERROR, LOG_TAG, "failed to set error cb");
      }
      

Managing Video Playback

To manage playback:

  1. Unlike in audio playback, the video player needs a region to display. To set the display, use the player_set_display() function with the player handle, display type, and display handle. After the function has been successfully executed, the player is connected to the display. The display handle can be retrieved using the GET_DISPLAY() function.

    error_code = player_set_display(ad->player, PLAYER_DISPLAY_TYPE_OVERLAY, GET_DISPLAY(evas_obj));
    
  2. When the player is created, it is in the PLAYER_STATE_IDLE state. To start playback, it must be in the PLAYER_STATE_READY state.

    Get the player ready for playback by calling the player_prepare() function, which changes the player state from PLAYER_STATE_IDLE to PLAYER_STATE_READY:

    error_code = player_prepare(ad->player);
    if (error_code != PLAYER_ERROR_NONE)
        dlog_print(DLOG_ERROR, LOG_TAG, "failed to prepare player: error code = %d", error_code);
    
  3. Once the player is ready, start playing the video file using the player_start() function. The player state changes to PLAYER_STATE_PLAYING.

    error_code = player_start(ad->player);
    if (error_code != PLAYER_ERROR_NONE)
        dlog_print(DLOG_ERROR, LOG_TAG, "failed to start player: error code = %d", error_code);
    
  4. Stop playback by calling the player_stop() function. The player state changes back to PLAYER_STATE_READY.

    By calling the player_pause() function, you can pause playback and change the player state to PLAYER_STATE_PAUSED.

Configuring the Video Playback

During video playback, you can manage the display settings – display mode and display orientation – as needed. Before setting the parameters, make sure that the player handle has been created, the display set, and the player started.

To configure the display settings:

  1. Check whether the display is visible.

    If the display is not visible, make it visible using the player_set_display_visible() function with true as the second parameter:

    bool is_visible;
    
    error_code = player_is_display_visible(ad->player, &is_visible);
    dlog_print(DLOG_ERROR, LOG_TAG, "player_is_display_visible = %d", error_code);
    
    if (!is_visible) {
        error_code = player_set_display_visible(ad->player, true);
        dlog_print(DLOG_ERROR, LOG_TAG, "player_set_display_visible = %d", error_code);
    }
    
  2. Set the display mode.

    The available display modes are defined in the player_display_mode_e enumeration (in mobile and wearable applications). Note that the DST ROI mode is deprecated since Tizen 3.0.

    error_code = player_set_display_mode(ad->player, PLAYER_DISPLAY_MODE_FULL_SCREEN);
    dlog_print(DLOG_ERROR, LOG_TAG, "player_set_display_mode = %d", error_code);
    
  3. Set the display orientation.

    The display orientations are defined in the player_display_rotation_e enumeration (in mobile and wearable applications).

    error_code = player_set_display_rotation(ad->player, PLAYER_DISPLAY_ROTATION_90);
    dlog_print(DLOG_ERROR, LOG_TAG, "player_set_display_mode = %d", error_code);
    

Terminating the Video Player

When you are finished using the player, release all the resources allocated to it:

  1. Reset the player by calling the player_unprepare() function, which changes the player state to PLAYER_STATE_IDLE.
  2. Destroy the player handle using the player_destroy() function.
error_code = player_stop(ad->player);
error_code = player_unprepare(ad->player);
error_code = player_destroy(ad->player);

if (error_code != PLAYER_ERROR_NONE)
    dlog_print(DLOG_ERROR, LOG_TAG, "fail to destroy player: error code = %d", error_code);

Recording Video

To record video, use the Recorder API (in mobile and wearable applications). The main features of the Recorder API include:

  • Basic recording functionalities: record, stop, pause, cancel, and mute
  • Setting the maximum recording time and size
  • Controlling the system volume level

The supported formats in video recording are MP4 and 3GP.

The following figure illustrates the general recorder state changes.

Figure: Recorder state changes

Recorder state changes

Initializing the Video Recorder

To prepare the recorder for the recording session, and to define the necessary callbacks to handle recording events:

  1. To use the data types and functions of the Recorder API (in mobile and wearable applications), include the <recorder.h> header file in your application:

    #include <recorder.h>
    
  2. To initialize the video recorder, configure the camera and video recorder, and register callback functions for the event of reaching the recording limit.

    Before you start configuring and initializing, define a structure for the camera and video recorder handles. This structure contains a Boolean variable specifying whether the video recorder is switched off.

    #include <camera.h>
    
    struct recdata {
        bool shutdown;
        recorder_h recorder;
        camera_h camera;
    };
    
  3. Define configuration values for the camera and the video recorder:

    static const int RECORD_TIME=2;
    static const int RECORD_LIMIT=4;
    #define FILENAME_PREFIX "VIDEO"
    static int g_bitrate = 288000;
    static int duration;
    static int playing=0;
    static int ret;
    
    • Configure the camera.

      Create a handle for the camera using the camera_create() function. The parameters are the hardware camera to access and the camera handle to be returned. The camera state is set as CAMERA_STATE_CREATED.

      static recdata rec_data;
      
      int error_code = 0;
      
      rec_data.shutdown = FALSE;
      rec_data.camera = NULL;
      rec_data.recorder = NULL;
      
      /* Create the camera handle */
      error_code = camera_create(CAMERA_DEVICE_CAMERA0, &rec_data.camera);
      if (error_code != CAMERA_ERROR_NONE)
          dlog_print(DLOG_ERROR, LOG_TAG, "fail to create camera: error code = %d", error_code);
      
    • Configure the video recorder.

      To create a handle for the video recorder, use the recorder_create_videorecorder() function with the camera and video recorder handles as parameters. The state of the video recorder is set as RECORDER_STATE_CREATED.

      /* Create the video recorder handle */
      error_code = recorder_create_videorecorder(rec_data.camera, &rec_data.recorder);
      if (error_code != RECORDER_ERROR_NONE)
          dlog_print(DLOG_INFO, LOG_TAG, "fail to create a Video Recorder: error code = %d", error_code);
      
  4. Register appropriate callback functions to receive notifications about recorder state changes or reaching the recording limit:

    /* Set the state change callback for the video recorder */
    error_code = recorder_set_state_changed_cb(rec_data.recorder, on_state_changed_cb, NULL);
    if (error_code != RECORDER_ERROR_NONE)
        dlog_print(DLOG_ERROR, LOG_TAG, "error code = %d", error_code);
    
    /* Define the change callback for the video recorder */
    static void
    on_state_changed_cb(recorder_state_e previous, recorder_state_e current, bool by_asm, void *data)
    {
        dlog_print(DLOG_DEBUG, LOG_TAG, "_recorder_state_changed_cb (prev: %d, curr: %d)\n", previous, current);
    }
    
  5. You can set various attributes of video recording, such as video codec, file format, file name, encoder bitrate, as well as audio attributes:

    • To set the video codec, you need to check first which codecs the device supports by calling the recorder_foreach_supported_video_encoder() function. The possible video codec values are defined in the recorder_video_codec_e enumeration (in mobile and wearable applications). Using the returned value and the recorder_set_video_encoder() function, you can set the video recorder codec:

      recorder_video_codec_e supported_codec;
      
      static bool
      _video_encoder_cb(recorder_video_codec_e codec, void *user_data)
      {
          recorder_video_codec_e * supported_codec = (recorder_video_codec_e*)user_data;
          *supported_codec = codec;
      
          return false;
      }
      
      error_code = recorder_foreach_supported_video_encoder(rec_data.recorder, _video_encoder_cb, &supported_codec);
      
      /* Set the video encoder for the video recorder */
      error_code = recorder_set_video_encoder(rec_data.recorder, supported_codec);
      
    • To set the video encoder bitrate, use the recorder_attr_set_video_encoder_bitrate() function:

      /* Set the video encoder bitrate */
      error_code = recorder_attr_set_video_encoder_bitrate(rec_data.recorder, g_bitrate);
      
    • To set the file format, use the recorder_set_file_format() function. Make sure that the file format matches the video codec.

      /* Set the file format */
      error_code = recorder_set_file_format(rec_data.recorder, RECORDER_FILE_FORMAT_MP4);
      
    • To set the file name, use the recorder_set_filename() function:

      struct tm localtime = {0};
      time_t rawtime = time(NULL);
      char filename[256] = {'\0'};
      size_t size;
      
      /* Create the file name */
      if (localtime_r(&rawtime, &localtime) != NULL) {
          size = snprintf(filename, sizeof(filename), "%s/%s-%04i-%02i-%02i_%02i:%02i:%02i.mp4",
                          app_get_data_path(), FILENAME_PREFIX,
                          localtime.tm_year + 1900, localtime.tm_mon + 1, localtime.tm_mday,
                          localtime.tm_hour, localtime.tm_min, localtime.tm_sec);
      } else {
          /* Error handling */
      }
      /* Set the full path and file name */
      error_code = recorder_set_filename(rec_data.recorder, filename);
      
    • To set the audio attributes, such as encoder, encoder bitrate, and sample rate, use the recorder_set_audio_encoder(), recorder_attr_set_audio_encoder_bitrate(), and recorder_attr_set_audio_samplerate() functions, respectively.

Managing Video Recording

When the recorder handle is created, the video recorder is in the RECORDER_STATE_CREATED state. To start recording, the video recorder must be in the RECORDER_STATE_READY state.

To manage recording:

  1. Get the recorder ready by calling the recorder_prepare() function, which changes the player state from RECORDER_STATE_CREATED to RECORDER_STATE_READY:

    error_code = recorder_prepare(rec_data.recorder);
    
  2. Once the recorder is ready, start video recording using the recorder_start() function. The recorder state changes to RECORDER_STATE_RECORDING.

    error_code = recorder_start(rec_data.recorder);
    
  3. In the RECORDER_STATE_RECORDING state, you can pause or stop recording:

    • To pause recording, call the recorder_pause() function that changes the recorder state to RECORDER_STATE_PAUSED. In this state, you can resume or stop recording. To resume, call the recorder_start() function.

      error_code = recorder_pause(rec_data.recorder);
      
    • You can stop recording either in the RECORDER_STATE_RECORDING or RECORDER_STATE_PAUSED state. In stopping recording, you can save or discard the recorded data. To save the recorded data, use the recorder_commit() function, and to discard, use the recorder_cancel() function. Both functions set the video recorder state to RECORDER_STATE_READY.

      error_code = recorder_commit(rec_data.recorder);
      /* OR */
      error_code = recorder_cancel(rec_data.recorder);
      

Terminating the Video Recorder

After you finish video recording, release all the resources allocated to the video recorder:

  1. Reset the video recorder by calling the recorder_unprepare() function, which changes the recorder state from RECORDER_STATE_READY to RECORDER_STATE_CREATED.
  2. Release the video recorder resources by calling the recorder_destroy() function. The recorder state changes to RECORDER_STATE_NONE.
error_code = recorder_unprepare(rec_data->recorder);
error_code = recorder_destroy(rec_data.recorder);

if (error_code != RECORDER_ERROR_NONE)
    dlog_print(DLOG_ERROR, LOG_TAG, "fail to destroy recorder: error code = %d", error_code);