dataout: added segment implementation layer [1/2]

PD#SWPL-137949

Problem:
request for data output only

Solution:
optimized for segment layer
implemented output segment layer

Verify:
PB

Change-Id: Ia4c6a2ec451736e523c0ff5fc94226e95d7a9b7a
Signed-off-by: Zhiqiang Han <zhiqiang.han@amlogic.com>
diff --git a/Android.bp b/Android.bp
index d5644c5..591ef8b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,7 @@
         "src/list_file.c",
         "src/record_device.c",
         "src/segment.c",
+        "src/segment_dataout.c",
         "src/am_crypt.c",
         "src/dvr_mutex.c",
     ],
@@ -115,6 +116,7 @@
         "src/list_file.c",
         "src/record_device.c",
         "src/segment.c",
+        "src/segment_dataout.c",
         "src/am_crypt.c",
         "src/dvr_mutex.c",
     ],
diff --git a/Makefile b/Makefile
index f35bbed..f7fb79f 100644
--- a/Makefile
+++ b/Makefile
@@ -16,8 +16,10 @@
 	src/dvr_wrapper.c\
 	src/list_file.c\
 	src/segment.c\
+	src/segment_dataout.c\
 	src/am_crypt.c\
 	src/dvr_mutex.c
+
 LIBAMDVR_OBJS := $(patsubst %.c,%.o,$(LIBAMDVR_SRCS))
 
 AM_FEND_TEST_SRCS := \
diff --git a/include/dvr_record.h b/include/dvr_record.h
index 626250a..f045fba 100644
--- a/include/dvr_record.h
+++ b/include/dvr_record.h
@@ -37,6 +37,7 @@
 typedef enum {
   DVR_RECORD_FLAG_SCRAMBLED = (1 << 0),
   DVR_RECORD_FLAG_ACCURATE  = (1 << 1),
+  DVR_RECORD_FLAG_DATAOUT   = (1 << 2),
 } DVR_RecordFlag_t;
 
 /**\brief DVR crypto parity flag*/
@@ -239,6 +240,16 @@
  */
 int dvr_record_discard_coming_data(DVR_RecordHandle_t handle, DVR_Bool_t discard);
 
+/**\brief control the recording logic
+ * \param[in] handle, DVR recording session handle
+ * \param[in] cmd, command
+ * \param[in/out] data, parameters
+ * \param[in/out] size, size of parameters
+ * \return DVR_SUCCESS on success
+ * \return error code on failure
+ */
+int dvr_record_ioctl(DVR_RecordHandle_t handle, unsigned int cmd, void *data, size_t size);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/dvr_wrapper.h b/include/dvr_wrapper.h
index e04654c..25e25ed 100644
--- a/include/dvr_wrapper.h
+++ b/include/dvr_wrapper.h
@@ -412,6 +412,17 @@
  */
 int dvr_wrapper_property_get(const char* prop_name, char* prop_value, int length);
 
+/**
+ * Control the internal recording logic
+ * \param rec The record handle.
+ * \param cmd control command
+ * \param data control data
+ * \param size size of the control data
+ * \retval DVR_SUCCESS On success.
+ * \return Error code.
+ */
+int dvr_wrapper_ioctl_record(DVR_WrapperRecord_t rec, unsigned int cmd, void *data, size_t size);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/segment.h b/include/segment.h
index bcc2709..6e60507 100644
--- a/include/segment.h
+++ b/include/segment.h
@@ -11,24 +11,7 @@
 #endif
 
 #include "dvr_types.h"
-
-/**\brief Segment handle*/
-typedef void* Segment_Handle_t;
-
-/**\brief Segment open mode*/
-typedef enum {
-  SEGMENT_MODE_READ,            /**< Segment open read mode*/
-  SEGMENT_MODE_WRITE,           /**< Segment open write mode*/
-  SEGMENT_MODE_MAX              /**< Segment invalid open mode*/
-} Segment_OpenMode_t;
-
-/**\brief Segment open parameters*/
-typedef struct Segment_OpenParams_s {
-  char                  location[DVR_MAX_LOCATION_SIZE];        /**< Segment file location*/
-  uint64_t              segment_id;                             /**< Segment index*/
-  Segment_OpenMode_t    mode;                                   /**< Segment open mode*/
-  DVR_Bool_t            force_sysclock;                         /**< If ture, force to use system clock as PVR index time source. If false, libdvr can determine index time source based on actual situation*/
-} Segment_OpenParams_t;
+#include "segment_ops.h"
 
 /**\brief Open a segment for a target giving some open parameters
  * \param[out] p_handle, Return the handle of the newly created segment
diff --git a/include/segment_dataout.h b/include/segment_dataout.h
new file mode 100644
index 0000000..9a33876
--- /dev/null
+++ b/include/segment_dataout.h
@@ -0,0 +1,39 @@
+#ifndef _SEGMENT_DATAOUT_H_
+#define _SEGMENT_DATAOUT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dvr_types.h"
+#include "segment_ops.h"
+
+/**
+ * Segment implementation 2
+ * for data loop out
+ */
+
+#define SEGMENT_DATAOUT_CMD_SET_CALLBACK 0x1001
+typedef struct Segment_DataoutCallback_s {
+    int (*callback)(unsigned char *buf, size_t size, void *priv);
+    void *priv;
+} Segment_DataoutCallback_t;
+
+
+
+int segment_dataout_open(Segment_OpenParams_t *params, Segment_Handle_t *p_handle);
+int segment_dataout_close(Segment_Handle_t handle);
+int segment_dataout_ioctl(Segment_Handle_t handle, int cmd, void *data, size_t size);
+ssize_t segment_dataout_write(Segment_Handle_t handle, void *buf, size_t count);
+loff_t segment_dataout_tell_total_time(Segment_Handle_t handle);
+int segment_dataout_store_info(Segment_Handle_t handle, Segment_StoreInfo_t *p_info);
+int segment_dataout_store_allInfo(Segment_Handle_t handle, Segment_StoreInfo_t *p_info);
+int segment_dataout_update_pts_force(Segment_Handle_t handle, uint64_t pts, loff_t offset);
+int segment_dataout_update_pts(Segment_Handle_t handle, uint64_t pts, loff_t offset);
+loff_t segment_dataout_tell_position(Segment_Handle_t handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/segment_ops.h b/include/segment_ops.h
new file mode 100644
index 0000000..79aaceb
--- /dev/null
+++ b/include/segment_ops.h
@@ -0,0 +1,197 @@
+/*
+ * \file
+ * Segment module
+ */
+
+#ifndef _SEGMENT_OPS_H_
+#define _SEGMENT_OPS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dvr_types.h"
+
+/**\brief Segment handle*/
+typedef void* Segment_Handle_t;
+
+/**\brief Segment open mode*/
+typedef enum {
+  SEGMENT_MODE_READ,            /**< Segment open read mode*/
+  SEGMENT_MODE_WRITE,           /**< Segment open write mode*/
+  SEGMENT_MODE_MAX              /**< Segment invalid open mode*/
+} Segment_OpenMode_t;
+
+/**\brief Segment open parameters*/
+typedef struct Segment_OpenParams_s {
+  char                  location[DVR_MAX_LOCATION_SIZE];        /**< Segment file location*/
+  uint64_t              segment_id;                             /**< Segment index*/
+  Segment_OpenMode_t    mode;                                   /**< Segment open mode*/
+  DVR_Bool_t            force_sysclock;                         /**< If ture, force to use system clock as PVR index time source. If false, libdvr can determine index time source based on actual situation*/
+} Segment_OpenParams_t;
+
+typedef struct Segment_Ops_s {
+
+  /**\brief Open a segment for a target giving some open parameters
+   * \param[out] p_handle, Return the handle of the newly created segment
+   * \param[in] params, Segment open parameters
+   * \return DVR_SUCCESS on success
+   * \return error code on failure
+   */
+  int (*segment_open)(Segment_OpenParams_t *params, Segment_Handle_t *p_handle);
+
+  /**\brief Close a segment
+   * \param[in] handle, Segment handle
+   * \return DVR_SUCCESS on success
+   * \return error code on failure
+   */
+  int (*segment_close)(Segment_Handle_t handle);
+
+  /**\brief control the giving segment
+   * \param[in] handle, Segment handle
+   * \param[in] cmd, The control command
+   * \param[in] data, The control data
+   * \param[in] size, Size of the control data
+   * \return DVR_SUCCESS on success
+   * \return error code on failure
+   */
+  int (*segment_ioctl)(Segment_Handle_t handle, int cmd, void *data, size_t size);
+
+  /**\brief Read data from the giving segment
+   * \param[out] buf, The buffer of data
+   * \param[in] handle, Segment handle
+   * \param[in] count, The data count
+   * \return The number of bytes read on success
+   * \return error code on failure
+   */
+  ssize_t (*segment_read)(Segment_Handle_t handle, void *buf, size_t count);
+
+  /**\brief Write data from the giving segment
+   * \param[in] buf, The buffer of data
+   * \param[in] handle, Segment handle
+   * \param[in] count, The data count
+   * \return The number of bytes write on success
+   * \return error code on failure
+   */
+  ssize_t (*segment_write)(Segment_Handle_t handle, void *buf, size_t count);
+
+  /**\brief force Update the pts and offset when record
+   * \param[in] handle, Segment handle
+   * \param[in] pts, Current pts
+   * \param[in] offset, Current segment offset
+   * \return DVR_SUCCESS on success
+   * \return error code on failure
+   */
+  int (*segment_update_pts_force)(Segment_Handle_t handle, uint64_t pts, loff_t offset);
+
+
+  /**\brief Update the pts and offset when record
+   * \param[in] handle, Segment handle
+   * \param[in] pts, Current pts
+   * \param[in] offset, Current segment offset
+   * \return DVR_SUCCESS on success
+   * \return error code on failure
+   */
+  int (*segment_update_pts)(Segment_Handle_t handle, uint64_t pts, loff_t offset);
+
+  /**\brief Seek the segment to the correct position which match the giving time
+   * \param[in] handle, Segment handle
+   * \param[in] time, The time offset
+   * \param[in] block_size, if block_size is > 0, we need aligned to block_size-byte boundary
+   * \return The segment current read position on success
+   * \return error code on failure
+   */
+  loff_t (*segment_seek)(Segment_Handle_t handle, uint64_t time, int block_size);
+
+  /**\brief Tell the current position for the giving segment
+   * \param[in] handle, Segment handle
+   * \return The segment current read position on success
+   * \return error code on failure
+   */
+  loff_t (*segment_tell_position)(Segment_Handle_t handle);
+
+  /**\brief Tell position time of the given segment's postion. Function is used for playback.
+   * \param[in] handle, Segment handle
+   * \param[in] position, Segment's file position
+   * \return position time in ms on success, or -1 on failure
+   */
+  loff_t (*segment_tell_position_time)(Segment_Handle_t handle, loff_t position);
+
+  /**\brief Tell current playback time of the given segment. Function is used for playback.
+   * \param[in] handle, Segment handle
+   * \return segment's current playback time in ms on success, or -1 on failure
+   */
+  loff_t (*segment_tell_current_time)(Segment_Handle_t handle);
+
+  /**\brief Tell total time of the given segment.
+   * \param[in] handle, Segment handle
+   * \return The segment's total time in ms on success, or -1 on failure
+   */
+  loff_t (*segment_tell_total_time)(Segment_Handle_t handle);
+
+  /**\brief Store the segment information to a file
+   * \param[in] handle, The segment handle
+   * \param[in] p_info, The segment information pointer
+   * \return DVR_SUCCESS On success
+   * \return Error code On failure
+   */
+  int (*segment_store_info)(Segment_Handle_t handle, Segment_StoreInfo_t *p_info);
+
+  /**\brief Store the segment all information to a file
+   * \param[in] handle, The segment handle
+   * \param[in] p_info, The segment information pointer
+   * \return DVR_SUCCESS On success
+   * \return Error code On failure
+   */
+  int (*segment_store_allInfo)(Segment_Handle_t handle, Segment_StoreInfo_t *p_info);
+
+  /**\brief Load the segment information from a file
+   * \param[in] handle, The segment handle
+   * \param[out] p_info, The segment information pointer
+   * \return DVR_SUCCESS On success
+   * \return Error code On failure
+   */
+  int (*segment_load_info)(Segment_Handle_t handle, Segment_StoreInfo_t *p_info);
+
+  /**\brief Load the segment information from a file
+   * \param[in] handle, The segment handle
+   * \param[out] p_info, The segment information pointer
+   * \return DVR_SUCCESS On success
+   * \return Error code On failure
+   */
+  int (*segment_load_allInfo)(Segment_Handle_t handle, struct list_head *list);
+
+
+  /**\brief Delete the segment information file
+   * \param[in] location, The record file's location
+   * \param[in] segment_id, The segment's index
+   * \return DVR_SUCCESS On success
+   * \return Error code On failure
+   */
+  int (*segment_delete)(const char *location, uint64_t segment_id);
+
+  /**\brief check the segment is ongoing file
+   * \param[in] handle, The segment handle
+   * \return DVR_SUCCESS On success
+   * \return Error code not ongoing
+   */
+  int (*segment_ongoing)(Segment_Handle_t handle);
+
+  /**\brief get current ongoing segment size
+   * \param[in] handle, The segment handle
+   * \return segment size
+   */
+  off_t (*segment_get_cur_segment_size)(Segment_Handle_t handle);
+
+  /**\brief get current ongoing segment id
+   * \param[in] handle, The segment handle
+   * \return segment id
+   */
+  uint64_t (*segment_get_cur_segment_id)(Segment_Handle_t handle);
+} Segment_Ops_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*END _SEGMENT_H_H_*/
diff --git a/src/dvr_record.c b/src/dvr_record.c
index 462ac75..07ae618 100644
--- a/src/dvr_record.c
+++ b/src/dvr_record.c
@@ -8,11 +8,13 @@
 #include "dvr_crypto.h"
 #include "dvb_utils.h"
 #include "record_device.h"
-#include "segment.h"
 #include <sys/time.h>
 #include <sys/prctl.h>
 #include "am_crypt.h"
 
+#include "segment.h"
+#include "segment_dataout.h"
+
 #define CHECK_PTS_MAX_COUNT  (20)
 
 //#define DEBUG_PERFORMANCE
@@ -80,8 +82,31 @@
   loff_t                          guarded_segment_size;                 /**< Guarded segment size in bytes. Libdvr will be forcely stopped to write anymore if current segment reaches this size*/
   size_t                          secbuf_size;                          /**< DVR record secure buffer length*/
   DVR_Bool_t                      discard_coming_data;                  /**< Whether to discard subsequent recording data due to exceeding total size limit too much.*/
+  Segment_Ops_t                   segment_ops;
+  struct list_head                segment_ctrls;
 } DVR_RecordContext_t;
 
+typedef struct {
+  struct list_head head;
+  unsigned int cmd;
+  void *data;
+  size_t size;
+} DVR_Control_t;
+
+#define SEG_CALL_INIT(_ops) Segment_Ops_t *ops = (_ops)
+#define SEG_CALL_RET_VALID(_name, _args, _ret, _def_ret) do {\
+    if (ops->segment_##_name)\
+      (_ret) = ops->segment_##_name _args;\
+    else\
+      (_ret) = (_def_ret);\
+  } while(0);
+#define SEG_CALL_RET(_name, _args, _ret) SEG_CALL_RET_VALID(_name, _args, _ret, _ret)
+#define SEG_CALL(_name, _args) do {\
+    if (ops->segment_##_name)\
+        ops->segment_##_name _args;\
+  } while(0)
+#define SEG_CALL_IS_VALID(_name) (!!ops->segment_##_name)
+
 extern ssize_t record_device_read_ext(Record_DeviceHandle_t handle, size_t *buf, size_t *len);
 
 static DVR_RecordContext_t record_ctx[MAX_DVR_RECORD_SESSION_COUNT] = {
@@ -93,6 +118,55 @@
   }
 };
 
+
+static int record_set_segment_ops(DVR_RecordContext_t *p_ctx, int flags)
+{
+  Segment_Ops_t *ops = &p_ctx->segment_ops;
+
+  memset(ops, 0, sizeof(Segment_Ops_t));
+
+  if (flags & DVR_RECORD_FLAG_DATAOUT) {
+    DVR_INFO("%s segment mode: dataout", __func__);
+    #define _SET(_op)\
+      ops->segment_##_op = segment_dataout_##_op
+    _SET(open);
+    _SET(close);
+    _SET(ioctl);
+    _SET(write);
+    _SET(update_pts);
+    _SET(update_pts_force);
+    _SET(tell_position);
+    _SET(tell_total_time);
+    _SET(store_info);
+    _SET(store_allInfo);
+    #undef _SET
+  } else {
+    #define _SET(_op)\
+      ops->segment_##_op = segment_##_op
+    _SET(open);
+    _SET(close);
+    _SET(read);
+    _SET(write);
+    _SET(update_pts);
+    _SET(update_pts_force);
+    _SET(seek);
+    _SET(tell_position);
+    _SET(tell_position_time);
+    _SET(tell_current_time);
+    _SET(tell_total_time);
+    _SET(store_info);
+    _SET(store_allInfo);
+    _SET(load_info);
+    _SET(load_allInfo);
+    _SET(delete);
+    _SET(ongoing);
+    _SET(get_cur_segment_size);
+    _SET(get_cur_segment_id);
+    #undef _SET
+  }
+  return DVR_SUCCESS;
+}
+
 static int record_is_valid_pid(DVR_RecordContext_t *p_ctx, int pid)
 {
   int i;
@@ -114,6 +188,8 @@
   int pid;
   int adp_field_len;
 
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   pid = ((p[1] & 0x1f) << 8) | p[2];
   if (pid == 0x1fff || !record_is_valid_pid(p_ctx, pid))
     return has_pcr;
@@ -156,7 +232,8 @@
       p_ctx->check_pts_count ++;
     }
     p_ctx->pts = pcr/90;
-    segment_update_pts(p_ctx->segment_handle, pcr/90, pos);
+
+    SEG_CALL(update_pts, (p_ctx->segment_handle, pcr/90, pos));
   }
   return has_pcr;
 }
@@ -168,7 +245,13 @@
   loff_t pos;
   int has_pcr = 0;
 
-  pos = segment_tell_position(p_ctx->segment_handle);
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
+  SEG_CALL_RET_VALID(tell_position, (p_ctx->segment_handle), pos, -1);
+
+  if (pos == -1)
+      return has_pcr;
+
   if (pos >= len) {
     pos = pos - len;
   }
@@ -212,6 +295,8 @@
   DVR_NewDmxSecureBuffer_t new_dmx_secure_buf;
   int first_read = 0;
 
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   prctl(PR_SET_NAME,"DvrRecording");
 
   // Force to use LOCAL_CLOCK as index type if force_sysclock is on. Please
@@ -296,6 +381,7 @@
       guarded_size_exceeded = DVR_TRUE;
     }
     /* Got data from device, record it */
+    ret = 0;
     if (guarded_size_exceeded) {
       len = 0;
       ret = 0;
@@ -334,7 +420,7 @@
       gettimeofday(&t3, NULL);
       /* Out buffer length may not equal in buffer length */
       if (crypto_params.output_size > 0) {
-        ret = segment_write(p_ctx->segment_handle, buf_out, crypto_params.output_size);
+        SEG_CALL_RET(write, (p_ctx->segment_handle, buf_out, crypto_params.output_size), ret);
         len = crypto_params.output_size;
       } else {
         len = 0;
@@ -345,14 +431,14 @@
       am_crypt_des_crypt(p_ctx->cryptor, buf_out, buf, &crypt_len, 0);
       len = crypt_len;
       gettimeofday(&t3, NULL);
-      ret = segment_write(p_ctx->segment_handle, buf_out, len);
+      SEG_CALL_RET(write, (p_ctx->segment_handle, buf_out, len), ret);
     } else {
       if (first_read == 0) {
         first_read = 1;
         DVR_INFO("%s:%d,first read ts", __func__,__LINE__);
       }
       gettimeofday(&t3, NULL);
-      ret = segment_write(p_ctx->segment_handle, buf, len);
+      SEG_CALL_RET(write, (p_ctx->segment_handle, buf, len), ret);
     }
     gettimeofday(&t4, NULL);
     //add DVR_RECORD_EVENT_WRITE_ERROR event if write error
@@ -367,10 +453,11 @@
         DVR_INFO("%s,write error %d", __func__,__LINE__);
       goto end;
     }
-    if (len>0) {
+
+    if (len > 0 && SEG_CALL_IS_VALID(tell_position)) {
       /* Do time index */
       uint8_t *index_buf = (p_ctx->enc_func || p_ctx->cryptor)? buf_out : buf;
-      pos = segment_tell_position(p_ctx->segment_handle);
+      SEG_CALL_RET(tell_position, (p_ctx->segment_handle), pos);
       has_pcr = record_do_pcr_index(p_ctx, index_buf, len);
       if (has_pcr == 0 && p_ctx->index_type == DVR_INDEX_TYPE_INVALID) {
         clock_gettime(CLOCK_MONOTONIC, &end_ts);
@@ -405,28 +492,30 @@
 
       /*Duration need use pcr to calculate, todo...*/
       if (p_ctx->index_type == DVR_INDEX_TYPE_PCR) {
-        p_ctx->segment_info.duration = segment_tell_total_time(p_ctx->segment_handle);
+        SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), p_ctx->segment_info.duration);
         if (pre_time == 0)
-         pre_time = p_ctx->segment_info.duration;
+          pre_time = p_ctx->segment_info.duration;
       } else if (p_ctx->index_type == DVR_INDEX_TYPE_LOCAL_CLOCK) {
         clock_gettime(CLOCK_MONOTONIC, &end_ts);
         p_ctx->segment_info.duration = (end_ts.tv_sec*1000 + end_ts.tv_nsec/1000000) -
           (start_ts.tv_sec*1000 + start_ts.tv_nsec/1000000) + pcr_rec_len;
         if (pre_time == 0)
           pre_time = p_ctx->segment_info.duration;
-        segment_update_pts(p_ctx->segment_handle, p_ctx->segment_info.duration, pos);
+        SEG_CALL(update_pts, (p_ctx->segment_handle, p_ctx->segment_info.duration, pos));
       } else {
         DVR_INFO("%s can NOT do time index", __func__);
       }
       if (p_ctx->index_type == DVR_INDEX_TYPE_PCR &&
           p_ctx->check_pts_count == CHECK_PTS_MAX_COUNT) {
-         DVR_INFO("%s change time from pcr to local time", __func__);
-         if (pcr_rec_len == 0)
-              pcr_rec_len = segment_tell_total_time(p_ctx->segment_handle);
-         p_ctx->index_type = DVR_INDEX_TYPE_LOCAL_CLOCK;
-        if (pcr_rec_len == 0)
-              pcr_rec_len = segment_tell_total_time(p_ctx->segment_handle);
-         clock_gettime(CLOCK_MONOTONIC, &start_ts);
+        DVR_INFO("%s change time from pcr to local time", __func__);
+        if (pcr_rec_len == 0) {
+          SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), pcr_rec_len);
+        }
+        p_ctx->index_type = DVR_INDEX_TYPE_LOCAL_CLOCK;
+        if (pcr_rec_len == 0) {
+          SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), pcr_rec_len);
+        }
+        clock_gettime(CLOCK_MONOTONIC, &start_ts);
       }
 
       if (p_ctx->index_type == DVR_INDEX_TYPE_PCR ) {
@@ -435,8 +524,9 @@
           (int)(start_no_pcr_ts.tv_sec*1000 + start_no_pcr_ts.tv_nsec/1000000);
          if (diff > 3000) {
             DVR_INFO("%s no pcr change time from pcr to local time diff[%d]", __func__, diff);
-            if (pcr_rec_len == 0)
-              pcr_rec_len = segment_tell_total_time(p_ctx->segment_handle);
+            if (pcr_rec_len == 0) {
+              SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), pcr_rec_len);
+            }
             p_ctx->index_type = DVR_INDEX_TYPE_LOCAL_CLOCK;
          }
       }
@@ -445,9 +535,10 @@
       if (p_ctx->segment_info.duration - pre_time > DVR_STORE_INFO_TIME) {
         pre_time = p_ctx->segment_info.duration + DVR_STORE_INFO_TIME;
         time_t duration = p_ctx->segment_info.duration;
-        if (p_ctx->index_type == DVR_INDEX_TYPE_LOCAL_CLOCK)
-          p_ctx->segment_info.duration = segment_tell_total_time(p_ctx->segment_handle);
-        segment_store_info(p_ctx->segment_handle, &(p_ctx->segment_info));
+        if (p_ctx->index_type == DVR_INDEX_TYPE_LOCAL_CLOCK) {
+          SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), p_ctx->segment_info.duration);
+        }
+        SEG_CALL(store_info, (p_ctx->segment_handle, &p_ctx->segment_info));
         p_ctx->segment_info.duration = duration;
       }
     } else {
@@ -472,9 +563,9 @@
       p_ctx->last_send_time = p_ctx->segment_info.duration;
       record_status.state = p_ctx->state;
       record_status.info.id = p_ctx->segment_info.id;
-      if (p_ctx->index_type == DVR_INDEX_TYPE_LOCAL_CLOCK)
-        record_status.info.duration = segment_tell_total_time(p_ctx->segment_handle);
-      else
+      if (p_ctx->index_type == DVR_INDEX_TYPE_LOCAL_CLOCK) {
+        SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), record_status.info.duration);
+      } else
         record_status.info.duration = p_ctx->segment_info.duration;
       record_status.info.size = p_ctx->segment_info.size;
       record_status.info.nb_packets = p_ctx->segment_info.size/188;
@@ -583,6 +674,10 @@
   }
   p_ctx->discard_coming_data = DVR_FALSE;
   DVR_INFO("%s, block_size:%d is_new:%d", __func__, p_ctx->block_size, p_ctx->is_new_dmx);
+
+  record_set_segment_ops(p_ctx, params->flags);
+  INIT_LIST_HEAD(&p_ctx->segment_ctrls);
+
   *p_handle = p_ctx;
   return DVR_SUCCESS;
 }
@@ -614,6 +709,17 @@
       DVR_INFO("%s, failed", __func__);
     }
   }
+
+  if (!list_empty(&p_ctx->segment_ctrls)) {
+    DVR_Control_t *pc, *pc_tmp;
+    list_for_each_entry_safe(pc, pc_tmp, &p_ctx->segment_ctrls, head) {
+      list_del(&pc->head);
+      if (pc->data)
+        free(pc->data);
+      free(pc);
+    }
+  }
+
   memset(p_ctx, 0, sizeof(DVR_RecordContext_t));
   p_ctx->state = DVR_RECORD_STATE_CLOSED;
   return ret;
@@ -691,20 +797,33 @@
   }
   DVR_RETURN_IF_FALSE(p_ctx == &record_ctx[i]);
 
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   DVR_INFO("%s , current state:%d pids:%d params->location:%s", __func__, p_ctx->state, params->segment.nb_pids, params->location);
   DVR_RETURN_IF_FALSE(p_ctx->state != DVR_RECORD_STATE_STARTED);
   DVR_RETURN_IF_FALSE(p_ctx->state != DVR_RECORD_STATE_CLOSED);
   DVR_RETURN_IF_FALSE(params);
-
   DVR_RETURN_IF_FALSE(strlen((const char *)params->location) < DVR_MAX_LOCATION_SIZE);
-  memset(&open_params, 0, sizeof(open_params));
-  memcpy(open_params.location, params->location, sizeof(params->location));
-  open_params.segment_id = params->segment.segment_id;
-  open_params.mode = SEGMENT_MODE_WRITE;
-  open_params.force_sysclock = p_ctx->force_sysclock;
 
-  ret = segment_open(&open_params, &p_ctx->segment_handle);
-  DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+  if (SEG_CALL_IS_VALID(open)) {
+    memset(&open_params, 0, sizeof(open_params));
+
+    memcpy(open_params.location, params->location, sizeof(params->location));
+    open_params.segment_id = params->segment.segment_id;
+    open_params.mode = SEGMENT_MODE_WRITE;
+    open_params.force_sysclock = p_ctx->force_sysclock;
+
+    SEG_CALL_RET(open, (&open_params, &p_ctx->segment_handle), ret);
+    DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+
+    if (SEG_CALL_IS_VALID(ioctl)) {
+      DVR_Control_t *pc;
+      list_for_each_entry(pc, &p_ctx->segment_ctrls, head) {
+        SEG_CALL_RET(ioctl, (p_ctx->segment_handle, pc->cmd, pc->data, pc->size), ret);
+        DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+      }
+    }
+  }
 
   /*process params*/
   {
@@ -728,7 +847,7 @@
     DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
   }
 
-  ret = segment_store_info(p_ctx->segment_handle, &p_ctx->segment_info);
+  SEG_CALL_RET(store_info, (p_ctx->segment_handle, &p_ctx->segment_info), ret);
   DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
 
   p_ctx->state = DVR_RECORD_STATE_STARTED;
@@ -760,6 +879,8 @@
   DVR_RETURN_IF_FALSE(p_info);
   DVR_RETURN_IF_FALSE(!p_ctx->is_vod);
 
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   /*Stop the on going record segment*/
   //ret = record_device_stop(p_ctx->dev_handle);
   //DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
@@ -767,33 +888,45 @@
   pthread_join(p_ctx->thread, NULL);
 
   //add index file store
-  pos = segment_tell_position(p_ctx->segment_handle);
-  segment_update_pts_force(p_ctx->segment_handle, p_ctx->segment_info.duration, pos);
+  if (SEG_CALL_IS_VALID(update_pts_force)) {
+    SEG_CALL_RET_VALID(tell_position, (p_ctx->segment_handle), pos, -1);
+    if (pos != -1) {
+      SEG_CALL(update_pts_force, (p_ctx->segment_handle, p_ctx->segment_info.duration, pos));
+    }
+  }
 
-  p_ctx->segment_info.duration = segment_tell_total_time(p_ctx->segment_handle);
+  SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), p_ctx->segment_info.duration);
+
   /*Update segment info*/
   memcpy(p_info, &p_ctx->segment_info, sizeof(p_ctx->segment_info));
 
-  ret = segment_store_info(p_ctx->segment_handle, p_info);
+  SEG_CALL_RET(store_info, (p_ctx->segment_handle, p_info), ret);
   DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
-  segment_store_allInfo(p_ctx->segment_handle, p_info);
+
+  SEG_CALL(store_allInfo, (p_ctx->segment_handle, p_info));
+
   DVR_INFO("%s dump segment info, id:%lld, nb_pids:%d, duration:%ld ms, size:%zu, nb_packets:%d params->segment.nb_pids:%d",
       __func__, p_info->id, p_info->nb_pids, p_info->duration, p_info->size, p_info->nb_packets, params->segment.nb_pids);
 
   /*Close current segment*/
-  ret = segment_close(p_ctx->segment_handle);
+  SEG_CALL_RET(close, (p_ctx->segment_handle), ret);
   DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
-  /*Open the new record segment*/
-  memset(&open_params, 0, sizeof(open_params));
-  memcpy(open_params.location, p_ctx->location, sizeof(p_ctx->location));
-  open_params.segment_id = params->segment.segment_id;
-  open_params.mode = SEGMENT_MODE_WRITE;
-  open_params.force_sysclock = p_ctx->force_sysclock;
-  DVR_INFO("%s: p_ctx->location:%s  params->location:%s", __func__, p_ctx->location,params->location);
+
   p_ctx->last_send_size = 0;
   p_ctx->last_send_time = 0;
-  ret = segment_open(&open_params, &p_ctx->segment_handle);
-  DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+
+  /*Open the new record segment*/
+  if (SEG_CALL_IS_VALID(open)) {
+    memset(&open_params, 0, sizeof(open_params));
+    memcpy(open_params.location, p_ctx->location, sizeof(p_ctx->location));
+    open_params.segment_id = params->segment.segment_id;
+    open_params.mode = SEGMENT_MODE_WRITE;
+    open_params.force_sysclock = p_ctx->force_sysclock;
+    DVR_INFO("%s: p_ctx->location:%s  params->location:%s", __func__, p_ctx->location,params->location);
+    SEG_CALL_RET(open, (&open_params, &p_ctx->segment_handle), ret);
+    DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+  }
+
   /*process params*/
   {
     //need all params??
@@ -830,11 +963,14 @@
 
   //ret = record_device_start(p_ctx->dev_handle);
   //DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+
   /*Update segment info*/
-  ret = segment_store_info(p_ctx->segment_handle, &p_ctx->segment_info);
+  SEG_CALL_RET(store_info, (p_ctx->segment_handle, &p_ctx->segment_info), ret);
   DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
-  if (p_ctx->pts != ULLONG_MAX)
-    segment_update_pts(p_ctx->segment_handle, p_ctx->pts, 0);
+
+  if (p_ctx->pts != ULLONG_MAX) {
+    SEG_CALL(update_pts, (p_ctx->segment_handle, p_ctx->pts, 0));
+  }
 
   p_ctx->state = DVR_RECORD_STATE_STARTED;
   pthread_create(&p_ctx->thread, NULL, record_thread, p_ctx);
@@ -846,7 +982,7 @@
   DVR_RecordContext_t *p_ctx;
   int ret = DVR_SUCCESS;
   uint32_t i;
-  loff_t pos;
+  loff_t pos = 0;
 
   p_ctx = (DVR_RecordContext_t *)handle;
   for (i = 0; i < MAX_DVR_RECORD_SESSION_COUNT; i++) {
@@ -866,9 +1002,10 @@
   DVR_RETURN_IF_FALSE(p_ctx->state != DVR_RECORD_STATE_CLOSED);
   DVR_RETURN_IF_FALSE(p_info);/*should support NULL*/
 
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   p_ctx->state = DVR_RECORD_STATE_STOPPED;
   if (p_ctx->is_vod) {
-    p_ctx->segment_info.duration = segment_tell_total_time(p_ctx->segment_handle);
     p_ctx->segment_info.duration = 10*1000; //debug, should delete it
   } else {
     pthread_join(p_ctx->thread, NULL);
@@ -880,27 +1017,32 @@
   }
 
   //add index file store
-  pos = segment_tell_position(p_ctx->segment_handle);
-  segment_update_pts_force(p_ctx->segment_handle, p_ctx->segment_info.duration, pos);
-  p_ctx->segment_info.duration = segment_tell_total_time(p_ctx->segment_handle);
+  if (SEG_CALL_IS_VALID(update_pts_force)) {
+    SEG_CALL_RET_VALID(tell_position, (p_ctx->segment_handle), pos, -1);
+    if (pos != -1) {
+      SEG_CALL(update_pts_force, (p_ctx->segment_handle, p_ctx->segment_info.duration, pos));
+    }
+  }
+
+  SEG_CALL_RET(tell_total_time, (p_ctx->segment_handle), p_ctx->segment_info.duration);
 
   /*Update segment info*/
   memcpy(p_info, &p_ctx->segment_info, sizeof(p_ctx->segment_info));
 
-  ret = segment_store_info(p_ctx->segment_handle, p_info);
-  //DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+  SEG_CALL_RET(store_info, (p_ctx->segment_handle, p_info), ret);
   if (ret != DVR_SUCCESS)
-      goto end;
+    goto end;
 
-  segment_store_allInfo(p_ctx->segment_handle, p_info);
+  SEG_CALL(store_allInfo, (p_ctx->segment_handle, p_info));
 
   DVR_INFO("%s dump segment info, id:%lld, nb_pids:%d, duration:%ld ms, size:%zu, nb_packets:%d",
       __func__, p_info->id, p_info->nb_pids, p_info->duration, p_info->size, p_info->nb_packets);
 
 end:
-  ret = segment_close(p_ctx->segment_handle);
+  SEG_CALL_RET(close, (p_ctx->segment_handle), ret);
   p_ctx->segment_handle = NULL;
   DVR_RETURN_IF_FALSE(ret == DVR_SUCCESS);
+
   return DVR_SUCCESS;
 }
 
@@ -955,7 +1097,6 @@
 {
   DVR_RecordContext_t *p_ctx;
   uint32_t i;
-  off_t pos = 0;
   int ret = DVR_SUCCESS;
   int has_pcr;
 
@@ -968,13 +1109,14 @@
   DVR_RETURN_IF_FALSE(buffer);
   DVR_RETURN_IF_FALSE(len);
 
-  pos = segment_tell_position(p_ctx->segment_handle);
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
   has_pcr = record_do_pcr_index(p_ctx, buffer, len);
   if (has_pcr == 0) {
     /* Pull VOD record should use PCR time index */
     DVR_INFO("%s has no pcr, can NOT do time index", __func__);
   }
-  ret = segment_write(p_ctx->segment_handle, buffer, len);
+  SEG_CALL_RET(write, (p_ctx->segment_handle, buffer, len), ret);
   if (ret != len) {
     DVR_INFO("%s write error ret:%d len:%d", __func__, ret, len);
   }
@@ -1077,3 +1219,47 @@
 
   return DVR_TRUE;
 }
+
+int dvr_record_ioctl(DVR_RecordHandle_t handle, unsigned int cmd, void *data, size_t size)
+{
+  DVR_RecordContext_t *p_ctx;
+  int ret = DVR_FAILURE;
+  int i;
+
+  p_ctx = (DVR_RecordContext_t *)handle;
+  for (i = 0; i < MAX_DVR_RECORD_SESSION_COUNT; i++) {
+    if (p_ctx == &record_ctx[i])
+      break;
+  }
+  DVR_RETURN_IF_FALSE(p_ctx == &record_ctx[i]);
+
+  SEG_CALL_INIT(&p_ctx->segment_ops);
+
+  if (SEG_CALL_IS_VALID(ioctl)) {
+    if (p_ctx->segment_handle) {
+      SEG_CALL_RET(ioctl, (p_ctx->segment_handle, cmd, data, size), ret);
+    } else {
+      DVR_Control_t *ctrl = (DVR_Control_t *)calloc(1, sizeof(DVR_Control_t));
+      if (ctrl) {
+        ctrl->cmd = cmd;
+        if (size) {
+          void *pdata = malloc(size);
+          if (pdata) {
+            memcpy(pdata, data, size);
+            ctrl->data = pdata;
+            ctrl->size = size;
+          } else {
+            free(ctrl);
+            ctrl = NULL;
+          }
+        }
+      }
+      if (ctrl) {
+        list_add_tail(&ctrl->head, &p_ctx->segment_ctrls);
+        ret = DVR_SUCCESS;
+      }
+    }
+  }
+
+  return ret;
+}
diff --git a/src/dvr_segment.c b/src/dvr_segment.c
index 7c39a64..db512b7 100644
--- a/src/dvr_segment.c
+++ b/src/dvr_segment.c
@@ -87,18 +87,17 @@
 
   DIR *dir; // pointer to directory
   struct dirent *entry; // pointer to file entry
-  char *ext; // pointer to file extension
-  char *loc_dname;
-  int loc_dname_len;
-  char *loc_fname;
-  int loc_fname_len;
-  char *path;
-  int path_size;
+  char *loc_dname = NULL;
+  int loc_dname_len = 0;
+  char *loc_fname = NULL;
+  int loc_fname_len = 0;
+  char *path = NULL;
+  int path_size = 0;
 
   /*get the dirname and filename*/
   loc_fname = strrchr(location, '/');// fine last slash
-  loc_fname_len = strlen(loc_fname);
   if (loc_fname) {// skip the slash
+    loc_fname_len = strlen(loc_fname);
     loc_fname += 1;
     loc_fname_len -= 1;
   }
diff --git a/src/dvr_wrapper.c b/src/dvr_wrapper.c
index 818b8d5..6bd0c31 100644
--- a/src/dvr_wrapper.c
+++ b/src/dvr_wrapper.c
@@ -3163,3 +3163,25 @@
   return dvr_prop_read(prop_name,prop_value,length);
 }
 
+int dvr_wrapper_ioctl_record(DVR_WrapperRecord_t rec, unsigned int cmd, void *data, size_t size)
+{
+  DVR_WrapperCtx_t *ctx;
+  int error;
+
+  DVR_RETURN_IF_FALSE(rec);
+
+  ctx = ctx_getRecord((unsigned long)rec);
+  DVR_RETURN_IF_FALSE(ctx);
+
+  wrapper_mutex_lock(&ctx->wrapper_lock);
+  DVR_WRAPPER_INFO("libdvr_api, ioctl_record (sn:%ld) cmd:%#x data:%p", ctx->sn, cmd, data);
+  WRAPPER_RETURN_IF_FALSE_WITH_UNLOCK(ctx_valid(ctx), &ctx->wrapper_lock);
+
+  error = dvr_record_ioctl(ctx->record.recorder, cmd, data, size);
+
+  DVR_WRAPPER_INFO("record(sn:%ld) ioctl_record = (%d)\n", ctx->sn, error);
+  wrapper_mutex_unlock(&ctx->wrapper_lock);
+
+  return error;
+
+}
diff --git a/src/segment_dataout.c b/src/segment_dataout.c
new file mode 100644
index 0000000..bad2928
--- /dev/null
+++ b/src/segment_dataout.c
@@ -0,0 +1,139 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "dvr_types.h"
+#include "segment_dataout.h"
+
+
+/**\brief Segment context*/
+typedef struct {
+  uint64_t        segment_id;
+
+  uint64_t        cur_time;
+  uint64_t        size_written;
+  uint64_t        pkts_written;
+
+  Segment_DataoutCallback_t dataout_callback;
+} Segment_Context_t;
+
+int segment_dataout_open(Segment_OpenParams_t *params, Segment_Handle_t *p_handle)
+{
+  Segment_Context_t *p_ctx;
+
+  DVR_RETURN_IF_FALSE(params);
+  DVR_RETURN_IF_FALSE(p_handle);
+
+  p_ctx = (void*)malloc(sizeof(Segment_Context_t));
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  memset(p_ctx, 0, sizeof(Segment_Context_t));
+
+  p_ctx->segment_id = params->segment_id;
+
+  *p_handle = (Segment_Handle_t)p_ctx;
+  return DVR_SUCCESS;
+}
+
+int segment_dataout_close(Segment_Handle_t handle)
+{
+  Segment_Context_t *p_ctx = (void *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  free(p_ctx);
+  return DVR_SUCCESS;
+}
+
+int segment_dataout_ioctl(Segment_Handle_t handle, int cmd, void *data, size_t size)
+{
+  Segment_Context_t *p_ctx = (void *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  switch (cmd) {
+    case SEGMENT_DATAOUT_CMD_SET_CALLBACK:
+      DVR_RETURN_IF_FALSE(data != NULL);
+      p_ctx->dataout_callback = *(Segment_DataoutCallback_t *)data;
+    break;
+
+    default:
+      return DVR_FAILURE;
+  }
+  return DVR_SUCCESS;
+}
+
+ssize_t segment_dataout_write(Segment_Handle_t handle, void *buf, size_t count)
+{
+  ssize_t len = 0;
+  Segment_Context_t *p_ctx = (Segment_Context_t *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+  DVR_RETURN_IF_FALSE(buf);
+
+  if (p_ctx->dataout_callback.callback)
+    len = p_ctx->dataout_callback.callback(buf, count, p_ctx->dataout_callback.priv);
+
+  p_ctx->size_written += count;
+
+  return count;
+}
+
+loff_t segment_dataout_tell_total_time(Segment_Handle_t handle)
+{
+  uint64_t pts = ULLONG_MAX;
+  Segment_Context_t *p_ctx = (Segment_Context_t *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  pts = p_ctx->cur_time;
+
+  return pts;
+}
+
+int segment_dataout_store_info(Segment_Handle_t handle, Segment_StoreInfo_t *p_info)
+{
+  Segment_Context_t *p_ctx = (Segment_Context_t *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+  DVR_RETURN_IF_FALSE(p_info);
+
+  p_ctx->segment_id = p_info->id;
+  p_ctx->cur_time = p_info->duration;
+  p_ctx->size_written = p_info->size;
+  p_ctx->pkts_written = p_info->nb_packets;
+
+  return DVR_SUCCESS;
+}
+
+int segment_dataout_store_allInfo(Segment_Handle_t handle, Segment_StoreInfo_t *p_info)
+{
+  return segment_dataout_store_info(handle, p_info);
+}
+
+
+int segment_dataout_update_pts_force(Segment_Handle_t handle, uint64_t pts, loff_t offset)
+{
+  Segment_Context_t *p_ctx = (Segment_Context_t *)handle;
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  if (offset == p_ctx->size_written)
+    p_ctx->cur_time = pts;
+  return DVR_SUCCESS;
+}
+
+int segment_dataout_update_pts(Segment_Handle_t handle, uint64_t pts, loff_t offset)
+{
+  return segment_dataout_update_pts_force(handle, pts, offset);
+}
+
+loff_t segment_dataout_tell_position(Segment_Handle_t handle)
+{
+  loff_t pos;
+  Segment_Context_t *p_ctx = (Segment_Context_t *)handle;
+
+  DVR_RETURN_IF_FALSE(p_ctx);
+
+  pos = p_ctx->size_written;
+
+  return pos;
+}
diff --git a/test/dvr_wrapper_test/Android.bp b/test/dvr_wrapper_test/Android.bp
index 198b17f..83cd0b4 100644
--- a/test/dvr_wrapper_test/Android.bp
+++ b/test/dvr_wrapper_test/Android.bp
@@ -35,3 +35,36 @@
       "vendor/amlogic/common/mediahal_sdk/include",
     ],
 }
+
+cc_binary {
+    name: "dvr_wrapper_test.dataout",
+    proprietary: true,
+    compile_multilib: "32",
+
+    arch: {
+        x86: {
+            enabled: false,
+        },
+        x86_64: {
+            enabled: false,
+        },
+    },
+
+    srcs: [
+        "dvr_wrapper_test.dataout.c"
+    ],
+
+    shared_libs: [
+        "libutils",
+        "libcutils",
+        "liblog",
+        "libdl",
+        "libc",
+        "libamdvr",
+    ],
+
+    include_dirs: [
+      "hardware/amlogic/media/amcodec/include",
+      "vendor/amlogic/common/mediahal_sdk/include",
+    ],
+}
diff --git a/test/dvr_wrapper_test/dvr_wrapper_test.dataout.c b/test/dvr_wrapper_test/dvr_wrapper_test.dataout.c
new file mode 100644
index 0000000..4ad3561
--- /dev/null
+++ b/test/dvr_wrapper_test/dvr_wrapper_test.dataout.c
@@ -0,0 +1,268 @@
+/**
+ * \page dvb_wrapper_test
+ * \section Introduction
+ * test code with dvb_wrapper_xxxx APIs.
+ * It supports:
+ * \li Record
+ *
+ * \section Usage
+ *
+ * Help msg will be shown if the test runs without parameters.\n
+ * There are some general concepts for the parameters:
+ * \li assign value "1" to the parameter to enable a feature specified
+ * \li millisecond is used as time unit
+ * \li no limit to the parameter order
+ *
+ * For recording:
+ * \code
+ *   dvr_wrapper_test.dataout [tsin=n] [dataout=<stdout>]
+ * \endcode
+ *
+ * \section ops Operations in the test:
+ * \li quit\n
+ *             quit the test
+ * \endsection
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "dvr_segment.h"
+#include "dvr_wrapper.h"
+#include "dvb_utils.h"
+#include "segment_dataout.h"
+
+
+/****************************************************************************
+ * Macro definitions
+ ***************************************************************************/
+#define DMX_DEV_DVR 1
+
+#define REC_EVT(fmt, ...)   fprintf(stderr, "recorder:" fmt, ##__VA_ARGS__)
+#define INF(fmt, ...)       fprintf(stderr, fmt, ##__VA_ARGS__)
+#define ERR(fmt, ...)       fprintf(stderr, "error:" fmt, ##__VA_ARGS__)
+#define RESULT(fmt, ...)    fprintf(stderr, fmt, ##__VA_ARGS__)
+
+static int ts_src=0;
+static FILE *dataout = NULL;
+static int flush_size = 10* 188 * 1024;
+static   DVR_WrapperRecord_t recorder;
+
+
+static void display_usage(void)
+{
+    INF( "==================\n");
+    INF( "*quit\n");
+    INF( "==================\n");
+}
+
+int start_test(void)
+{
+    DVR_Bool_t go = DVR_TRUE;
+    char buf[256];
+
+    display_usage();
+
+    while (go) {
+        if (fgets(buf, sizeof(buf), stdin)) {
+
+            if (!strncmp(buf, "quit", 4)) {
+                go = DVR_FALSE;
+                continue;
+            }
+            else if (!strncmp(buf, "recpause", 8)) {
+                INF("rec paused.\n");
+            }
+            else if (!strncmp(buf, "recresume", 9)) {
+                INF("rec resumed.\n");
+            }
+            else {
+                ERR("Unknown command: %s\n", buf);
+                display_usage();
+            }
+        }
+    }
+
+    if (dataout && dataout != stdout) {
+        fclose(dataout);
+        dataout = NULL;
+    }
+
+    return 0;
+}
+
+static void log_rec_evt(DVR_WrapperRecordStatus_t *status, void *user)
+{
+  char *state[] = {
+    /*DVR_RECORD_STATE_OPENED*/ "open",        /**< Record state is opened*/
+    /*DVR_RECORD_STATE_STARTED*/"start",       /**< Record state is started*/
+    /*DVR_RECORD_STATE_STOPPED*/"stop",        /**< Record state is stopped*/
+    /*DVR_RECORD_STATE_CLOSED*/ "close",       /**< Record state is closed*/
+  };
+  REC_EVT("[%s] state[%s(0x%x)] time/size/pkts[%lu/%llu/%u]\n",
+    (char *)user,
+    state[status->state],
+    status->state,
+    status->info.time,
+    status->info.size,
+    status->info.pkts
+    );
+}
+
+static DVR_Result_t RecEventHandler(DVR_RecordEvent_t event, void *params, void *userdata)
+{
+   if (userdata != NULL)
+   {
+      DVR_WrapperRecordStatus_t *status = (DVR_WrapperRecordStatus_t *)params;
+
+      switch (event)
+      {
+         case DVR_RECORD_EVENT_STATUS:
+            log_rec_evt(status, userdata);
+         break;
+         default:
+            REC_EVT("Unhandled recording event 0x%x from (%s)\n", event, (char *)userdata);
+         break;
+      }
+   }
+   return DVR_SUCCESS;
+}
+
+static int data_cb(unsigned char *buf, size_t size, void *priv)
+{
+    INF("data(%d) priv(%s): %02x %02x %02x %02x\n", size, (char *)priv, buf[0], buf[1], buf[2], buf[3]);
+
+    if (dataout)
+        fwrite(buf, size, 1, dataout);
+
+    return size;
+}
+
+static int start_recording()
+{
+    DVR_WrapperRecordOpenParams_t rec_open_params;
+    DVR_WrapperRecordStartParams_t rec_start_params;
+    DVR_WrapperPidsInfo_t *pids_info;
+    char cmd[256];
+    int error = 0;
+
+    sprintf(cmd, "echo ts%d > /sys/class/stb/demux%d_source", ts_src, DMX_DEV_DVR);
+    //system(cmd);
+
+    dvb_set_demux_source(DMX_DEV_DVR, ts_src);
+
+    memset(&rec_open_params, 0, sizeof(DVR_WrapperRecordOpenParams_t));
+
+    rec_open_params.dmx_dev_id = DMX_DEV_DVR;
+    rec_open_params.segment_size = 0;/*infinite*/
+    rec_open_params.max_size = 0;/*infinite*/
+    rec_open_params.max_time = 0;/*infinite*/
+    rec_open_params.event_fn = RecEventHandler;
+    rec_open_params.event_userdata = "rec0";
+    rec_open_params.flags = DVR_RECORD_FLAG_DATAOUT;
+    rec_open_params.force_sysclock = DVR_TRUE;
+    rec_open_params.flush_size = flush_size;
+    snprintf(rec_open_params.location, DVR_MAX_LOCATION_SIZE, "%s", "dataout");
+
+    error = dvr_wrapper_open_record(&recorder, &rec_open_params);
+    if (error) {
+      ERR( "recorder open fail = (0x%x)\n", error);
+      return -1;
+    }
+
+    INF( "Starting %s recording %p [%ld secs/%llu bytes]\n",
+       "dataout",
+       recorder,
+       rec_open_params.max_time,
+       rec_open_params.max_size);
+
+    Segment_DataoutCallback_t dataout = { data_cb, "datacb_priv"};
+    error = dvr_wrapper_ioctl_record(recorder, SEGMENT_DATAOUT_CMD_SET_CALLBACK, &dataout, sizeof(dataout));
+    if (error) {
+        INF("Set dataout callback fail = (%#x)", error);
+        dvr_wrapper_close_record(recorder);
+        return -1;
+    }
+
+    memset(&rec_start_params, 0, sizeof(rec_start_params));
+
+    pids_info = &rec_start_params.pids_info;
+    pids_info->nb_pids = 1;
+    pids_info->pids[0].pid = 0x2000;
+    pids_info->pids[0].type = DVR_STREAM_TYPE_OTHER << 24;
+    error = dvr_wrapper_start_record(recorder, &rec_start_params);
+    if (error)
+    {
+      ERR( "recorder start fail = (0x%x)\n", error);
+      dvr_wrapper_close_record(recorder);
+      return -1;
+    }
+
+    return 0;
+}
+
+static void usage(int argc, char *argv[])
+{
+  INF( "Usage: record      : %s [tsin=n] [dataout=<stdout>]\n", argv[0]);
+}
+
+
+static void exit_helper() { if (dataout && dataout != stdout) fclose(dataout); }
+void sig_handler(int signo)
+{
+  if (signo == SIGTERM)
+    exit_helper();
+  exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int error;
+    int i;
+    int helper = 0;
+
+    for (i = 1; i < argc; i++) {
+        if (!strncmp(argv[i], "tsin", 4))
+            sscanf(argv[i], "tsin=%i", &ts_src);
+        else if (!strncmp(argv[i], "dataout=stdout", 14))
+            dataout = stdout;
+        else if (!strncmp(argv[i], "flush=", 6))
+            sscanf(argv[i], "flush=%i", &flush_size);
+        else if (!strncmp(argv[i], "help", 4)) {
+            usage(argc, argv);
+            exit(0);
+        }
+    }
+
+    if (argc == 1) {
+      usage(argc, argv);
+      exit(0);
+    }
+
+    if (helper) {
+        atexit(&exit_helper);
+        signal(SIGTERM, &sig_handler);
+        signal(SIGINT, &sig_handler);
+    }
+
+    error = start_recording();
+    if (error != 0) {
+      ERR("start_recording failed with return value %d",error);
+    }
+
+    //command loop
+    start_test();
+
+    error = dvr_wrapper_stop_record(recorder);
+    INF("stop record = (0x%x)\n", error);
+    error = dvr_wrapper_close_record(recorder);
+    INF("close record = (0x%x)\n", error);
+
+    return 0;
+}