--- /dev/null
+/**
+ * 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__
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;
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
{
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
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);
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);
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;
#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 */
protected:
void linkMediaSourceToMuxer(GstElement* mux);
std::map<unsigned int, std::unique_ptr<IMediaSourceBin>> _mediaSources;
+ bool _asyncStart = false;
};
} // namespace
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,
} 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
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
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
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
#include "MediaSourceBinMic.h"
#include "MediaSourceBinVideoTest.h"
#include "MediaSourceBinAudioTest.h"
+#include "MediaSourceBinMediaPacket.h"
using namespace tizen_media_transporter;
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;
--- /dev/null
+/**
+ * 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});
+ }
+}
#include "MediaTransporterReceiver.h"
#include "MediaTransporterUtil.h"
#include "MediaSourceBinMic.h"
+#include "MediaSourceBinMediaPacket.h"
#include <cassert>
#include <memory>
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);
_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
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)
[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()
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));
void MediaTransporterSenderRist::startPipeline()
{
gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
- MediaTransporterIni::get().general().timeout);
+ MediaTransporterIni::get().general().timeout, _asyncStart);
}
void MediaTransporterSenderRist::stopPipeline()
void MediaTransporterSenderSrt::startPipeline()
{
gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
- MediaTransporterIni::get().general().timeout);
+ MediaTransporterIni::get().general().timeout, _asyncStart);
startStatsMonitoring();
}
void MediaTransporterSenderToServerRtsp::startPipeline()
{
gst::_setPipelineState(_gst.pipeline, GST_STATE_PLAYING,
- MediaTransporterIni::get().general().timeout);
+ MediaTransporterIni::get().general().timeout, _asyncStart);
}
void MediaTransporterSenderToServerRtsp::stopPipeline()
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
)
--- /dev/null
+/*
+ * 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__
--- /dev/null
+/*
+ * 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