[1.0.18] Add media packet source 87/289287/12
authorGilbok Lee <gilbok.lee@samsung.com>
Sat, 4 Mar 2023 08:23:14 +0000 (17:23 +0900)
committerGilbok Lee <gilbok.lee@samsung.com>
Fri, 17 Mar 2023 02:33:13 +0000 (11:33 +0900)
Change-Id: Ib974186105f6015b49b1f30d4ae5f76edcf2e3ec

22 files changed:
include/MediaSourceBinMediaPacket.h [new file with mode: 0644]
include/MediaTransporter.h
include/MediaTransporterCallback.h
include/MediaTransporterGst.h
include/MediaTransporterParam.h
include/MediaTransporterParseIni.h
include/MediaTransporterSender.h
include/mtpr.h
packaging/capi-media-transporter.spec
src/MediaSourceBinFactory.cpp
src/MediaSourceBinMediaPacket.cpp [new file with mode: 0644]
src/MediaTransporter.cpp
src/MediaTransporterCallback.cpp
src/MediaTransporterGst.cpp
src/MediaTransporterParseIni.cpp
src/MediaTransporterSender.cpp
src/MediaTransporterSenderRist.cpp
src/MediaTransporterSenderSrt.cpp
src/MediaTransporterSenderToServerRtsp.cpp
unittest/CMakeLists.txt
unittest/ut_es_reader.hpp [new file with mode: 0644]
unittest/ut_rist_media_packet_sender.cpp [new file with mode: 0644]

diff --git a/include/MediaSourceBinMediaPacket.h b/include/MediaSourceBinMediaPacket.h
new file mode 100644 (file)
index 0000000..1f50f4b
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TIZEN_MEDIA_SOURCE_BIN_MEDIA_PACKET_H__
+#define __TIZEN_MEDIA_SOURCE_BIN_MEDIA_PACKET_H__
+
+#ifdef __cplusplus
+
+#include <string>
+
+#include "MediaTransporter.h"
+#include "MediaTransporterGst.h"
+#include "MediaTransporterParam.h"
+#include "MediaTransporterCallback.h"
+#include "MediaSourceBinBase.h"
+
+namespace tizen_media_transporter {
+
+const std::string DEFAULT_ELEMENT_MEDIA_PACKET = "appsrc";
+
+class MediaSourceBinMediaPacket : public MediaSourceBinBase
+{
+public:
+       MediaSourceBinMediaPacket();
+       ~MediaSourceBinMediaPacket();
+
+       enum formatType
+       {
+               FORMAT_ENCODED_AUDIO,
+               FORMAT_ENCODED_VIDEO,
+               FORMAT_RAW_AUDIO,
+               FORMAT_RAW_VIDEO,
+               FORMAT_CONTAINER,
+               FORMAT_NONE
+       };
+
+       MediaSourceBinInfo generate() override;
+       void setMediaFormat(media_format_h format);
+       void pushMediaPacket(media_packet_h packet);
+       void setBufferStateChangedCallback(void* handle, mtprPacketSourceBufferStateCallback callback, void* userData);
+       void unsetBufferStateChangedCallback();
+
+protected:
+       static void _needDataCallback(GstElement* object, guint size, gpointer data);
+       static void _enoughDataCallback(GstElement* object, gpointer data);
+
+       std::unique_ptr<IInvokable> _bufferStateCallback;
+
+private:
+       void _createMediaPacketSource();
+       void _makeCapsFromMediaFormat(media_format_h format);
+       void _makeVideoCapsFromMediaFormat(media_format_h format);
+       void _makeAudioCapsFromMediaFormat(media_format_h format);
+       GstCaps* _makeH264Caps(media_format_h format);
+       GstCaps* _makeAACCaps(media_format_h format);
+       GstBuffer* _mediaPacketToGstBuffer(media_packet_h packet);
+
+       media_format_h _mediaFormat = nullptr;
+       formatType _formatType = FORMAT_NONE;
+       GstElement* _mediaPacketSource = nullptr;
+       GstCaps* _sourceCaps = nullptr;
+       GList* _signals = nullptr;
+       param::videoInfo _videoInfo;
+       param::audioInfo _audioInfo;
+};
+
+} // namespace
+
+#endif // __cplusplus
+#endif // __TIZEN_MEDIA_SOURCE_BIN_MEDIA_PACKET_H__
index 14cb683..c3d77e5 100644 (file)
@@ -18,10 +18,12 @@ using mtprSourceType = mtpr_source_type_e;
 using mtprMediaType = mtpr_media_type_e;
 using mtprSourceAudioFormat = mtpr_source_audio_format_e;
 using mtprState = mtpr_state_e;
+using mtprPacketSourceBufferState = mtpr_media_packet_source_buffer_state_e;
 using mtprTrackAddedCallback = mtpr_track_added_cb;
 using mtprNoMoreTrackCallback = mtpr_no_more_track_cb;
 using mtprPacketCallback = mtpr_encoded_frame_cb;
 using mtprErrorCallback = mtpr_error_cb;
+using mtprPacketSourceBufferStateCallback = mtpr_media_packet_source_buffer_state_changed_cb;
 using mtprError = mtpr_error_e;
 
 using mtprDisplayType = mtpr_display_type_e;
index 811b3d7..b315eb9 100644 (file)
@@ -29,7 +29,7 @@ extern "C" {
 
 namespace tizen_media_transporter {
 
-using VariantData = std::variant<mtprError, media_packet_h, unsigned int, mtprMediaType>;
+using VariantData = std::variant<mtprError, media_packet_h, unsigned int, mtprMediaType, mtprPacketSourceBufferState>;
 
 class IInvokable
 {
@@ -106,6 +106,18 @@ private:
        mtprErrorCallback _callback;
 };
 
+class PacketSourceBufferStateCallback : public AbstractCallback
+{
+public:
+       PacketSourceBufferStateCallback(void* handle, mtprPacketSourceBufferStateCallback cb, void* userdata);
+       virtual ~PacketSourceBufferStateCallback() = default;
+
+       void invoke(VariantData data1, VariantData data2) override;
+
+private:
+       mtprPacketSourceBufferStateCallback _callback;
+};
+
 } // namespace
 
 #ifdef __cplusplus
index 98a0933..cf074bf 100644 (file)
@@ -111,7 +111,10 @@ std::string _getMimeTypeFromPad(GstPad* pad);
 
 void _generateDot(GstElement* pipeline, std::string name);
 void _dumpPipelineState(GstElement* pipeline);
-void _setPipelineState(GstElement* pipeline, GstState state, int timeout);
+
+void _setElementState(GstElement* Element, GstState state);
+void _waitElementStateChange(GstElement* element, GstState state, int timeout);
+void _setPipelineState(GstElement* pipeline, GstState state, int timeout, bool async = false);
 
 GstAudioFormat _getAudioFormatFromString(std::string format);
 std::string _getVideoMediaType(std::string codec_name);
@@ -121,8 +124,6 @@ GstCaps* _getCapsFromEncodedAudioMediaType(std::string media_type, int channels,
 GstCaps* _makeCapsForCapsfilter(GstPad *pad);
 GstElement* _createElementFromRegistry(std::string klassName, GstCaps* sinkCaps, GstCaps* srcCaps, const std::vector<std::string>& excludedElements);
 
-void _setPipelineState(GstElement* pipeline, GstState state, int timeoutSec);
-
 void _destroyElementFromParent(GstElement* element);
 
 void _printCaps(GstCaps* caps, std::string prefix);
index 58c4dd2..d427c44 100644 (file)
@@ -25,12 +25,14 @@ namespace tizen_media_transporter {
 namespace param {
 
 struct videoInfo {
+       std::string mimeType;
        int width { -1 };
        int height { -1 };
        int frameRate { -1 };
 };
 
 struct audioInfo {
+       std::string mimeType;
        int channels { -1 };
        int rate { -1 };
        std::string format;
index 9cc5a81..6800578 100644 (file)
@@ -37,6 +37,7 @@ namespace tizen_media_transporter {
 #define INI_CATEGORY_SOURCE_MIC           "source mic"
 #define INI_CATEGORY_SOURCE_AUDIOTEST     "source audiotest"
 #define INI_CATEGORY_SOURCE_VIDEOTEST     "source videotest"
+#define INI_CATEGORY_SOURCE_MEDIAPACKET   "source mediapacket"
 #define INI_CATEGORY_RENDERING_SINK       "rendering sink"
 
 /* items for general */
index 1574476..9952858 100644 (file)
@@ -42,6 +42,7 @@ public:
 protected:
        void linkMediaSourceToMuxer(GstElement* mux);
        std::map<unsigned int, std::unique_ptr<IMediaSourceBin>> _mediaSources;
+       bool _asyncStart = false;
 };
 
 } // namespace
index aa6e741..f3235cc 100644 (file)
@@ -110,8 +110,13 @@ typedef enum {
        MTPR_SOURCE_TYPE_MIC,          /**< Audio from microphone */
        MTPR_SOURCE_TYPE_VIDEOTEST,    /**< Video test */
        MTPR_SOURCE_TYPE_AUDIOTEST,    /**< Audio test */
+       MTPR_SOURCE_TYPE_MEDIAPACKET,  /**< Media Packet */
 } mtpr_source_type_e;
 
+/**
+ * @brief Enumeration for Media Transporter audio source format.
+ * @since_tizen 7.0
+ */
 typedef enum {
        MTPR_SOURCE_AUDIO_FORMAT_NONE,
        MTPR_SOURCE_AUDIO_FORMAT_S16LE,
@@ -131,6 +136,15 @@ typedef enum {
 } mtpr_source_audio_format_e;
 
 /**
+ * @brief Enumeration for buffer state type of media packet source.
+ * @since_tizen 7.5
+ */
+typedef enum {
+       MTPR_MEDIA_PACKET_SOURCE_BUFFER_STATE_UNDERFLOW,     /**< Buffer underflow */
+       MTPR_MEDIA_PACKET_SOURCE_BUFFER_STATE_OVERFLOW,      /**< Buffer overflow */
+} mtpr_media_packet_source_buffer_state_e;
+
+/**
  * @brief Definition for mode parameter of SRT.
  * @details The connection mode of SRT.\n
             0: not connected.\n
@@ -288,6 +302,17 @@ typedef void (*mtpr_no_more_track_cb)(mtpr_h mtpr, void *user_data);
 typedef void (*mtpr_encoded_frame_cb)(mtpr_h mtpr, mtpr_media_type_e type, unsigned int track_id, media_packet_h packet, void *user_data);
 
 /**
+ * @brief Called when the buffer state of media packet source is changed.
+ * @since_tizen 7.5
+ * @param[in] source_id  The media source id
+ * @param[in] state      The buffer state (underflow or overflow)
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see mtpr_media_packet_source_set_buffer_state_changed_cb()
+ * @see mtpr_media_packet_source_unset_buffer_state_changed_cb()
+ */
+typedef void (*mtpr_media_packet_source_buffer_state_changed_cb)(unsigned int source_id, mtpr_media_packet_source_buffer_state_e state, void *user_data);
+
+/**
  * @brief Creates an instance of Media Transporter.
  * @since_tizen 7.0
  * @privlevel public
@@ -806,6 +831,83 @@ int mtpr_get_encoding_bitrate(mtpr_h mtpr, unsigned int source_id, int *bitrate)
 int mtpr_mic_source_set_sound_stream_info(mtpr_h mtpr, unsigned int source_id, sound_stream_info_h stream_info);
 
 /**
+ * @brief Sets media format to the media packet source.
+ * @since_tizen 7.5
+ * @remarks The following media format mimetypes can be used to create the @a format :\n
+ *          #MEDIA_FORMAT_H264_SP\n
+ *          #MEDIA_FORMAT_H264_MP\n
+ *          #MEDIA_FORMAT_H264_HP\n
+ *          For more details, please refer to @ref CAPI_MEDIA_TOOL_MEDIA_FORMAT_MODULE.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The mic source id
+ * @param[in] format      The media format
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MTPR_ERROR_INVALID_STATE Invalid state
+ * @pre Add media packet source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @pre @a mtpr state must be set to #MTPR_STATE_IDLE.
+ * @see mtpr_media_packet_source_push_packet()
+ * @see mtpr_media_packet_source_set_buffer_state_changed_cb()
+ */
+int mtpr_media_packet_source_set_format(mtpr_h mtpr, unsigned int source_id, media_format_h format);
+
+/**
+ * @brief Pushes media packet to the media packet source.
+ * @since_tizen 7.5
+ * @remarks This function takes ownership of the @a packet.
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The mic source id
+ * @param[in] packet      The media packet
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MTPR_ERROR_INVALID_OPERATION Invalid operation
+ * @pre Add media packet source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @pre mtpr_media_packet_source_set_format() must be called before calling this function.
+ * @pre mtpr_media_packet_source_buffer_state_changed_cb() must be set by calling mtpr_media_packet_source_set_buffer_state_changed_cb().
+ * @see mtpr_media_packet_source_set_format()
+ * @see mtpr_media_packet_source_set_buffer_state_changed_cb()
+ */
+int mtpr_media_packet_source_push_packet(mtpr_h mtpr, unsigned int source_id, media_packet_h packet);
+
+/**
+ * @brief Sets a callback function to be invoked when the buffer state of media packet source is changed.
+ * @since_tizen 7.5
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The mic source id
+ * @param[in] callback    Callback function pointer
+ * @param[in] user_data   The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre Add media packet source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @post mtpr_media_packet_source_buffer_state_changed_cb() will be invoked.
+ * @see mtpr_media_packet_source_push_packet()
+ * @see mtpr_media_packet_source_unset_buffer_state_changed_cb()
+ * @see mtpr_media_packet_source_buffer_state_changed_cb()
+ */
+int mtpr_media_packet_source_set_buffer_state_changed_cb(mtpr_h mtpr, unsigned int source_id, mtpr_media_packet_source_buffer_state_changed_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the buffer state changed callback function.
+ * @since_tizen 7.5
+ * @param[in] mtpr        Media Transporter handle
+ * @param[in] source_id   The mic source id
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #MTPR_ERROR_NONE    Successful
+ * @retval #MTPR_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre Add media packet source to @a mtpr to get @a source_id by calling mtpr_add_media_source().
+ * @see mtpr_media_packet_source_set_buffer_state_changed_cb()
+ */
+int mtpr_media_packet_source_unset_buffer_state_changed_cb(mtpr_h mtpr, unsigned int source_id);
+
+/**
  * @brief Sets a callback function to be invoked when an asynchronous operation error occurs.
  * @since_tizen 7.0
  * @param[in] mtpr        Media Transporter handle
index 515a5a3..9216479 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-transporter
 Summary:    A Media Transporter library in Tizen Native API
-Version:    1.0.17
+Version:    1.0.18
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index a8caca4..fafadc3 100644 (file)
@@ -20,6 +20,7 @@
 #include "MediaSourceBinMic.h"
 #include "MediaSourceBinVideoTest.h"
 #include "MediaSourceBinAudioTest.h"
+#include "MediaSourceBinMediaPacket.h"
 
 using namespace tizen_media_transporter;
 
@@ -35,6 +36,8 @@ IMediaSourceBin* MediaSourceBinFactory::create(mtprSourceType type)
                return static_cast<IMediaSourceBin*>(new MediaSourceBinVideoTest());
        case MTPR_SOURCE_TYPE_AUDIOTEST:
                return static_cast<IMediaSourceBin*>(new MediaSourceBinAudioTest());
+       case MTPR_SOURCE_TYPE_MEDIAPACKET:
+               return static_cast<IMediaSourceBin*>(new MediaSourceBinMediaPacket());
        default:
                LOG_WARNING("Invalid source type (%d)", static_cast<int>(type));
                return nullptr;
diff --git a/src/MediaSourceBinMediaPacket.cpp b/src/MediaSourceBinMediaPacket.cpp
new file mode 100644 (file)
index 0000000..5e9cd93
--- /dev/null
@@ -0,0 +1,346 @@
+/**
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <gst/app/gstappsrc.h>
+
+#include "MediaSourceBinMediaPacket.h"
+#include "MediaTransporterLog.h"
+#include "MediaTransporterException.h"
+#include "MediaTransporterParseIni.h"
+#include "MediaTransporterParam.h"
+#include "MediaTransporterGst.h"
+
+
+using namespace tizen_media_transporter;
+
+MediaSourceBinMediaPacket::MediaSourceBinMediaPacket()
+{
+       LOG_DEBUG("ctor: %p", this);
+}
+
+MediaSourceBinMediaPacket::~MediaSourceBinMediaPacket()
+{
+       LOG_DEBUG("dtor: %p", this);
+       if (_sourceCaps)
+               gst_caps_unref(_sourceCaps);
+}
+
+void MediaSourceBinMediaPacket::_createMediaPacketSource()
+{
+       try {
+               auto& ini = MediaTransporterIni::get().mediaSource(static_cast<int>(MTPR_SOURCE_TYPE_MEDIAPACKET));
+               if (ini.sourceElement.empty()) {
+                       _mediaPacketSource = gst::_createElement(DEFAULT_ELEMENT_MEDIA_PACKET);
+               } else {
+                       _mediaPacketSource = gst::_createElement(ini.sourceElement);
+               }
+               gst::_setElementProperties(_mediaPacketSource, ini.sourceElementProperties);
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               throw;
+       }
+}
+
+MediaSourceBinInfo MediaSourceBinMediaPacket::generate()
+{
+       gst::GstElements elements;
+
+       try {
+               elements.push_back(_mediaPacketSource);
+               gst::_connectAndAppendSignal(&_signals, G_OBJECT(_mediaPacketSource), "need-data", G_CALLBACK(_needDataCallback), this);
+               gst::_connectAndAppendSignal(&_signals, G_OBJECT(_mediaPacketSource), "enough-data", G_CALLBACK(_enoughDataCallback), this);
+               if (_formatType == FORMAT_ENCODED_VIDEO || _formatType == FORMAT_ENCODED_AUDIO) {
+                       GstCaps* appsrcCaps = nullptr;
+                       g_object_get(_mediaPacketSource, "caps", &appsrcCaps, NULL);
+                       gst::_printCaps(appsrcCaps, "getAppsrcCaps");
+                       GstElement* parser = gst::_createElementFromRegistry("Parser", appsrcCaps, NULL,
+                                                                                                                               MediaTransporterIni::get().general().gstExcludedElements);
+                       if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(parser)), "config-interval")) {
+                               g_object_set(G_OBJECT(parser), "config-interval", -1, NULL);
+                               LOG_DEBUG("[%s] set config-interval -1", GST_ELEMENT_NAME(parser) );
+                       }
+
+                       elements.push_back(parser);
+
+                       GstElement *queue = gst::_createElement("queue", "srcQueue");
+                       g_object_set(G_OBJECT(queue), "max-size-buffers", 1, NULL);
+                       elements.push_back(queue);
+               }
+       } catch (const MediaTransporterException& e) {
+               gst::_disconnectSignal(&_signals, G_OBJECT(_mediaPacketSource));
+               gst::_clearElements(elements);
+               throw;
+       }
+
+       GstBin* bin = GST_BIN(gst_bin_new(nullptr));
+
+       try {
+               gst::_addElementsToBin(bin, elements);
+               gst::_linkElements(elements);
+       } catch (const MediaTransporterException& e) {
+               if (std::string { e.what() } == "Failed to link elements")
+                       gst::_removeElementsFromBin(bin, elements);
+
+               g_object_unref(bin);
+
+               throw;
+       }
+
+       return std::make_tuple(MTPR_SOURCE_TYPE_MEDIAPACKET, bin, ResourceSet());
+}
+
+void MediaSourceBinMediaPacket::setMediaFormat(media_format_h format)
+{
+       _mediaFormat = format;
+       try {
+               _createMediaPacketSource();
+               _makeCapsFromMediaFormat(format);
+               g_object_set(_mediaPacketSource, "caps", _sourceCaps, nullptr);
+               gst::_printCaps(_sourceCaps, "setAppsrcCaps");
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               throw;
+       }
+}
+
+void MediaSourceBinMediaPacket::pushMediaPacket(media_packet_h packet)
+{
+       try {
+               if (_mediaPacketSource == nullptr)
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Media packet source isn't created");
+
+               GstBuffer* buffer = _mediaPacketToGstBuffer(packet);
+               gst_app_src_push_buffer(GST_APP_SRC(_mediaPacketSource), buffer);
+
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("%s", e.what());
+               throw;
+       }
+}
+
+bool isRawFormat(media_format_mimetype_e mimetype)
+{
+       return (mimetype & MEDIA_FORMAT_RAW) == MEDIA_FORMAT_RAW;
+}
+
+void MediaSourceBinMediaPacket::_makeCapsFromMediaFormat(media_format_h format)
+{
+       int ret;
+       media_format_type_e mediaFormatType;
+
+       ret = media_format_get_type(format, &mediaFormatType);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get media format type");
+       }
+
+       if (mediaFormatType == MEDIA_FORMAT_VIDEO) {
+               _makeVideoCapsFromMediaFormat(format);
+       } else if (mediaFormatType == MEDIA_FORMAT_AUDIO) {
+               _makeAudioCapsFromMediaFormat(format);
+       } else {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "Not supported media format");
+       }
+}
+
+
+GstBuffer* MediaSourceBinMediaPacket::_mediaPacketToGstBuffer(media_packet_h packet)
+{
+       GstBuffer *gstBuffer = nullptr;
+
+       guint8 *buf = nullptr;
+       if (media_packet_get_buffer_data_ptr(packet, (void **)&buf) != MEDIA_PACKET_ERROR_NONE) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get buffer data ptr");
+       }
+
+       uint64_t size = 0;
+       if (media_packet_get_buffer_size(packet, &size) != MEDIA_PACKET_ERROR_NONE) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to get buffer size");
+       }
+
+       if (buf == nullptr || size <= 0) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "packet buffer is null or size is zero");
+       }
+
+       gstBuffer = gst_buffer_new_and_alloc(size);
+       if (!gstBuffer) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed to allocate gst buffer");
+       }
+
+       GstMapInfo buff_info = GST_MAP_INFO_INIT;
+       if (!gst_buffer_map(gstBuffer, &buff_info, GST_MAP_READWRITE)) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "failed gstBuffer map");
+       }
+
+       memcpy(buff_info.data, buf, size);
+       buff_info.size = size;
+
+       gst_buffer_unmap(gstBuffer, &buff_info);
+
+       gst_buffer_set_size(gstBuffer, size);
+
+       uint64_t pts = 0;
+       media_packet_get_pts(packet, &pts);
+       GST_BUFFER_PTS(gstBuffer) = static_cast<GstClockTime>(pts);
+
+       uint64_t duration = 0;
+       media_packet_get_duration(packet, &duration);
+       GST_BUFFER_DURATION(gstBuffer) = static_cast<GstClockTime>(duration);
+
+       return gstBuffer;
+}
+
+void MediaSourceBinMediaPacket::_makeAudioCapsFromMediaFormat(media_format_h format)
+{
+       int ret;
+       media_format_mimetype_e mimetype;
+       ret = media_format_get_audio_info(format, &mimetype, &_audioInfo.channels, &_audioInfo.rate, nullptr, nullptr);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("Fail to get audio info");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get audio info");
+       }
+
+       if (isRawFormat(mimetype)) {
+               _formatType = FORMAT_RAW_AUDIO;
+               _audioInfo.mimeType = "audio/x-raw";
+       } else {
+               _formatType = FORMAT_ENCODED_AUDIO;
+               if ((mimetype >= MEDIA_FORMAT_AAC) && (mimetype <= MEDIA_FORMAT_AAC_HE_PS)) {
+                       LOG_DEBUG("Input format is aac");
+                       _sourceCaps = _makeAACCaps(format);
+               } else {
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "Not supported audio format");
+               }
+       }
+}
+
+void MediaSourceBinMediaPacket::_makeVideoCapsFromMediaFormat(media_format_h format)
+{
+       int ret;
+       media_format_mimetype_e mimetype;
+       ret = media_format_get_video_info(format, &mimetype, nullptr, nullptr, nullptr, nullptr);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("Fail to get video info");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get video info");
+       }
+
+       if (isRawFormat(mimetype)) {
+               _formatType = FORMAT_RAW_VIDEO;
+               _videoInfo.mimeType = "video/x-raw";
+       } else {
+               _formatType = FORMAT_ENCODED_VIDEO;
+               if ((mimetype >= MEDIA_FORMAT_H264_SP) && (mimetype <= MEDIA_FORMAT_H264_C444P)) {
+                       LOG_DEBUG("Input format is H264");
+                       _sourceCaps =_makeH264Caps(format);
+               } else {
+                       throw MediaTransporterException(MTPR_ERROR_INVALID_PARAMETER, "Not supported video format");
+               }
+       }
+}
+
+GstCaps* MediaSourceBinMediaPacket::_makeH264Caps(media_format_h format)
+{
+       int ret;
+       int width = 0;
+       int height = 0;
+       int frameRate = 0;
+       GstCaps* caps = nullptr;
+
+       ret = media_format_get_video_info(format, nullptr, &width, &height, nullptr, nullptr);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("Fail to get video info");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get video info");
+       }
+
+       ret = media_format_get_video_frame_rate(format, &frameRate);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get video frame rate");
+       }
+
+       caps = gst_caps_new_empty_simple("video/x-h264");
+       gst_caps_set_simple(caps, "stream-format", G_TYPE_STRING, "byte-stream", nullptr);
+
+       if (width > 0)
+               gst_caps_set_simple(caps, "width", G_TYPE_INT, width, nullptr);
+
+       if (height > 0)
+               gst_caps_set_simple(caps, "height", G_TYPE_INT, height, nullptr);
+
+       if (frameRate > 0)
+               gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION,
+                               frameRate, 1, nullptr);
+
+       return caps;
+}
+
+GstCaps* MediaSourceBinMediaPacket::_makeAACCaps(media_format_h format)
+{
+       int ret;
+       int channels = 0;
+       int rate = 0;
+       GstCaps* caps = nullptr;
+
+       ret = media_format_get_audio_info(format, nullptr, &channels, &rate, nullptr, nullptr);
+       if (ret != MEDIA_FORMAT_ERROR_NONE) {
+               LOG_ERROR("Fail to get audio info");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Fail to get audio info");
+       }
+
+       caps = gst_caps_new_simple("audio/mpeg",
+                                       "mpegversion", G_TYPE_INT, 4, nullptr);
+
+       if (channels > 0)
+               gst_caps_set_simple(caps, "channels", G_TYPE_INT, channels, nullptr);
+
+       if (rate > 0)
+               gst_caps_set_simple(caps, "rate", G_TYPE_INT, rate, nullptr);
+
+       return caps;
+}
+
+void MediaSourceBinMediaPacket::setBufferStateChangedCallback(void* handle, mtprPacketSourceBufferStateCallback callback, void* userData)
+{
+       _bufferStateCallback = std::unique_ptr<IInvokable>(new PacketSourceBufferStateCallback(handle, callback, userData));
+}
+
+void MediaSourceBinMediaPacket::unsetBufferStateChangedCallback()
+{
+       _bufferStateCallback = nullptr;
+}
+
+void MediaSourceBinMediaPacket::_needDataCallback(GstElement* object, guint size, gpointer data)
+{
+       auto mtprMediaSource = static_cast<MediaSourceBinMediaPacket*>(data);
+       RET_IF(!mtprMediaSource, "mtprMediaSource is NULL");
+
+       if (mtprMediaSource->_bufferStateCallback) {
+               auto sourceId = mtprMediaSource->sourceId();
+               LOG_DEBUG("need data is called (%u)", sourceId);
+               mtprMediaSource->_bufferStateCallback->invoke(VariantData{sourceId}, VariantData{MTPR_MEDIA_PACKET_SOURCE_BUFFER_STATE_UNDERFLOW});
+       }
+}
+
+void MediaSourceBinMediaPacket::_enoughDataCallback(GstElement* object, gpointer data)
+{
+       auto mtprMediaSource = static_cast<MediaSourceBinMediaPacket*>(data);
+       RET_IF(!mtprMediaSource, "mtprMediaSource is NULL");
+
+       if (mtprMediaSource->_bufferStateCallback) {
+               unsigned int sourceId = mtprMediaSource->sourceId();
+               LOG_DEBUG("enough data is called (%u)", sourceId);
+               mtprMediaSource->_bufferStateCallback->invoke(VariantData{sourceId}, VariantData{MTPR_MEDIA_PACKET_SOURCE_BUFFER_STATE_OVERFLOW});
+       }
+}
index 2f0f2a3..3f21a70 100644 (file)
@@ -12,6 +12,7 @@
 #include "MediaTransporterReceiver.h"
 #include "MediaTransporterUtil.h"
 #include "MediaSourceBinMic.h"
+#include "MediaSourceBinMediaPacket.h"
 
 #include <cassert>
 #include <memory>
@@ -544,6 +545,103 @@ int mtpr_media_source_get_audio_format(mtpr_h mtpr, unsigned int source_id, mtpr
        return MTPR_ERROR_NONE;
 }
 
+int mtpr_media_packet_source_set_format(mtpr_h mtpr, unsigned int source_id, media_format_h format)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+       RET_ERR_IF_NULL_ARG(format);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       try {
+               MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+               RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+               MediaSourceBinMediaPacket* mediaPacketsourceBin = dynamic_cast<MediaSourceBinMediaPacket*>(sender->getMediaSource(source_id));
+               RET_VAL_IF(!mediaPacketsourceBin, MTPR_ERROR_INVALID_PARAMETER, "Invalid source id");
+
+               mediaPacketsourceBin->setMediaFormat(format);
+
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set media packet format!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_media_packet_source_push_packet(mtpr_h mtpr, unsigned int source_id, media_packet_h packet)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       try {
+               MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+               RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+               MediaSourceBinMediaPacket* mediaPacketsourceBin = dynamic_cast<MediaSourceBinMediaPacket*>(sender->getMediaSource(source_id));
+               RET_VAL_IF(!mediaPacketsourceBin, MTPR_ERROR_INVALID_PARAMETER, "Invalid source id");
+
+               mediaPacketsourceBin->pushMediaPacket(packet);
+
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to push media packet!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_media_packet_source_set_buffer_state_changed_cb(mtpr_h mtpr, unsigned int source_id, mtpr_media_packet_source_buffer_state_changed_cb callback, void *user_data)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       try {
+               MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+               RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+               MediaSourceBinMediaPacket* mediaPacketsourceBin = dynamic_cast<MediaSourceBinMediaPacket*>(sender->getMediaSource(source_id));
+               RET_VAL_IF(!mediaPacketsourceBin, MTPR_ERROR_INVALID_PARAMETER, "Invalid source id");
+
+               mediaPacketsourceBin->setBufferStateChangedCallback(mtpr, callback, user_data);
+
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to set media packet source state changed callback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
+int mtpr_media_packet_source_unset_buffer_state_changed_cb(mtpr_h mtpr, unsigned int source_id)
+{
+       RET_ERR_IF_INVALID_INSTANCE(mtpr);
+
+       auto handle = static_cast<media_transporter_s*>(mtpr);
+       assert(handle->base);
+
+       try {
+               MediaTransporterSender* sender = dynamic_cast<MediaTransporterSender*>(handle->base.get());
+               RET_VAL_IF(!sender, MTPR_ERROR_INVALID_OPERATION, "only sender support this api!!!");
+
+               MediaSourceBinMediaPacket* mediaPacketsourceBin = dynamic_cast<MediaSourceBinMediaPacket*>(sender->getMediaSource(source_id));
+               RET_VAL_IF(!mediaPacketsourceBin, MTPR_ERROR_INVALID_PARAMETER, "Invalid source id");
+
+               mediaPacketsourceBin->unsetBufferStateChangedCallback();
+
+       } catch (const MediaTransporterException& e) {
+               LOG_ERROR("Failed to unset media packet source state changed callback!!! : %s", e.what());
+               return e.error();
+       }
+
+       return MTPR_ERROR_NONE;
+}
+
 int mtpr_mic_source_set_sound_stream_info(mtpr_h mtpr, unsigned int source_id, sound_stream_info_h stream_info)
 {
        RET_ERR_IF_INVALID_INSTANCE(mtpr);
index 9df9feb..d7c754d 100644 (file)
@@ -86,4 +86,22 @@ void ErrorCallback::invoke(VariantData data)
                        _callback, _handle, error, _userdata);
 
        _callback(_handle, error, _userdata);
+}
+
+PacketSourceBufferStateCallback::PacketSourceBufferStateCallback(void* handle, mtprPacketSourceBufferStateCallback cb, void* userdata)
+                               : AbstractCallback(handle, userdata), _callback(cb)
+{
+       LOG_DEBUG(">>> callback[%p], handle[%p], user_data[%p] registered",
+                       cb, handle, userdata);
+}
+
+void PacketSourceBufferStateCallback::invoke(VariantData data1, VariantData data2)
+{
+       auto sourceId = std::get<unsigned int>(data1);
+       auto state = std::get<mtprPacketSourceBufferState>(data2);
+
+       LOG_DEBUG(">>> callback[%p], handle[%p], sourcdId[%d], state[%d], user_data[%p]",
+                       _callback, _handle, sourceId, state, _userdata);
+
+       _callback(sourceId, state, _userdata);
 }
\ No newline at end of file
index 9ec7137..6743317 100644 (file)
@@ -662,44 +662,50 @@ void gst::_dumpPipelineState(GstElement* pipeline)
                gst_iterator_free(iter);
 }
 
-void gst::_setPipelineState(GstElement *pipeline, GstState state, int timeout)
+void gst::_setElementState(GstElement* Element, GstState state)
 {
-       GstState element_state = GST_STATE_VOID_PENDING;
-       GstState element_pending_state = GST_STATE_VOID_PENDING;
-
-       if (!pipeline) {
-               LOG_ERROR("pipeline is null");
-               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "pipeline is null");
+       if (!Element) {
+               LOG_ERROR("Element is null");
+               throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Element is null");
        }
 
-       GstStateChangeReturn ret = gst_element_set_state(pipeline, state);
+       GstStateChangeReturn ret = gst_element_set_state(Element, state);
        if (ret == GST_STATE_CHANGE_FAILURE) {
                LOG_ERROR("failed to change [%s] element state to [%s]",
-                       GST_ELEMENT_NAME(pipeline), gst_element_state_get_name(state));
+                       GST_ELEMENT_NAME(Element), gst_element_state_get_name(state));
 
-               _dumpPipelineState(pipeline);
+               _dumpPipelineState(Element);
 
                throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to set state!!");
        }
+}
+
+void gst::_waitElementStateChange(GstElement* element, GstState state, int timeout)
+{
+       GstState element_state = GST_STATE_VOID_PENDING;
+       GstState element_pending_state = GST_STATE_VOID_PENDING;
 
-       /* wait for state transition */
-       ret = gst_element_get_state(pipeline, &element_state, &element_pending_state, timeout * GST_SECOND);
+       GstStateChangeReturn ret = gst_element_get_state(element, &element_state, &element_pending_state, timeout * GST_SECOND);
        if (ret == GST_STATE_CHANGE_FAILURE || (state != element_state)) {
                LOG_ERROR("failed to change [%s] element state to [%s] within %d sec",
-                       GST_ELEMENT_NAME(pipeline), gst_element_state_get_name(state), timeout);
+                       GST_ELEMENT_NAME(element), gst_element_state_get_name(state), timeout);
 
                LOG_ERROR(" [%s] state : %s   pending : %s",
-                       GST_ELEMENT_NAME(pipeline),
+                       GST_ELEMENT_NAME(element),
                        gst_element_state_get_name(element_state),
                        gst_element_state_get_name(element_pending_state));
 
-               _dumpPipelineState(pipeline);
+               _dumpPipelineState(element);
 
                throw MediaTransporterException(MTPR_ERROR_INVALID_OPERATION, "Failed to get state!!");
        }
-
-       std::string dotName = std::string { GST_ELEMENT_NAME(pipeline) } + "." + std::string { gst_element_state_get_name(state) };
-       _generateDot(pipeline, dotName);
+}
+void gst::_setPipelineState(GstElement *pipeline, GstState state, int timeout, bool async)
+{
+       _setElementState(pipeline, state);
+       if (!async)
+               _waitElementStateChange(pipeline, state, timeout);
+       _generateDot(pipeline, std::string { GST_ELEMENT_NAME(pipeline) } + "." + std::string { gst_element_state_get_name(state) });
 }
 
 GstCaps* gst::_makeCapsForCapsfilter(GstPad *pad)
index 9c4b337..0b0d9e2 100644 (file)
@@ -206,7 +206,8 @@ static const char* category_source_names[] = {
        [MTPR_SOURCE_TYPE_MIC] = INI_CATEGORY_SOURCE_MIC,
        [MTPR_SOURCE_TYPE_VIDEOTEST] = INI_CATEGORY_SOURCE_VIDEOTEST,
        [MTPR_SOURCE_TYPE_AUDIOTEST] = INI_CATEGORY_SOURCE_AUDIOTEST,
-       [MTPR_SOURCE_TYPE_AUDIOTEST + 1] = NULL,
+       [MTPR_SOURCE_TYPE_MEDIAPACKET] = INI_CATEGORY_SOURCE_MEDIAPACKET,
+       [MTPR_SOURCE_TYPE_MEDIAPACKET + 1] = NULL,
 };
 
 void MediaTransporterIni::dumpIni()
index 55ced15..01c793e 100644 (file)
@@ -66,6 +66,9 @@ void MediaTransporterSender::linkMediaSourceToMuxer(GstElement* mux)
                        LOG_INFO("mediaSource(type:%d, bin:%p, resourceRequired:%zu) generated",
                                        static_cast<int>(type), bin, resourceRequired.size());
 
+                       if (type == MTPR_SOURCE_TYPE_MEDIAPACKET)
+                               _asyncStart = true;
+
                        _resourceManager->acquire(resourceRequired);
 
                        gst_bin_add(GST_BIN(_gst.pipeline), GST_ELEMENT(bin));
index 84c3a2d..3e12ceb 100644 (file)
@@ -119,7 +119,7 @@ void MediaTransporterSenderRist::buildPipeline()
 void MediaTransporterSenderRist::startPipeline()
 {
        gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
-                                                       MediaTransporterIni::get().general().timeout);
+                                               MediaTransporterIni::get().general().timeout, _asyncStart);
 }
 
 void MediaTransporterSenderRist::stopPipeline()
index aae5349..9bef241 100644 (file)
@@ -203,7 +203,7 @@ void MediaTransporterSenderSrt::buildPipeline()
 void MediaTransporterSenderSrt::startPipeline()
 {
        gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
-                                                       MediaTransporterIni::get().general().timeout);
+                                               MediaTransporterIni::get().general().timeout, _asyncStart);
        startStatsMonitoring();
 }
 
index f8b2e6a..4746a90 100644 (file)
@@ -102,7 +102,7 @@ void MediaTransporterSenderToServerRtsp::buildPipeline()
 void MediaTransporterSenderToServerRtsp::startPipeline()
 {
        gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
-                                                       MediaTransporterIni::get().general().timeout);
+                                                       MediaTransporterIni::get().general().timeout, _asyncStart);
 }
 
 void MediaTransporterSenderToServerRtsp::stopPipeline()
index 98d4a91..83a22a1 100644 (file)
@@ -52,6 +52,8 @@ SET(UT_SRC
   ut_rtsp_receiver.cpp
   ut_srt_sender.cpp
   ut_srt_receiver.cpp
+  ut_rist_media_packet_sender.cpp
+  ut_es_reader.hpp
   ut_main.cpp
 )
 
diff --git a/unittest/ut_es_reader.hpp b/unittest/ut_es_reader.hpp
new file mode 100644 (file)
index 0000000..39a0a93
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MTPR_UT_INCLUDE_ES_READER_H__
+#define __MTPR_UT_INCLUDE_ES_READER_H__
+
+#include <glib.h>
+#include <string>
+#include <fstream>
+
+#include <dlog.h>
+#include <mtpr.h>
+#include <media_packet.h>
+
+#undef LOG_TAG
+#define LOG_TAG "MTPR_UT"
+
+typedef enum {
+       NAL_SLICE_NO_PARTITIONING = 1,
+       NAL_SLICE_PART_A,
+       NAL_SLICE_PART_B,
+       NAL_SLICE_PART_C,
+       NAL_SLICE_IDR,
+       NAL_SEI,
+       NAL_SEQUENCE_PARAMETER_SET,
+       NAL_PICTURE_PARAMETER_SET,
+       NAL_PICTURE_DELIMITER,
+       NAL_END_OF_SEQUENCE,
+       NAL_END_OF_STREAM,
+       NAL_FILLER_DATA,
+       NAL_PREFIX_SVC = 14
+} nal_unit_type;
+
+#define ES_DEFAULT_VIDEO_PTS_OFFSET       41667000
+#define ES_DEFAULT_VIDEO_FPS           24
+
+#define ES_DEFAULT_AUDIO_PTS_OFFSET    23219955
+
+static const std::string tc_root_dir = "/home/owner/";
+
+class EsStreamReader {
+public:
+       explicit EsStreamReader(const std::string dirpath,
+                                                                                                       mtpr_media_type_e type) {
+               _dirPath = tc_root_dir + dirpath;
+               _ESDataFile = _dirPath + "MTPR.es";
+               _ESInfoFile = _dirPath + "MTPR.info";
+               _type = type;
+               LOGD("ES data file %s", _ESDataFile.c_str());
+               makeMediaFormat();
+       }
+       ~EsStreamReader() {
+               LOGD("[tearDown] ES data file %s", _ESDataFile.c_str());
+               if (_stream.is_open()) {
+                       _stream.close();
+               }
+
+               if (_mediaFormat)
+                       media_format_unref(_mediaFormat);
+       }
+
+       bool makeMediaFormat() {
+               media_format_create(&_mediaFormat);
+               if (_type == MTPR_MEDIA_TYPE_AUDIO) {
+                       uint32_t mimeType;
+                       uint32_t sampleRate;
+                       uint32_t channels;
+                       auto formatStream = std::ifstream(_ESInfoFile, std::ifstream::in);
+                       if (!formatStream.is_open()) {
+                               LOGW("No audio info file: %s", _ESInfoFile.c_str());
+                               return false;
+                       }
+                       formatStream >> mimeType >> sampleRate >> channels;
+                       LOGD("mime_type: %u, sampleRate: %u, channels: %u",
+                               mimeType, sampleRate, channels);
+                       formatStream.close();
+
+                       media_format_set_audio_mime(_mediaFormat, static_cast<media_format_mimetype_e>(mimeType));
+                       media_format_set_audio_samplerate(_mediaFormat, sampleRate);
+                       media_format_set_audio_channel(_mediaFormat, channels);
+
+                       return true;
+
+               } else if (_type == MTPR_MEDIA_TYPE_VIDEO) {
+                       uint32_t mimeType;
+                       uint32_t width;
+                       uint32_t height;
+                       uint32_t frameRate;
+                       auto formatStream = std::ifstream(_ESInfoFile, std::ifstream::in);
+                       if (!formatStream.is_open()) {
+                               LOGW("No video info file: %s", _ESInfoFile.c_str());
+                               return false;
+                       }
+                       formatStream >> mimeType >> width >> height >> frameRate;
+                       LOGD("mime_type: %u, width: %u, height: %u, frameRate: %u",
+                               mimeType, width, height, frameRate);
+                       formatStream.close();
+
+                       media_format_set_video_mime(_mediaFormat, static_cast<media_format_mimetype_e>(mimeType));
+                       media_format_set_video_width(_mediaFormat, width);
+                       media_format_set_video_height(_mediaFormat, height);
+                       media_format_set_video_frame_rate(_mediaFormat, frameRate);
+
+                       return true;
+               }
+               return false;
+       }
+
+       media_format_h getMediaFormat() { return _mediaFormat; }
+
+       bool ReadNextPacket(media_packet_h* outPacket) {
+               static uint64_t pts = 0L;
+               media_packet_h packet;
+
+               if (!_stream.is_open()) {
+                       _stream = std::ifstream(_ESDataFile, std::ifstream::binary);
+                       if (!_stream.is_open()) return false;
+               }
+
+               if (_stream.eof() || GetFileLeftSize_() < 24) {
+                       LOGW("stream EOF");
+                       return false;
+               }
+
+               if (_mediaFormat == nullptr) {
+                       LOGW("mediaFormat not set");
+                       return false;
+               }
+
+               char* tmp = new char[1000000];
+
+               if (_type == MTPR_MEDIA_TYPE_VIDEO) {
+                       int size = videobytestream2nalunit(tmp);
+                       if (size <= 0) {
+                               delete[] tmp;
+                               return false;
+                       }
+
+                       void *buffer = nullptr;
+                       media_packet_new_alloc(_mediaFormat, nullptr, nullptr, &packet);
+                       media_packet_set_pts(packet, pts);
+                       media_packet_set_duration(packet, ES_DEFAULT_VIDEO_PTS_OFFSET);
+                       media_packet_set_buffer_size(packet, size);
+                       media_packet_get_buffer_data_ptr(packet, &buffer);
+
+                       memcpy(buffer, tmp, size);
+
+                       pts += ES_DEFAULT_VIDEO_PTS_OFFSET;
+
+                       *outPacket = packet;
+
+               } else if (_type == MTPR_MEDIA_TYPE_AUDIO) {
+                       int size = audiobytestream2nalunit(tmp);
+                       if (size <= 0) {
+                               delete[] tmp;
+                               return false;
+                       }
+
+                       void *buffer = nullptr;
+                       media_packet_new_alloc(_mediaFormat, nullptr, nullptr, &packet);
+                       media_packet_set_pts(packet, pts);
+                       media_packet_set_duration(packet, ES_DEFAULT_AUDIO_PTS_OFFSET);
+                       media_packet_set_buffer_size(packet, size);
+                       media_packet_get_buffer_data_ptr(packet, &buffer);
+
+                       memcpy(buffer, tmp, size);
+
+                       pts += ES_DEFAULT_AUDIO_PTS_OFFSET;
+
+                       *outPacket = packet;
+               }
+
+               delete[] tmp;
+               return true;
+       }
+
+       void ResetReader() {
+               _stream.seekg(0,std::ios::beg);
+       }
+
+private:
+       int videobytestream2nalunit(char *nal)
+       {
+               int nal_length = 0;
+               int read_size = 1;
+               char buffer[1000000];
+               unsigned char val, zero_count, i;
+               int init = 0;
+               int type = 0;
+
+               do {
+                       zero_count = 0;
+
+                       _stream.read(buffer, read_size);
+
+                       val = buffer[0];
+                       nal[nal_length++] = val;
+                       while (1) {
+                               if ((zero_count == 2 || zero_count == 3) && val == 1)
+                                       break;
+                               zero_count++;
+                               _stream.read(buffer, read_size);
+
+                               val = buffer[0];
+                               nal[nal_length++] = val;
+                       }
+
+                       zero_count = 0;
+                       init = 1;
+
+                       while (1) {
+                               if (_stream.eof())
+                                       return -1;
+
+                               _stream.read(buffer, read_size);
+
+                               val = buffer[0];
+
+                               if (init) {
+                                       type = val & 0x1F;
+                                       init = 0;
+                               }
+
+                               if (!val) {
+                                       zero_count++;
+                               } else {
+                                       if ((zero_count == 2 || zero_count == 3 || zero_count == 4)
+                                               && (val == 1)) {
+                                               break;
+                                       } else {
+                                               for (i = 0; i < zero_count; i++)
+                                                       nal[nal_length++] = 0;
+                                               nal[nal_length++] = val;
+                                               zero_count = 0;
+                                       }
+                               }
+                       }
+
+                       _stream.seekg(-(zero_count + 1), std::ios::cur);
+               } while (type == NAL_SEI ||
+                               type == NAL_SEQUENCE_PARAMETER_SET ||
+                               type == NAL_PICTURE_PARAMETER_SET ||
+                               type == NAL_PICTURE_DELIMITER);
+
+
+               return nal_length;
+       }
+
+       int audiobytestream2nalunit(char *nal)
+       {
+               const int HEADER_SIZE = 7;
+               int nal_length = 0;
+               int audio_data_length = 0;
+
+               if (nal == NULL || _stream.eof())
+                       return -1;
+
+               _stream.read(nal, HEADER_SIZE);
+
+               nal_length = static_cast<int>(((nal[4] << 3) & 0x7ff)
+                       + (((nal[5] - 0x1f) >> 5) & 0x7));
+
+               audio_data_length = nal_length - HEADER_SIZE;
+               _stream.read(nal + HEADER_SIZE, audio_data_length);
+
+               return nal_length;
+       }
+
+       int GetFileLeftSize_(void) {
+               if (!_stream.is_open()) return 0;
+               int cur = _stream.tellg();
+               _stream.seekg(0, _stream.end);
+               int total = _stream.tellg();
+               _stream.seekg(cur);
+               return total - cur;
+       }
+
+private:
+       std::string _dirPath;
+       std::string _ESDataFile;
+       std::string _ESInfoFile;
+       std::ifstream _stream;
+       mtpr_media_type_e _type;
+       media_format_h _mediaFormat = nullptr;
+};
+
+#endif  // __MTPR_UT_INCLUDE_ES_READER_H__
diff --git a/unittest/ut_rist_media_packet_sender.cpp b/unittest/ut_rist_media_packet_sender.cpp
new file mode 100644 (file)
index 0000000..ce2d57e
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "ut_base.hpp"
+#include "ut_es_reader.hpp"
+
+using namespace std::chrono_literals;
+
+static const unsigned numberOfVideoFeedingPacket = 100;
+static const unsigned numberOfAudioFeedingPacket = 200;
+
+static void bufferStateCb(unsigned int sourceId, mtpr_media_packet_source_buffer_state_e state, void *user_data) {
+       LOGD("callback is invoked. sourceId: %d, state: %d", sourceId, state);
+
+       static std::once_flag flag;
+
+       std::call_once(flag, [&](){
+               auto p = static_cast<std::promise<bool>*>(user_data);
+               assert(p);
+               p->set_value(true);
+       });
+}
+
+static bool waitBufferStateCallback(std::future<bool>& f) {
+       LOGI("start waiting for buffer state callback arrival...");
+
+       if (f.wait_for(1s) != std::future_status::ready) {
+               LOGW("timeout(1s)!!!");
+               return false;
+       }
+
+       LOGI("ready to get the result!");
+       return f.get();
+}
+
+class MediaTransporterTestRistPacketSender : public MediaTransporterTestBase {
+public:
+       MediaTransporterTestRistPacketSender() = default;
+       ~MediaTransporterTestRistPacketSender() = default;
+
+       void SetUp() override {
+               LOGD("Enter");
+
+               int ret = mtpr_create(MTPR_CONNECTION_TYPE_RIST_SENDER, &_mtpr);
+               ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+               LOGD("Leave");
+       }
+
+       void TearDown() override {
+               int ret = MTPR_ERROR_NONE;
+               mtpr_state_e state = MTPR_STATE_IDLE;
+               LOGD("Enter");
+
+               if (_videoEsReader != nullptr)
+                       delete _videoEsReader;
+
+               if (_audioEsReader != nullptr)
+                       delete _audioEsReader;
+
+               if (_mtpr) {
+                       ret = mtpr_get_state(_mtpr, &state);
+                       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+                       if (state != MTPR_STATE_IDLE) {
+                               ret = mtpr_stop(_mtpr);
+                               ASSERT_EQ(ret, MTPR_ERROR_NONE);
+                       }
+                       ret = mtpr_destroy(_mtpr);
+                       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+               }
+               LOGD("Leave");
+       }
+
+       std::thread feedingEsPacket(mtpr_h mtpr, const mtpr_media_type_e type, unsigned int source_id, unsigned int feedingPacketCount);
+
+protected:
+       const std::string _receiverPath = "127.0.0.1:5004";
+       EsStreamReader* _videoEsReader = nullptr;
+       EsStreamReader* _audioEsReader = nullptr;
+       std::thread _audioFeeder;
+       std::thread _videoFeeder;
+};
+
+std::thread MediaTransporterTestRistPacketSender::feedingEsPacket(mtpr_h mtpr, const mtpr_media_type_e type, unsigned int source_id, unsigned int feedingPacketCount)
+{
+       EsStreamReader* reader = nullptr;
+       reader = (type == MTPR_MEDIA_TYPE_VIDEO) ?      _videoEsReader : _audioEsReader;
+
+       auto feeding_task_fn = [mtpr, source_id, reader, feedingPacketCount]() {
+               unsigned int leftFeedingPacket = feedingPacketCount;
+               while (leftFeedingPacket--) {
+                       media_packet_h pkt = nullptr;
+
+                       if (!reader->ReadNextPacket(&pkt))
+                               break;
+
+                       mtpr_media_packet_source_push_packet(mtpr, source_id, pkt);
+                       media_packet_unref(pkt);
+                       std::this_thread::sleep_for(std::chrono::milliseconds(10));
+               }
+       };
+
+       return std::thread(feeding_task_fn);
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, set_media_packet_source)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+       EXPECT_GE(source_id, 0);
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, set_media_format)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(source_id, 0);
+
+       _videoEsReader = new EsStreamReader("video/", MTPR_MEDIA_TYPE_VIDEO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, source_id, _videoEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, push_media_video_packet)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(source_id, 0);
+
+       _videoEsReader = new EsStreamReader("video/", MTPR_MEDIA_TYPE_VIDEO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, source_id, _videoEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_start(_mtpr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       _videoFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_VIDEO, source_id, numberOfVideoFeedingPacket);
+       if (_videoFeeder.joinable())
+               _videoFeeder.join();
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, push_media_audio_packet)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(source_id, 0);
+
+       _audioEsReader = new EsStreamReader("audio/", MTPR_MEDIA_TYPE_AUDIO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, source_id, _audioEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_start(_mtpr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       _audioFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_AUDIO, source_id, numberOfAudioFeedingPacket);
+               _audioFeeder.join();
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, push_media_av_packet)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int audioSourceID = 0;
+       unsigned int videoSourceID = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &audioSourceID);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(audioSourceID, 0);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &videoSourceID);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(videoSourceID, 0);
+
+       _audioEsReader = new EsStreamReader("audio/", MTPR_MEDIA_TYPE_AUDIO);
+       _videoEsReader = new EsStreamReader("video/", MTPR_MEDIA_TYPE_VIDEO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, audioSourceID, _audioEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, videoSourceID, _videoEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_start(_mtpr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       _audioFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_AUDIO, audioSourceID, numberOfAudioFeedingPacket);
+       _videoFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_VIDEO, videoSourceID, numberOfVideoFeedingPacket);
+
+       if (_audioFeeder.joinable())
+               _audioFeeder.join();
+
+       if (_videoFeeder.joinable())
+               _videoFeeder.join();
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, set_buffer_state_callback)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(source_id, 0);
+
+       _videoEsReader = new EsStreamReader("video/", MTPR_MEDIA_TYPE_VIDEO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, source_id, _videoEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       std::promise<bool> p;
+       std::future<bool> f = p.get_future();
+       ret = mtpr_media_packet_source_set_buffer_state_changed_cb(_mtpr, source_id, bufferStateCb, &p);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_start(_mtpr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       _videoFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_VIDEO, source_id, numberOfVideoFeedingPacket);
+
+       ASSERT_TRUE(waitBufferStateCallback(f));
+
+       if (_videoFeeder.joinable())
+               _videoFeeder.join();
+}
+
+TEST_F(MediaTransporterTestRistPacketSender, unset_buffer_state_callback)
+{
+       int ret = MTPR_ERROR_NONE;
+       unsigned int source_id = 0;
+
+       ret = mtpr_set_receiver_address(_mtpr, _receiverPath.c_str());
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_add_media_source(_mtpr, MTPR_SOURCE_TYPE_MEDIAPACKET, &source_id);
+       ASSERT_EQ(ret, MTPR_ERROR_NONE);
+       ASSERT_GE(source_id, 0);
+
+       _videoEsReader = new EsStreamReader("video/", MTPR_MEDIA_TYPE_VIDEO);
+
+       ret = mtpr_media_packet_source_set_format(_mtpr, source_id, _videoEsReader->getMediaFormat());
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_media_packet_source_set_buffer_state_changed_cb(_mtpr, source_id, bufferStateCb, nullptr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_media_packet_source_unset_buffer_state_changed_cb(_mtpr, source_id);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       ret = mtpr_start(_mtpr);
+       EXPECT_EQ(ret, MTPR_ERROR_NONE);
+
+       _videoFeeder = feedingEsPacket(_mtpr, MTPR_MEDIA_TYPE_VIDEO, source_id, numberOfVideoFeedingPacket);
+
+       if (_videoFeeder.joinable())
+               _videoFeeder.join();
+}
\ No newline at end of file