Re #1182:
- Added video stream interface in vid_stream.h, the video stream will be able to handle different video formats in encoding and decoding direction.
- Renamed video device stream class identifiers from 'pjmedia_vid_stream*' to 'pjmedia_vid_dev_stream*' as 'pjmedia_vid_stream' is used by video stream interface.
- Added ffmpeg video capability to be able to parse SDP format param for H263 and also decide video format for encoding direction based on remote preference and local format-capability setting.
- Added some new APIs in jitter buffer for handling video stream: pjmedia_jbuf_put_frame3(), pjmedia_jbuf_get_frame3(), pjmedia_jbuf_peek_frame(), and pjmedia_jbuf_remove_frame().
- Moved pjmedia_stream_info_from_sdp() from session to stream
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3420 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index 2b5a89e..72b0532 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -4312,64 +4312,6 @@
</FileConfiguration>
</File>
<File
- RelativePath="..\src\pjmedia\session.c"
- >
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug-Static|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release-Dynamic|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug-Dynamic|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release-Static|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=""
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- </File>
- <File
RelativePath="..\src\pjmedia\silencedet.c"
>
<FileConfiguration
@@ -4610,6 +4552,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\stream_common.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\tonegen.c"
>
<FileConfiguration
@@ -4746,6 +4692,10 @@
>
</File>
<File
+ RelativePath="..\src\pjmedia\vid_stream.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\videoport.c"
>
</File>
@@ -5119,10 +5069,6 @@
>
</File>
<File
- RelativePath="..\include\pjmedia\session.h"
- >
- </File>
- <File
RelativePath="..\include\pjmedia\silencedet.h"
>
</File>
@@ -5147,6 +5093,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\stream_common.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\tonegen.h"
>
</File>
@@ -5183,6 +5133,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\vid_stream.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\videoport.h"
>
</File>
diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h
index f9766f6..2a17df8 100644
--- a/pjmedia/include/pjmedia-videodev/videodev.h
+++ b/pjmedia/include/pjmedia-videodev/videodev.h
@@ -78,8 +78,8 @@
* structure.
*
* Once video stream is running, application can also retrieve or set some
- * specific video capability, by using #pjmedia_vid_stream_get_cap() and
- * #pjmedia_vid_stream_set_cap() and specifying the desired capability. The
+ * specific video capability, by using #pjmedia_vid_dev_stream_get_cap() and
+ * #pjmedia_vid_dev_stream_set_cap() and specifying the desired capability. The
* value of the capability is specified as pointer, and application needs to
* supply the pointer with the correct value, according to the documentation
* of each of the capability.
@@ -154,8 +154,8 @@
} pjmedia_vid_dev_info;
-/** Forward declaration for pjmedia_vid_stream */
-typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+/** Forward declaration for pjmedia_vid_dev_stream */
+typedef struct pjmedia_vid_dev_stream pjmedia_vid_dev_stream;
typedef enum pjmedia_event_type
{
@@ -187,7 +187,7 @@
* @return Returning non-PJ_SUCCESS will cause the video
* stream to stop
*/
- pj_status_t (*capture_cb)(pjmedia_vid_stream *stream,
+ pj_status_t (*capture_cb)(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame);
@@ -210,7 +210,7 @@
* @return Returning non-PJ_SUCCESS will cause the video
* stream to stop
*/
- pj_status_t (*render_cb)(pjmedia_vid_stream *stream,
+ pj_status_t (*render_cb)(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame);
@@ -226,7 +226,7 @@
* default event-handler (if any), otherwise the
* video stream will ignore the particular event.
*/
- pj_status_t (*on_event_cb)(pjmedia_vid_stream *stream,
+ pj_status_t (*on_event_cb)(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_vid_event *event);
@@ -476,10 +476,11 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_create(const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_create(
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_strm);
/**
* Get the running parameters for the specified video stream.
@@ -491,8 +492,9 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_param(pjmedia_vid_stream *strm,
- pjmedia_vid_param *param);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_param(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_param *param);
/**
* Get the value of a specific capability of the video stream.
@@ -507,9 +509,10 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- void *value);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
/**
* Set the value of a specific capability of the video stream.
@@ -522,9 +525,10 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_set_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- const void *value);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
/**
* Start the stream.
@@ -534,14 +538,17 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start(
+ pjmedia_vid_dev_stream *strm);
/* Get/put frame API for passive stream */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_frame(pjmedia_vid_stream *strm,
- pjmedia_frame *frame);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame);
-PJ_DECL(pj_status_t) pjmedia_vid_stream_put_frame(pjmedia_vid_stream *strm,
- const pjmedia_frame *frame);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+ pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
/**
* Stop the stream.
@@ -551,7 +558,8 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_stop(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_stop(
+ pjmedia_vid_dev_stream *strm);
/**
* Destroy the stream.
@@ -561,7 +569,8 @@
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
-PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_destroy(
+ pjmedia_vid_dev_stream *strm);
/**
diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h
index e16b90a..53d6927 100644
--- a/pjmedia/include/pjmedia-videodev/videodev_imp.h
+++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h
@@ -83,13 +83,13 @@
/**
* Open the video device and create video stream. See
- * #pjmedia_vid_stream_create()
+ * #pjmedia_vid_dev_stream_create()
*/
pj_status_t (*create_stream)(pjmedia_vid_dev_factory *f,
const pjmedia_vid_param *param,
const pjmedia_vid_cb *cb,
void *user_data,
- pjmedia_vid_stream **p_vid_strm);
+ pjmedia_vid_dev_stream **p_vid_strm);
} pjmedia_vid_dev_factory_op;
@@ -113,62 +113,62 @@
/**
* Video stream operations.
*/
-typedef struct pjmedia_vid_stream_op
+typedef struct pjmedia_vid_dev_stream_op
{
/**
- * See #pjmedia_vid_stream_get_param()
+ * See #pjmedia_vid_dev_stream_get_param()
*/
- pj_status_t (*get_param)(pjmedia_vid_stream *strm,
+ pj_status_t (*get_param)(pjmedia_vid_dev_stream *strm,
pjmedia_vid_param *param);
/**
- * See #pjmedia_vid_stream_get_cap()
+ * See #pjmedia_vid_dev_stream_get_cap()
*/
- pj_status_t (*get_cap)(pjmedia_vid_stream *strm,
+ pj_status_t (*get_cap)(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
void *value);
/**
- * See #pjmedia_vid_stream_set_cap()
+ * See #pjmedia_vid_dev_stream_set_cap()
*/
- pj_status_t (*set_cap)(pjmedia_vid_stream *strm,
+ pj_status_t (*set_cap)(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
const void *value);
/**
- * See #pjmedia_vid_stream_start()
+ * See #pjmedia_vid_dev_stream_start()
*/
- pj_status_t (*start)(pjmedia_vid_stream *strm);
+ pj_status_t (*start)(pjmedia_vid_dev_stream *strm);
/**
- * See #pjmedia_vid_stream_get_frame()
+ * See #pjmedia_vid_dev_stream_get_frame()
*/
- pj_status_t (*get_frame)(pjmedia_vid_stream *strm,
+ pj_status_t (*get_frame)(pjmedia_vid_dev_stream *strm,
pjmedia_frame *frame);
/**
- * See #pjmedia_vid_stream_put_frame()
+ * See #pjmedia_vid_dev_stream_put_frame()
*/
- pj_status_t (*put_frame)(pjmedia_vid_stream *strm,
+ pj_status_t (*put_frame)(pjmedia_vid_dev_stream *strm,
const pjmedia_frame *frame);
/**
- * See #pjmedia_vid_stream_stop().
+ * See #pjmedia_vid_dev_stream_stop().
*/
- pj_status_t (*stop)(pjmedia_vid_stream *strm);
+ pj_status_t (*stop)(pjmedia_vid_dev_stream *strm);
/**
- * See #pjmedia_vid_stream_destroy().
+ * See #pjmedia_vid_dev_stream_destroy().
*/
- pj_status_t (*destroy)(pjmedia_vid_stream *strm);
+ pj_status_t (*destroy)(pjmedia_vid_dev_stream *strm);
-} pjmedia_vid_stream_op;
+} pjmedia_vid_dev_stream_op;
/**
* This structure describes the video device stream.
*/
-struct pjmedia_vid_stream
+struct pjmedia_vid_dev_stream
{
/** Internal data to be initialized by video subsystem */
struct {
@@ -177,7 +177,7 @@
} sys;
/** Operations */
- pjmedia_vid_stream_op *op;
+ pjmedia_vid_dev_stream_op *op;
};
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 8187b4c..6b9e507 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -52,13 +52,14 @@
#include <pjmedia/rtp.h>
#include <pjmedia/sdp.h>
#include <pjmedia/sdp_neg.h>
-#include <pjmedia/session.h>
+//#include <pjmedia/session.h>
#include <pjmedia/silencedet.h>
#include <pjmedia/sound.h>
#include <pjmedia/sound_port.h>
#include <pjmedia/splitcomb.h>
#include <pjmedia/stereo.h>
#include <pjmedia/stream.h>
+#include <pjmedia/stream_common.h>
#include <pjmedia/tonegen.h>
#include <pjmedia/transport.h>
#include <pjmedia/transport_adapter_sample.h>
@@ -68,6 +69,7 @@
#include <pjmedia/transport_udp.h>
#include <pjmedia/videoport.h>
#include <pjmedia/vid_codec.h>
+#include <pjmedia/vid_stream.h>
#include <pjmedia/wav_playlist.h>
#include <pjmedia/wav_port.h>
#include <pjmedia/wave.h>
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index de5c443..2c61577 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -979,6 +979,15 @@
#endif
/**
+ * Maximum video frame size.
+ * Default: 16kB
+ */
+#ifndef PJMEDIA_MAX_VIDEO_FRAME_SIZE
+# define PJMEDIA_MAX_VIDEO_FRAME_SIZE (1<<14)
+#endif
+
+
+/**
* Specify the maximum duration (in ms) for resynchronization. When a media
* is late to another media it is supposed to be synchronized to, it is
* guaranteed to be synchronized again after this duration. While if the
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 4f5a12b..04892cb 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -68,7 +68,7 @@
/**
* This structure describes jitter buffer state.
*/
-struct pjmedia_jb_state
+typedef struct pjmedia_jb_state
{
/* Setting */
unsigned frame_size; /**< Individual frame size, in bytes. */
@@ -89,13 +89,7 @@
unsigned lost; /**< Number of lost frames. */
unsigned discard; /**< Number of discarded frames. */
unsigned empty; /**< Number of empty on GET events. */
-};
-
-
-/**
- * @see pjmedia_jb_state
- */
-typedef struct pjmedia_jb_state pjmedia_jb_state;
+} pjmedia_jb_state;
/**
@@ -180,6 +174,19 @@
/**
+ * Enable/disable the jitter buffer drift detection and handling mechanism.
+ * The default behavior is enabled.
+ *
+ * @param jb The jitter buffer
+ * @param enable Set to PJ_TRUE to enable or PJ_FALSE to disable.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_enable_discard(pjmedia_jbuf *jb,
+ pj_bool_t enable);
+
+
+/**
* Destroy jitter buffer instance.
*
* @param jb The jitter buffer.
@@ -244,6 +251,32 @@
pj_bool_t *discarded);
/**
+ * Put a frame to the jitter buffer. If the frame can be accepted (based
+ * on the sequence number), the jitter buffer will copy the frame and put
+ * it in the appropriate position in the buffer.
+ *
+ * Application MUST manage it's own synchronization when multiple threads
+ * are accessing the jitter buffer at the same time.
+ *
+ * @param jb The jitter buffer.
+ * @param frame Pointer to frame buffer to be stored in the jitter
+ * buffer.
+ * @param size The frame size.
+ * @param bit_info Bit precise info of the frame, e.g: a frame may not
+ * exactly start and end at the octet boundary, so this
+ * field may be used for specifying start & end bit offset.
+ * @param frame_seq The frame sequence number.
+ * @param frame_ts The frame timestamp.
+ * @param discarded Flag whether the frame is discarded by jitter buffer.
+ */
+PJ_DECL(void) pjmedia_jbuf_put_frame3( pjmedia_jbuf *jb,
+ const void *frame,
+ pj_size_t size,
+ pj_uint32_t bit_info,
+ int frame_seq,
+ pj_uint32_t frame_ts,
+ pj_bool_t *discarded);
+/**
* Get a frame from the jitter buffer. The jitter buffer will return the
* oldest frame from it's buffer, when it is available.
*
@@ -293,6 +326,25 @@
pj_uint32_t *bit_info);
+PJ_DECL(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
+ void *frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts);
+
+PJ_DECL(void) pjmedia_jbuf_peek_frame(pjmedia_jbuf *jb,
+ unsigned idx,
+ const void **frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts);
+
+PJ_DECL(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb,
+ unsigned frame_cnt);
+
+
/**
* Get jitter buffer current state/settings.
*
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 8bf4433..8094c30 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -143,6 +143,30 @@
typedef struct pjmedia_stream_info pjmedia_stream_info;
+/**
+ * This function will initialize the stream info based on information
+ * in both SDP session descriptors for the specified stream index.
+ * The remaining information will be taken from default codec parameters.
+ * If socket info array is specified, the socket will be copied to the
+ * session info as well.
+ *
+ * @param si Stream info structure to be initialized.
+ * @param pool Pool to allocate memory.
+ * @param endpt PJMEDIA endpoint instance.
+ * @param local Local SDP session descriptor.
+ * @param remote Remote SDP session descriptor.
+ * @param stream_idx Media stream index in the session descriptor.
+ *
+ * @return PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_stream_info_from_sdp( pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx);
+
/**
* Create a media stream based on the specified parameter. After the stream
diff --git a/pjmedia/include/pjmedia/vid_codec.h b/pjmedia/include/pjmedia/vid_codec.h
index d5321b5..24eaa88 100644
--- a/pjmedia/include/pjmedia/vid_codec.h
+++ b/pjmedia/include/pjmedia/vid_codec.h
@@ -22,8 +22,8 @@
/**
- * @file codec.h
- * @brief Codec framework.
+ * @file vid_codec.h
+ * @brief Video codec framework.
*/
#include <pjmedia/codec.h>
@@ -45,20 +45,22 @@
{
pjmedia_format_id fmt_id; /**< Encoded format ID */
pj_str_t encoding_name; /**< Encoding name */
- pjmedia_dir dir; /**< Direction */
- unsigned pt; /**< Payload type (may be 0 for
+ unsigned pt; /**< Payload type (may be 255 for
dynamic payload type) */
- unsigned clock_rate; /**< (?) Clock rate */
- unsigned dec_fmt_id_cnt;
+ unsigned clock_rate; /**< Clock rate */
+ pjmedia_dir dir; /**< Direction */
+ unsigned dec_fmt_id_cnt; /**< # of supported encoding source
+ format IDs */
pjmedia_format_id dec_fmt_id[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
/**< Supported encoding source
format IDs */
- unsigned fps_cnt; /**< zero if support any fps */
- pjmedia_ratio fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT];
- /**< (?) supported FPSes,
- ffmpeg has this */
+ unsigned fps_cnt; /**< # of supported frame-rates, can be
+ zero (support any frame-rate) */
+ pjmedia_ratio fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT];
+ /**< Supported frame-rates */
} pjmedia_vid_codec_info;
+
/**
* Detailed codec attributes used in configuring a codec and in querying
* the capability of codec factories. Default attributes of any codecs could
@@ -79,13 +81,28 @@
pjmedia_codec_fmtp dec_fmtp; /**< Decoder fmtp params */
unsigned enc_mtu; /**< MTU or max payload size setting*/
- unsigned pt; /**< Payload type. */
} pjmedia_vid_codec_param;
+/**
+ * Enumeration of video codec events.
+ */
+typedef enum pjmedia_vid_codec_event
+{
+ /**
+ * Format changed event. The decoder output format is not really
+ * configurable, so that the output format setting configured in the
+ * initialization may be changed. Application can catch this event
+ * by checking the bit_info field of the pjmedia_frame of the decoder
+ * output frame.
+ */
+ PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED = 1,
-/*
- * Forward declaration for pjmedia_vid_codec.
+} pjmedia_vid_codec_event;
+
+
+/**
+ * Forward declaration for video codec.
*/
typedef struct pjmedia_vid_codec pjmedia_vid_codec;
@@ -111,8 +128,9 @@
/**
* Open the codec and initialize with the specified parameter.
* Upon successful initialization, the codec may modify the parameter
- * and fills in the unspecified values (such as enc_ptime, when
- * encoder ptime is different than decoder ptime).
+ * and fills in the unspecified values (such as size or frame rate of
+ * the encoder format, as it may need to be negotiated with remote
+ * preferences via SDP fmtp).
*
* @param codec The codec instance.
* @param param Codec initialization parameter.
@@ -138,47 +156,61 @@
* When the parameter cannot be changed, this function will return
* non-PJ_SUCCESS, and the original parameters will not be changed.
*
- * Application can expect changing trivial codec settings such as
- * changing VAD setting to succeed.
- *
* @param codec The codec instance.
* @param param The new codec parameter.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*modify)(pjmedia_vid_codec *codec,
- const pjmedia_vid_codec_param *param );
+ const pjmedia_vid_codec_param *param);
+
+ /**
+ * Get the codec parameter after the codec is opened.
+ *
+ * @param codec The codec instance.
+ * @param param The codec parameter.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+ pj_status_t (*get_param)(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param);
/**
- * Instruct the codec to inspect the specified payload/packet and
- * split the packet into individual base frames. Each output frames will
- * have ptime that is equal to basic frame ptime (i.e. the value of
- * info.frm_ptime in #pjmedia_vid_codec_param).
+ * Instruct the codec to generate a payload/packet from a picture
+ * bitstream to be sent (via network). The maximum payload size or
+ * MTU is configurable via enc_mtu field of #pjmedia_vid_codec_param.
+ * For a long bitstream, application usually need to call this function
+ * multiple times until the whole bitstream is sent. Note that, for
+ * performance reason, the packetization will be done in-place, so the
+ * original bitstream may be modified by this function.
*
* @param codec The codec instance
- * @param pkt The input packet.
- * @param pkt_size Size of the packet.
- * @param timestamp The timestamp of the first sample in the packet.
- * @param frame_cnt On input, specifies the maximum number of frames
- * in the array. On output, the codec must fill
- * with number of frames detected in the packet.
- * @param frames On output, specifies the frames that have been
- * detected in the packet.
+ * @param bits The picture bitstream.
+ * @param bits_len The length of the bitstream.
+ * @param bits_pos On input, the start position of the bitstream
+ * to be packetized. On output, the next position for
+ * next packet.
+ * @param pkt The pointer of the generated payload.
+ * @param pkt_len The payload length.
*
* @return PJ_SUCCESS on success.
*/
pj_status_t (*packetize) (pjmedia_vid_codec *codec,
- pj_uint8_t *buf,
- pj_size_t buf_len,
- unsigned *pos,
- const pj_uint8_t **payload,
- pj_size_t *payload_len);
+ pj_uint8_t *bits,
+ pj_size_t bits_len,
+ unsigned *bits_pos,
+ const pj_uint8_t **pkt,
+ pj_size_t *pkt_len);
/**
- * Instruct the codec to inspect the specified payload/packet and
- * split the packet into individual base frames. Each output frames will
- * have ptime that is equal to basic frame ptime (i.e. the value of
- * info.frm_ptime in #pjmedia_vid_codec_param).
+ * Instruct the codec to parse a payload and append it into a picture
+ * bitstream. A picture bitstreams may need to be reconstructed from
+ * one or more payloads. Note that this function will not provide the
+ * detection of picture boundary, so application should manage the
+ * picture boundary detection by itself, e.g: for RTP delivery, payloads
+ * belong to the same picture will share the same RTP timestamp and also
+ * there is marker bit in the RTP header that is usually reserved for
+ * end-of-picture flag.
*
* @param codec The codec instance
* @param pkt The input packet.
@@ -195,13 +227,13 @@
pj_status_t (*unpacketize)(pjmedia_vid_codec *codec,
const pj_uint8_t *payload,
pj_size_t payload_len,
- pj_uint8_t *buf,
- pj_size_t *buf_len);
+ pj_uint8_t *bits,
+ pj_size_t *bits_len);
/**
* Instruct the codec to encode the specified input frame. The input
- * PCM samples MUST have ptime that is multiplication of base frame
- * ptime (i.e. the value of info.frm_ptime in #pjmedia_vid_codec_param).
+ * MUST contain only one picture with appropriate format as specified
+ * in opening the codec.
*
* @param codec The codec instance.
* @param input The input frame.
@@ -217,10 +249,12 @@
/**
* Instruct the codec to decode the specified input frame. The input
- * frame MUST have ptime that is exactly equal to base frame
- * ptime (i.e. the value of info.frm_ptime in #pjmedia_vid_codec_param).
- * Application can achieve this by parsing the packet into base
- * frames before decoding each frame.
+ * frame MUST contain exactly one picture. Note that the decoded picture
+ * format may different to the current setting, e.g: the format specified
+ * in the #pjmedia_vid_codec_param when opening the codec, in this case the
+ * PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED flag will be set in the bit_info
+ * field of the output frame and application can query the new format
+ * using #get_param().
*
* @param codec The codec instance.
* @param input The input frame.
@@ -247,6 +281,7 @@
pj_status_t (*recover)(pjmedia_vid_codec *codec,
unsigned out_size,
pjmedia_frame *output);
+
} pjmedia_vid_codec_op;
@@ -258,7 +293,7 @@
/**
- * This structure describes a codec instance.
+ * This structure describes a video codec instance.
*/
struct pjmedia_vid_codec
{
@@ -266,13 +301,13 @@
PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec);
/** Codec's private data. */
- void *codec_data;
+ void *codec_data;
/** Codec factory where this codec was allocated. */
pjmedia_vid_codec_factory *factory;
/** Operations to codec. */
- pjmedia_vid_codec_op *op;
+ pjmedia_vid_codec_op *op;
};
@@ -376,55 +411,25 @@
/**
+ * Opaque declaration for codec manager.
+ */
+typedef struct pjmedia_vid_codec_mgr pjmedia_vid_codec_mgr;
+
+/**
* Declare maximum codecs
*/
#define PJMEDIA_VID_CODEC_MGR_MAX_CODECS 32
-/**
- * Codec manager maintains array of these structs for each supported
- * codec.
- */
-typedef struct pjmedia_vid_codec_desc
-{
- pjmedia_vid_codec_info info; /**< Codec info. */
- pjmedia_codec_id id; /**< Fully qualified name */
- pjmedia_codec_priority prio; /**< Priority. */
- pjmedia_vid_codec_factory *factory; /**< The factory. */
- pjmedia_vid_codec_param *def_param; /**< Default codecs
- parameters. */
-} pjmedia_vid_codec_desc;
-
/**
- * The declaration for codec manager. Application doesn't normally need
- * to see this declaration, but nevertheless this declaration is needed
- * by media endpoint to instantiate the codec manager.
- */
-typedef struct pjmedia_vid_codec_mgr
-{
- /** Codec manager mutex. */
- pj_mutex_t *mutex;
-
- /** List of codec factories registered to codec manager. */
- pjmedia_vid_codec_factory factory_list;
-
- /** Number of supported codecs. */
- unsigned codec_cnt;
-
- /** Array of codec descriptor. */
- pjmedia_vid_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
-
-} pjmedia_vid_codec_mgr;
-
-
-
-/**
- * Initialize codec manager. Normally this function is called by pjmedia
+ * Initialize codec manager. If there is no the default video codec manager,
+ * this function will automatically set the default video codec manager to
+ * the new codec manager instance. Normally this function is called by pjmedia
* endpoint's initialization code.
*
- * @param mgr Codec manager instance.
- * @param pf Pool factory instance.
+ * @param pool The pool instance.
+ * @param mgr The pointer to the new codec manager instance.
*
* @return PJ_SUCCESS on success.
*/
@@ -436,13 +441,27 @@
* Destroy codec manager. Normally this function is called by pjmedia
* endpoint's deinitialization code.
*
- * @param mgr Codec manager instance.
+ * @param mgr Codec manager instance. If NULL, it is the default codec
+ * manager instance will be destroyed.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr);
+
+/**
+ * Get the default codec manager instance.
+ *
+ * @return The default codec manager instance or NULL if none.
+ */
PJ_DECL(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void);
+
+
+/**
+ * Set the default codec manager instance.
+ *
+ * @param mgr The codec manager instance.
+ */
PJ_DECL(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr);
@@ -450,8 +469,8 @@
* Register codec factory to codec manager. This will also register
* all supported codecs in the factory to the codec manager.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param factory The codec factory to be registered.
*
* @return PJ_SUCCESS on success.
@@ -465,8 +484,8 @@
* remove all the codecs registered by the codec factory from the
* codec manager's list of supported codecs.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param factory The codec factory to be unregistered.
*
* @return PJ_SUCCESS on success.
@@ -479,8 +498,8 @@
* Enumerate all supported codecs that have been registered to the
* codec manager by codec factories.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param count On input, specifies the number of elements in
* the array. On output, the value will be set to
* the number of elements that have been initialized
@@ -500,21 +519,21 @@
/**
* Get codec info for the specified static payload type.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param pt Static payload type/number.
- * @param inf Pointer to receive codec info.
+ * @param info Pointer to receive codec info.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr,
unsigned pt,
- const pjmedia_vid_codec_info **inf);
+ const pjmedia_vid_codec_info **info);
/**
* Convert codec info struct into a unique codec identifier.
- * A codec identifier looks something like "L16/44100/2".
+ * A codec identifier looks something like "H263/90000".
*
* @param info The codec info
* @param id Buffer to put the codec info string.
@@ -531,11 +550,11 @@
/**
* Find codecs by the unique codec identifier. This function will find
* all codecs that match the codec identifier prefix. For example, if
- * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * "H26" is specified, then it will find "H263/90000", "H264/90000",
* and so on, up to the maximum count specified in the argument.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param codec_id The full codec ID or codec ID prefix. If an empty
* string is given, it will match all codecs.
* @param count Maximum number of codecs to find. On return, it
@@ -548,11 +567,11 @@
* @return PJ_SUCCESS if at least one codec info is found.
*/
PJ_DECL(pj_status_t)
-pjmedia_vid_codec_mgr_find_codecs_by_id( pjmedia_vid_codec_mgr *mgr,
- const pj_str_t *codec_id,
- unsigned *count,
- const pjmedia_vid_codec_info *p_info[],
- unsigned prio[]);
+pjmedia_vid_codec_mgr_find_codecs_by_id(pjmedia_vid_codec_mgr *mgr,
+ const pj_str_t *codec_id,
+ unsigned *count,
+ const pjmedia_vid_codec_info *p_info[],
+ unsigned prio[]);
/**
@@ -561,8 +580,8 @@
* are found with the same codec_id prefix, then the function sets the
* priorities of all those codecs.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param codec_id The full codec ID or codec ID prefix. If an empty
* string is given, it will match all codecs.
* @param prio Priority to be set. The priority can have any value
@@ -573,15 +592,15 @@
*/
PJ_DECL(pj_status_t)
pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr,
- const pj_str_t *codec_id,
- pj_uint8_t prio);
+ const pj_str_t *codec_id,
+ pj_uint8_t prio);
/**
* Get default codec param for the specified codec info.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param info The codec info, which default parameter's is being
* queried.
* @param param On return, will be filled with the default codec
@@ -590,16 +609,17 @@
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t)
-pjmedia_vid_codec_mgr_get_default_param( pjmedia_vid_codec_mgr *mgr,
- const pjmedia_vid_codec_info *info,
- pjmedia_vid_codec_param *param );
+pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_vid_codec_info *info,
+ pjmedia_vid_codec_param *param);
/**
* Set default codec param for the specified codec info.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
+ * @param pool The pool instance.
* @param info The codec info, which default parameter's is being
* updated.
* @param param The new default codec parameter. Set to NULL to reset
@@ -619,8 +639,8 @@
* specified codec info. The codec will enumerate all codec factories
* until it finds factory that is able to create the specified codec.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param info The information about the codec to be created.
* @param p_codec Pointer to receive the codec instance.
*
@@ -635,8 +655,8 @@
* Deallocate the specified codec instance. The codec manager will return
* the instance of the codec back to its factory.
*
- * @param mgr The codec manager instance. Application can get the
- * instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr The codec manager instance. If NULL, the default codec
+ * manager instance will be used.
* @param codec The codec instance.
*
* @return PJ_SUCESS on success.
@@ -654,9 +674,9 @@
*/
/**
- * @defgroup PJMEDIA_CODEC_CODECS Supported codecs
+ * @defgroup PJMEDIA_CODEC_VID_CODECS Supported video codecs
* @ingroup PJMEDIA_CODEC
- * @brief Documentation about individual codec supported by PJMEDIA
+ * @brief Documentation about individual video codec supported by PJMEDIA
* @{
* Please see the APIs provided by the individual codecs below.
*/
diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h
new file mode 100644
index 0000000..fa20aa1
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_stream.h
@@ -0,0 +1,303 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_VID_STREAM_H__
+#define __PJMEDIA_VID_STREAM_H__
+
+
+/**
+ * @file vid_stream.h
+ * @brief Video Stream.
+ */
+
+#include <pjmedia/endpoint.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/port.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/transport.h>
+#include <pjmedia/vid_codec.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_VID_STRM Video streams
+ * @ingroup PJMEDIA_PORT
+ * @brief Video communication via the network
+ * @{
+ *
+ * A video stream is a bidirectional video communication between two
+ * endpoints. It corresponds to a video media description ("m=video" line)
+ * in SDP session descriptor.
+ *
+ * A video stream consists of two unidirectional channels:
+ * - encoding channel, which transmits unidirectional video to remote, and
+ * - decoding channel, which receives unidirectional media from remote.
+ *
+ * A video stream exports two media port interface (see @ref PJMEDIA_PORT),
+ * one for each direction, and application normally uses this interface to
+ * interconnect the stream to other PJMEDIA components, e.g: the video
+ * capture port supplies frames to the encoding port and video renderer
+ * consumes frames from the decoding port.
+ *
+ * A video stream internally manages the following objects:
+ * - an instance of video codec (see @ref PJMEDIA_VID_CODEC),
+ * - an @ref PJMED_JBUF,
+ * - two instances of RTP sessions (#pjmedia_rtp_session, one for each
+ * direction),
+ * - one instance of RTCP session (#pjmedia_rtcp_session),
+ * - and a reference to video transport to send and receive packets
+ * to/from the network (see @ref PJMEDIA_TRANSPORT).
+ *
+ * Video streams are created by calling #pjmedia_vid_stream_create(),
+ * specifying #pjmedia_stream_info structure in the parameter. Application
+ * can construct the #pjmedia_vid_stream_info structure manually, or use
+ * #pjmedia_vid_stream_info_from_sdp() function to construct the
+ * #pjmedia_vid stream_info from local and remote SDP session descriptors.
+ */
+
+
+/**
+ * This structure describes video stream information. Each video stream
+ * corresponds to one "m=" line in SDP session descriptor, and it has
+ * its own RTP/RTCP socket pair.
+ */
+typedef struct pjmedia_vid_stream_info
+{
+ pjmedia_type type; /**< Media type (audio, video) */
+ pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */
+ pjmedia_dir dir; /**< Media direction. */
+ pj_sockaddr rem_addr; /**< Remote RTP address */
+ pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If
+ sin_family is zero, the RTP address
+ will be calculated from RTP. */
+ unsigned tx_pt; /**< Outgoing codec paylaod type. */
+ pj_uint32_t ssrc; /**< RTP SSRC. */
+ pj_uint32_t rtp_ts; /**< Initial RTP timestamp. */
+ pj_uint16_t rtp_seq; /**< Initial RTP sequence number. */
+ pj_uint8_t rtp_seq_ts_set;
+ /**< Bitmask flags if initial RTP sequence
+ and/or timestamp for sender are set.
+ bit 0/LSB : sequence flag
+ bit 1 : timestamp flag */
+ int jb_init; /**< Jitter buffer init delay in msec.
+ (-1 for default). */
+ int jb_min_pre; /**< Jitter buffer minimum prefetch
+ delay in msec (-1 for default). */
+ int jb_max_pre; /**< Jitter buffer maximum prefetch
+ delay in msec (-1 for default). */
+ int jb_max; /**< Jitter buffer max delay in msec. */
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ pj_bool_t use_ka; /**< Stream keep-alive and NAT hole punch
+ (see #PJMEDIA_STREAM_ENABLE_KA)
+ is enabled? */
+#endif
+
+ pjmedia_vid_codec_info codec_info; /**< Incoming codec format info. */
+ pjmedia_vid_codec_param *codec_param; /**< Optional codec param. */
+
+} pjmedia_vid_stream_info;
+
+
+/**
+ * This function will initialize the video stream info based on information
+ * in both SDP session descriptors for the specified stream index.
+ * The remaining information will be taken from default codec parameters.
+ * If socket info array is specified, the socket will be copied to the
+ * session info as well.
+ *
+ * @param si Stream info structure to be initialized.
+ * @param pool Pool to allocate memory.
+ * @param endpt PJMEDIA endpoint instance.
+ * @param local Local SDP session descriptor.
+ * @param remote Remote SDP session descriptor.
+ * @param stream_idx Media stream index in the session descriptor.
+ *
+ * @return PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx);
+
+
+/*
+ * Opaque declaration for video stream.
+ */
+typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+
+
+/**
+ * Create a video stream based on the specified parameter. After the video
+ * stream has been created, application normally would want to get the media
+ * port interface of the stream, by calling pjmedia_vid_stream_get_port().
+ * The media port interface exports put_frame() and get_frame() function,
+ * used to transmit and receive media frames from the stream.
+ *
+ * Without application calling put_frame() and get_frame(), there will be
+ * no media frames transmitted or received by the stream.
+ *
+ * @param endpt Media endpoint.
+ * @param pool Pool to allocate memory for the stream. A large
+ * number of memory may be needed because jitter
+ * buffer needs to preallocate some storage.
+ * @param info Stream information.
+ * @param tp Stream transport instance used to transmit
+ * and receive RTP/RTCP packets to/from the underlying
+ * transport.
+ * @param user_data Arbitrary user data (for future callback feature).
+ * @param p_stream Pointer to receive the video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_create(
+ pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_vid_stream_info *info,
+ pjmedia_transport *tp,
+ void *user_data,
+ pjmedia_vid_stream **p_stream);
+
+/**
+ * Destroy the video stream.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get the media port interface of the stream. The media port interface
+ * declares put_frame() and get_frame() function, which is the only
+ * way for application to transmit and receive media frames from the
+ * stream. As bidirectional video streaming may have different video
+ * formats in the encoding and decoding direction, there are two media
+ * ports exported by the video stream, one for each direction.
+ *
+ * @param stream The video stream.
+ * @param dir The video direction.
+ * @param p_port Pointer to receive the port interface.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_port(
+ pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ pjmedia_port **p_port);
+
+
+/**
+ * Get the media transport object associated with this stream.
+ *
+ * @param st The video stream.
+ *
+ * @return The transport object being used by the stream.
+ */
+PJ_DECL(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+ pjmedia_vid_stream *st);
+
+
+/**
+ * Start the video stream. This will start the appropriate channels
+ * in the video stream, depending on the video direction that was set
+ * when the stream was created.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get the stream statistics. See also #pjmedia_stream_get_stat_jbuf()
+ *
+ * @param stream The video stream.
+ * @param stat Media stream statistics.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_stat *stat);
+
+/**
+ * Reset the video stream statistics.
+ *
+ * @param stream The video stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get current jitter buffer state. See also #pjmedia_stream_get_stat()
+ *
+ * @param stream The video stream.
+ * @param state Jitter buffer state.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+ const pjmedia_vid_stream *stream,
+ pjmedia_jb_state *state);
+
+
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_param(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_stat *stat);
+
+/**
+ * Pause the individual channel in the stream.
+ *
+ * @param stream The video channel.
+ * @param dir Which direction to pause.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+ pjmedia_dir dir);
+
+/**
+ * Resume the individual channel in the stream.
+ *
+ * @param stream The video channel.
+ * @param dir Which direction to resume.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+ pjmedia_dir dir);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_VID_STREAM_H__ */
diff --git a/pjmedia/include/pjmedia/videoport.h b/pjmedia/include/pjmedia/videoport.h
index da11db2..3b46632 100644
--- a/pjmedia/include/pjmedia/videoport.h
+++ b/pjmedia/include/pjmedia/videoport.h
@@ -108,7 +108,7 @@
*
* @return The video stream.
*/
-PJ_DECL(pjmedia_vid_stream*)
+PJ_DECL(pjmedia_vid_dev_stream*)
pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port);
/**
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
index 35128e5..b1d15c2 100644
--- a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
@@ -27,6 +27,7 @@
#include <pj/string.h>
#include <pj/os.h>
+
/*
* Only build this file if PJMEDIA_HAS_FFMPEG_CODEC != 0
*/
@@ -37,6 +38,7 @@
#include "../pjmedia/ffmpeg_util.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
#define PJMEDIA_FORMAT_FFMPEG_UNKNOWN PJMEDIA_FORMAT_PACK('f','f','0','0');
@@ -65,6 +67,8 @@
static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec );
static pj_status_t ffmpeg_codec_modify(pjmedia_vid_codec *codec,
const pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param);
static pj_status_t ffmpeg_packetize ( pjmedia_vid_codec *codec,
pj_uint8_t *buf,
pj_size_t buf_len,
@@ -95,6 +99,7 @@
&ffmpeg_codec_open,
&ffmpeg_codec_close,
&ffmpeg_codec_modify,
+ &ffmpeg_codec_get_param,
&ffmpeg_packetize,
&ffmpeg_unpacketize,
&ffmpeg_codec_encode,
@@ -113,14 +118,6 @@
};
-typedef struct ffmpeg_codec_info {
- PJ_DECL_LIST_MEMBER(struct ffmpeg_codec_info);
- pjmedia_vid_codec_info info;
- AVCodec *enc;
- AVCodec *dec;
-} ffmpeg_codec_info;
-
-
/* FFMPEG codecs factory */
static struct ffmpeg_factory {
pjmedia_vid_codec_factory base;
@@ -128,30 +125,359 @@
pj_pool_factory *pf;
pj_pool_t *pool;
pj_mutex_t *mutex;
- ffmpeg_codec_info codecs;
} ffmpeg_factory;
+typedef struct ffmpeg_codec_desc ffmpeg_codec_desc;
+
+/* ITU resolution ID */
+typedef enum itu_res_id {
+ ITU_RES_SQCIF,
+ ITU_RES_QCIF,
+ ITU_RES_CIF,
+ ITU_RES_4CIF,
+ ITU_RES_16CIF,
+ ITU_RES_CUSTOM,
+} itu_res_id;
+
+/* ITU resolution definition */
+struct itu_res {
+ itu_res_id id;
+ pj_str_t name;
+ pjmedia_rect_size size;
+} itu_res_def [] =
+{
+ {ITU_RES_16CIF, {"16CIF",5}, {1408,1142}},
+ {ITU_RES_4CIF, {"4CIF",4}, {704,576}},
+ {ITU_RES_CIF, {"CIF",3}, {352,288}},
+ {ITU_RES_QCIF, {"QCIF",4}, {176,144}},
+ {ITU_RES_SQCIF, {"SQCIF",5}, {88,72}},
+ {ITU_RES_CUSTOM, {"CUSTOM",6}, {0,0}},
+};
+
/* FFMPEG codecs private data. */
typedef struct ffmpeg_private {
+ const ffmpeg_codec_desc *desc;
+ pjmedia_vid_codec_param param; /**< Codec param */
+ pj_pool_t *pool; /**< Pool for each instance */
+ pj_timestamp last_tx; /**< Timestamp of last
+ transmit */
+
+ /* Format info and apply format param */
+ const pjmedia_video_format_info *enc_vfi;
+ pjmedia_video_apply_fmt_param enc_vafp;
+ const pjmedia_video_format_info *dec_vfi;
+ pjmedia_video_apply_fmt_param dec_vafp;
+
+ /* The ffmpeg codec states. */
+ AVCodec *enc;
+ AVCodec *dec;
+ AVCodecContext *enc_ctx;
+ AVCodecContext *dec_ctx;
+
+ /* The ffmpeg decoder cannot set the output format, so format conversion
+ * may be needed for post-decoding.
+ */
+ enum PixelFormat expected_dec_fmt;
+ /**< expected output format of
+ ffmpeg decoder */
+ struct SwsContext *sws_ctx; /**< the format converter for
+ post decoding */
+
+} ffmpeg_private;
+
+
+typedef pj_status_t (*func_packetize) (pj_uint8_t *buf,
+ pj_size_t buf_len,
+ unsigned *pos,
+ int max_payload_len,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len);
+
+typedef pj_status_t (*func_unpacketize) (const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t *bits_len);
+
+typedef pj_status_t (*func_parse_fmtp) (ffmpeg_private *ff);
+
+/* FFMPEG codec info */
+struct ffmpeg_codec_desc {
+ /* Predefined info */
+ pjmedia_vid_codec_info info;
+ func_packetize packetize;
+ func_unpacketize unpacketize;
+ func_parse_fmtp parse_fmtp;
+ pjmedia_codec_fmtp dec_fmtp;
+
+ /* Init time defined info */
+ pj_bool_t enabled;
AVCodec *enc;
AVCodec *dec;
- AVCodecContext *enc_ctx;
- AVCodecContext *dec_ctx;
- AVCodecParserContext *dec_parser_ctx;
+};
- /*
- pjmedia_frame *pack_frms;
- unsigned pack_frm_cnt;
- unsigned pack_frm_max_cnt;
- */
+/* H263 packetizer */
+static pj_status_t h263_packetize(pj_uint8_t *buf,
+ pj_size_t buf_len,
+ unsigned *pos,
+ int max_payload_len,
+ const pj_uint8_t **payload,
+ pj_size_t *payload_len)
+{
+ return pjmedia_h263_packetize(buf, buf_len, pos, max_payload_len,
+ payload, payload_len);
+}
- pjmedia_vid_codec_param param; /**< Codec param. */
- pj_pool_t *pool; /**< Pool for each instance. */
- pj_timestamp last_tx; /**< Timestamp of last transmit.*/
- const pjmedia_video_format_info *vfi;
- pjmedia_video_apply_fmt_param vafp;
-} ffmpeg_private;
+/* H263 unpacketizer */
+static pj_status_t h263_unpacketize(const pj_uint8_t *payload,
+ pj_size_t payload_len,
+ pj_uint8_t *bits,
+ pj_size_t *bits_len)
+{
+ return pjmedia_h263_unpacketize(payload, payload_len, bits, bits_len);
+}
+
+/* H263 fmtp parser */
+static pj_status_t h263_parse_fmtp(ffmpeg_private *ff);
+
+
+/* Internal codec info */
+ffmpeg_codec_desc codec_desc[] =
+{
+ {
+ {PJMEDIA_FORMAT_H263, {"H263",4}, PJMEDIA_RTP_PT_H263},
+ &h263_packetize, &h263_unpacketize, &h263_parse_fmtp,
+ {2, { {{"CIF",3}, {"2",1}}, {{"QCIF",4}, {"1",1}}, } },
+ },
+ {
+ {PJMEDIA_FORMAT_H261, {"H261",4}, PJMEDIA_RTP_PT_H261},
+ },
+ {
+ {PJMEDIA_FORMAT_MJPEG, {"JPEG",4}, PJMEDIA_RTP_PT_JPEG},
+ },
+};
+
+/* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */
+static pj_status_t parse_fmtp_itu_custom_res(const pj_str_t *fmtp_val,
+ pjmedia_rect_size *size,
+ unsigned *mpi)
+{
+ const char *p, *p_end;
+ pj_str_t token;
+ unsigned long val[3] = {0};
+ unsigned i = 0;
+
+ p = token.ptr = fmtp_val->ptr;
+ p_end = p + fmtp_val->slen;
+
+ while (p<=p_end && i<PJ_ARRAY_SIZE(val)) {
+ if (*p==',' || p==p_end) {
+ token.slen = (char*)p - token.ptr;
+ val[i++] = pj_strtoul(&token);
+ token.ptr = (char*)p+1;
+ }
+ ++p;
+ }
+
+ if (!val[0] || !val[1])
+ return PJ_ETOOSMALL;
+
+ if (val[2]<1 || val[2]>32)
+ return PJ_EINVAL;
+
+ size->w = val[0];
+ size->h = val[1];
+ *mpi = val[2];
+ return PJ_SUCCESS;
+}
+
+#define CALC_ITU_CUSTOM_RES_SCORE(size, mpi) ((size)->w * (size)->h / mpi)
+
+/* ITU codec capabilities */
+typedef struct itu_cap
+{
+ /* Lowest MPI for each non-custom resolution */
+ unsigned lowest_mpi[PJ_ARRAY_SIZE(itu_res_def)];
+ /* For custom resolution, we use maximum processing score */
+ unsigned custom_res_max_score;
+} itu_cap;
+
+
+static pj_status_t load_itu_cap(const pjmedia_codec_fmtp *fmtp,
+ itu_cap *cap)
+{
+ unsigned i, j;
+ unsigned min_mpi = 0;
+
+ /* Get Minimum Picture Interval (MPI) for each resolution. If a resolution
+ * has no MPI setting in fmtp, the MPI setting is derived from the higher
+ * resolution.
+ */
+ for (i=0; i<PJ_ARRAY_SIZE(itu_res_def); ++i) {
+
+ /* Init lowest MPI */
+ cap->lowest_mpi[i] = min_mpi? min_mpi:1;
+
+ for (j=0; j<fmtp->cnt; ++j) {
+ if (pj_stricmp(&fmtp->param[j].name, &itu_res_def[i].name)==0) {
+ pjmedia_rect_size size;
+ unsigned mpi;
+ unsigned score;
+
+ if (i != ITU_RES_CUSTOM) {
+ size = itu_res_def[i].size;
+ mpi = pj_strtoul(&fmtp->param[j].val);
+ if (min_mpi)
+ min_mpi = PJ_MIN(mpi, min_mpi);
+ else
+ min_mpi = mpi;
+
+ /* Update the lowest MPI for this resolution */
+ cap->lowest_mpi[i] = min_mpi;
+
+ /* Also update the processing score for the custom
+ * resolution.
+ */
+ score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+ cap->custom_res_max_score =
+ PJ_MAX(score, cap->custom_res_max_score);
+ } else {
+
+
+ if (parse_fmtp_itu_custom_res(&fmtp->param[j].val,
+ &size, &mpi) == PJ_SUCCESS)
+ {
+ score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+ cap->custom_res_max_score =
+ PJ_MAX(score, cap->custom_res_max_score);
+ }
+ }
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* H263 fmtp parser */
+static pj_status_t h263_parse_fmtp(ffmpeg_private *ff)
+{
+ pjmedia_dir dir;
+ pj_status_t status;
+
+ dir = ff->param.dir;
+
+ if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+ pjmedia_vid_codec_param param_ref;
+ pjmedia_codec_fmtp *fmtp_rem, *fmtp_ref;
+ itu_cap local_cap;
+ pjmedia_rect_size size = {0};
+ unsigned mpi = 0;
+ pj_bool_t got_good_res = PJ_FALSE;
+ pj_bool_t has_prefered_res = PJ_FALSE;
+ unsigned i, j;
+
+ fmtp_rem = &ff->param.enc_fmtp;
+ dir &= ~PJMEDIA_DIR_ENCODING;
+
+ /* Get default fmtp setting as the reference for local capabilities */
+ status = pjmedia_vid_codec_mgr_get_default_param(
+ ffmpeg_factory.mgr, &ff->desc->info, ¶m_ref);
+ fmtp_ref = (status==PJ_SUCCESS)? ¶m_ref.enc_fmtp : fmtp_rem;
+
+ /* Load default local capabilities */
+ status = load_itu_cap(fmtp_ref, &local_cap);
+ pj_assert(status == PJ_SUCCESS);
+
+ /* Negotiate resolution and MPI */
+ for (i=0; i<fmtp_rem->cnt && !got_good_res; ++i)
+ {
+ for (j=0; j<PJ_ARRAY_SIZE(itu_res_def) && !got_good_res; ++j)
+ {
+ if (pj_stricmp(&fmtp_rem->param[i].name, &itu_res_def[j].name))
+ continue;
+
+ has_prefered_res = PJ_TRUE;
+ if (j == ITU_RES_CUSTOM) {
+ unsigned score;
+
+ if (parse_fmtp_itu_custom_res(&fmtp_rem->param[i].val,
+ &size, &mpi) != PJ_SUCCESS)
+ {
+ /* Invalid custom resolution format, skip this
+ * custom resolution
+ */
+ break;
+ }
+
+ score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+ if (score <= local_cap.custom_res_max_score)
+ got_good_res = PJ_TRUE;
+ } else {
+ mpi = pj_strtoul(&fmtp_rem->param[i].val);
+ if (mpi>=1 && mpi<=32 && mpi>=local_cap.lowest_mpi[j]) {
+ got_good_res = PJ_TRUE;
+ size = itu_res_def[j].size;
+ }
+ }
+ }
+ }
+
+ if (has_prefered_res) {
+ if (got_good_res) {
+ pjmedia_video_format_detail *vfd;
+
+ /* Apply this size & MPI */
+ vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt,
+ PJ_TRUE);
+ vfd->size = size;
+ vfd->fps.num = 30000;
+ vfd->fps.denum = 1001 * mpi;
+ got_good_res = PJ_TRUE;
+
+ PJ_TODO(NOTIFY_APP_ABOUT_THIS_NEW_ENCODING_FORMAT);
+ } else {
+ return PJ_EUNKNOWN;
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+
+static const ffmpeg_codec_desc* find_codec_info(
+ const pjmedia_vid_codec_info *info)
+{
+ int i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+ ffmpeg_codec_desc *desc = &codec_desc[i];
+
+ if (desc->enabled &&
+ (desc->info.fmt_id == info->fmt_id) &&
+ ((desc->info.dir & info->dir) == info->dir) &&
+ pj_stricmp(&desc->info.encoding_name, &info->encoding_name)==0)
+ {
+ return desc;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int find_codec_info_idx_by_fmt_id(pjmedia_format_id fmt_id)
+{
+ int i;
+ for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+ if (codec_desc[i].info.fmt_id == fmt_id)
+ return i;
+ }
+
+ return -1;
+}
/*
@@ -162,7 +488,6 @@
{
pj_pool_t *pool;
AVCodec *c;
- enum CodecID last_codec_id = CODEC_ID_NONE;
pj_status_t status;
if (ffmpeg_factory.pool != NULL) {
@@ -178,7 +503,6 @@
ffmpeg_factory.base.factory_data = NULL;
ffmpeg_factory.mgr = mgr;
ffmpeg_factory.pf = pf;
- pj_list_init(&ffmpeg_factory.codecs);
pool = pj_pool_create(pf, "ffmpeg codec factory", 256, 256, NULL);
if (!pool)
@@ -196,122 +520,118 @@
/* Enum FFMPEG codecs */
for (c=av_codec_next(NULL); c; c=av_codec_next(c))
{
- ffmpeg_codec_info *ci;
+ ffmpeg_codec_desc *desc;
+ pjmedia_format_id fmt_id;
+ int codec_info_idx;
if (c->type != CODEC_TYPE_VIDEO)
continue;
/* Video encoder and decoder are usually implemented in separate
- * AVCodec instances.
+ * AVCodec instances. While the codec attributes (e.g: raw formats,
+ * supported fps) are in the encoder.
*/
- if (c->id == last_codec_id) {
- /* This codec usually be the decoder, and not as much info as in
- * encoder can be fetched here.
- */
- pj_assert(!pj_list_empty(&ffmpeg_factory.codecs));
- ci = ffmpeg_factory.codecs.prev;
- pj_assert(ci->info.dir != PJMEDIA_DIR_ENCODING_DECODING);
- pj_assert(!ci->dec || !ci->enc);
- } else {
- pjmedia_format_id enc_fmt_id;
- pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
- unsigned raw_fmt_cnt = 0;
- unsigned raw_fmt_cnt_should_be = 0;
+ status = CodecID_to_pjmedia_format_id(c->id, &fmt_id);
+ /* Skip if format ID is unknown */
+ if (status != PJ_SUCCESS)
+ continue;
- /* Get encoded format id */
- status = CodecID_to_pjmedia_format_id(c->id, &enc_fmt_id);
- if (status != PJ_SUCCESS) {
- //PJ_LOG(5, (THIS_FILE, "Unrecognized ffmpeg codec id %d, "
- // "codec [%s/%s] ignored",
- // c->id, c->name, c->long_name));
- //enc_fmt_id = PJMEDIA_FORMAT_FFMPEG_UNKNOWN;
+ codec_info_idx = find_codec_info_idx_by_fmt_id(fmt_id);
+ /* Skip if codec is unwanted by this wrapper (not listed in
+ * the codec info array)
+ */
+ if (codec_info_idx < 0)
+ continue;
- /* Skip unrecognized encoding format ID */
- continue;
- }
+ desc = &codec_desc[codec_info_idx];
- /* Get raw/decoded format ids */
- if (c->pix_fmts) {
- const enum PixelFormat *p = c->pix_fmts;
- for(;(p && *p != -1) &&
- (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
- ++p)
- {
- pjmedia_format_id fmt_id;
+ /* Skip duplicated codec implementation */
+ if ((c->encode && (desc->info.dir & PJMEDIA_DIR_ENCODING)) ||
+ (c->decode && (desc->info.dir & PJMEDIA_DIR_DECODING)))
+ {
+ continue;
+ }
- raw_fmt_cnt_should_be++;
- status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
- if (status != PJ_SUCCESS) {
- PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
- "format %d", *p));
- continue;
- }
- raw_fmt[raw_fmt_cnt++] = fmt_id;
- }
- } else {
- /* Unknown raw format, ignore this codec? */
- continue;
- }
+ /* Get raw/decoded format ids in the encoder */
+ if (c->pix_fmts && c->encode) {
+ pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
+ unsigned raw_fmt_cnt = 0;
+ unsigned raw_fmt_cnt_should_be = 0;
+ const enum PixelFormat *p = c->pix_fmts;
- if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
- PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
- "recognized only %d raw formats",
- c->name, c->long_name,
- raw_fmt_cnt_should_be, raw_fmt_cnt));
- }
- if (raw_fmt_cnt == 0) {
- PJ_LOG(5, (THIS_FILE, "No recognized raw format "
- "for codec [%s/%s], codec ignored",
- c->name, c->long_name));
- /* Comment this to see all ffmpeg codecs */
- continue;
- }
+ for(;(p && *p != -1) &&
+ (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
+ ++p)
+ {
+ pjmedia_format_id fmt_id;
- ci = PJ_POOL_ZALLOC_T(pool, ffmpeg_codec_info);
- ci->info.fmt_id = enc_fmt_id;
- pj_cstr(&ci->info.encoding_name, c->name);
- ci->info.clock_rate = 90000;
- ci->info.dec_fmt_id_cnt = raw_fmt_cnt;
- pj_memcpy(ci->info.dec_fmt_id, raw_fmt,
- sizeof(raw_fmt[0])*raw_fmt_cnt);
+ raw_fmt_cnt_should_be++;
+ status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
+ "format %d", *p));
+ continue;
+ }
+ raw_fmt[raw_fmt_cnt++] = fmt_id;
+ }
- switch (enc_fmt_id) {
- case PJMEDIA_FORMAT_H263:
- ci->info.pt = PJMEDIA_RTP_PT_H263;
- break;
- case PJMEDIA_FORMAT_H261:
- ci->info.pt = PJMEDIA_RTP_PT_H261;
- break;
- default:
- break;
- }
+ if (raw_fmt_cnt == 0) {
+ PJ_LOG(5, (THIS_FILE, "No recognized raw format "
+ "for codec [%s/%s], codec ignored",
+ c->name, c->long_name));
+ /* Skip this encoder */
+ continue;
+ }
- if (c->supported_framerates) {
- const AVRational *fr = c->supported_framerates;
- while ((fr->num != 0 || fr->den != 0) &&
- ci->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
- {
- ci->info.fps[ci->info.fps_cnt].num = fr->num;
- ci->info.fps[ci->info.fps_cnt].denum = fr->den;
- ++ci->info.fps_cnt;
- ++fr;
- }
- }
+ if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
+ PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
+ "recognized only %d raw formats",
+ c->name, c->long_name,
+ raw_fmt_cnt_should_be, raw_fmt_cnt));
+ }
- pj_list_push_back(&ffmpeg_factory.codecs, ci);
+ desc->info.dec_fmt_id_cnt = raw_fmt_cnt;
+ pj_memcpy(desc->info.dec_fmt_id, raw_fmt,
+ sizeof(raw_fmt[0])*raw_fmt_cnt);
+ }
+
+ /* Get supported framerates */
+ if (c->supported_framerates) {
+ const AVRational *fr = c->supported_framerates;
+ while ((fr->num != 0 || fr->den != 0) &&
+ desc->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
+ {
+ desc->info.fps[desc->info.fps_cnt].num = fr->num;
+ desc->info.fps[desc->info.fps_cnt].denum = fr->den;
+ ++desc->info.fps_cnt;
+ ++fr;
+ }
+ }
+
+ /* Get ffmpeg encoder instance */
+ if (c->encode && !desc->enc) {
+ desc->info.dir |= PJMEDIA_DIR_ENCODING;
+ desc->enc = c;
+ }
+
+ /* Get ffmpeg decoder instance */
+ if (c->decode && !desc->dec) {
+ desc->info.dir |= PJMEDIA_DIR_DECODING;
+ desc->dec = c;
}
- if (c->encode) {
- ci->info.dir |= PJMEDIA_DIR_ENCODING;
- ci->enc = c;
- }
- if (c->decode) {
- ci->info.dir |= PJMEDIA_DIR_DECODING;
- ci->dec = c;
- }
+ /* Enable this codec when any ffmpeg codec instance are recognized
+ * and the supported raw formats info has been collected.
+ */
+ if ((desc->dec || desc->enc) && desc->info.dec_fmt_id_cnt)
+ {
+ desc->enabled = PJ_TRUE;
+ }
- last_codec_id = c->id;
+ /* Normalize default value of clock rate */
+ if (desc->info.clock_rate == 0)
+ desc->info.clock_rate = 90000;
}
/* Register codec factory to codec manager. */
@@ -359,42 +679,19 @@
}
-static ffmpeg_codec_info* find_codec(const pjmedia_vid_codec_info *info)
-{
- ffmpeg_codec_info *ci = ffmpeg_factory.codecs.next;
-
- pj_mutex_lock(ffmpeg_factory.mutex);
-
- while (ci != &ffmpeg_factory.codecs) {
- if ((ci->info.fmt_id == info->fmt_id) &&
- ((ci->info.dir & info->dir) == info->dir) &&
- pj_stricmp(&ci->info.encoding_name, &info->encoding_name)==0)
- {
- pj_mutex_unlock(ffmpeg_factory.mutex);
- return ci;
- }
- ci = ci->next;
- }
-
- pj_mutex_unlock(ffmpeg_factory.mutex);
-
- return NULL;
-}
-
-
/*
* Check if factory can allocate the specified codec.
*/
static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory,
const pjmedia_vid_codec_info *info )
{
- ffmpeg_codec_info *ci;
+ const ffmpeg_codec_desc *desc;
PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
PJ_ASSERT_RETURN(info, PJ_EINVAL);
- ci = find_codec(info);
- if (!ci) {
+ desc = find_codec_info(info);
+ if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
@@ -408,23 +705,31 @@
const pjmedia_vid_codec_info *info,
pjmedia_vid_codec_param *attr )
{
- ffmpeg_codec_info *ci;
+ const ffmpeg_codec_desc *desc;
PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
- ci = find_codec(info);
- if (!ci) {
+ desc = find_codec_info(info);
+ if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
- attr->dir = ci->info.dir;
- attr->pt = info->pt;
- pjmedia_format_init_video(&attr->enc_fmt, ci->info.fmt_id,
- 352, 288, 25, 1);
- pjmedia_format_init_video(&attr->dec_fmt, ci->info.dec_fmt_id[0],
- 352, 288, 25, 1);
+
+ /* Direction */
+ attr->dir = desc->info.dir;
+
+ /* Encoded format */
+ pjmedia_format_init_video(&attr->enc_fmt, desc->info.fmt_id,
+ 352, 288, 30000, 1001);
+
+ /* Decoded format */
+ pjmedia_format_init_video(&attr->dec_fmt, desc->info.dec_fmt_id[0],
+ 352, 288, 30000, 1001);
+
+ /* Decoding fmtp */
+ attr->dec_fmtp = desc->dec_fmtp;
return PJ_SUCCESS;
}
@@ -436,23 +741,17 @@
unsigned *count,
pjmedia_vid_codec_info codecs[])
{
- ffmpeg_codec_info *ci;
- unsigned max;
+ unsigned i;
PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
- max = *count;
- *count = 0;
- ci = ffmpeg_factory.codecs.next;
+ *count = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
- pj_mutex_lock(ffmpeg_factory.mutex);
- while (*count < max && ci != &ffmpeg_factory.codecs) {
- pj_memcpy(&codecs[*count], &ci->info, sizeof(pjmedia_vid_codec_info));
- *count = *count + 1;
- ci = ci->next;
+ for (i=0; i<*count; ++i) {
+ pj_memcpy(&codecs[i], &codec_desc[i].info,
+ sizeof(pjmedia_vid_codec_info));
}
- pj_mutex_unlock(ffmpeg_factory.mutex);
return PJ_SUCCESS;
}
@@ -465,7 +764,7 @@
pjmedia_vid_codec **p_codec)
{
ffmpeg_private *ff;
- ffmpeg_codec_info *ci;
+ const ffmpeg_codec_desc *desc;
pjmedia_vid_codec *codec;
pj_pool_t *pool = NULL;
pj_status_t status = PJ_SUCCESS;
@@ -473,8 +772,8 @@
PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
- ci = find_codec(info);
- if (!ci) {
+ desc = find_codec_info(info);
+ if (!desc) {
return PJMEDIA_CODEC_EUNSUP;
}
@@ -494,8 +793,9 @@
}
codec->codec_data = ff;
ff->pool = pool;
- ff->enc = ci->enc;
- ff->dec = ci->dec;
+ ff->enc = desc->enc;
+ ff->dec = desc->dec;
+ ff->desc = desc;
*p_codec = codec;
return PJ_SUCCESS;
@@ -550,35 +850,39 @@
}
-/*
-static enum PixelFormat ffdec_nego_format(struct AVCodecContext *s,
+static enum PixelFormat dec_get_format(struct AVCodecContext *s,
const enum PixelFormat * fmt)
{
- enum PixelFormat pix_fmt;
+ ffmpeg_private *ff = (ffmpeg_private*)s->opaque;
+ enum PixelFormat def_fmt = *fmt;
- PJ_UNUSED_ARG(s);
- PJ_UNUSED_ARG(fmt);
+ while (*fmt != -1) {
+ if (*fmt == ff->expected_dec_fmt)
+ return *fmt;
+ ++fmt;
+ }
- pjmedia_format_id_to_PixelFormat(PJMEDIA_FORMAT_BGRA, &pix_fmt);
- return pix_fmt;
+ pj_assert(!"Inconsistency in supported formats");
+ return def_fmt;
}
-static void enc_got_payload(struct AVCodecContext *avctx,
- void *data, int size, int mb_nb);
-*/
-
static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff,
pj_mutex_t *ff_mutex)
{
enum PixelFormat pix_fmt;
pj_status_t status;
+ pjmedia_video_format_detail *vfd;
status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id,
&pix_fmt);
if (status != PJ_SUCCESS)
return status;
+ vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt,
+ PJ_TRUE);
+ ff->expected_dec_fmt = pix_fmt;
+
while (((ff->param.dir & PJMEDIA_DIR_ENCODING) && ff->enc_ctx == NULL) ||
((ff->param.dir & PJMEDIA_DIR_DECODING) && ff->dec_ctx == NULL))
{
@@ -594,12 +898,11 @@
dir = ff->param.dir;
}
+ /* Init ffmpeg codec context */
ctx = avcodec_alloc_context();
/* Common attributes */
ctx->pix_fmt = pix_fmt;
- ctx->width = ff->param.enc_fmt.det.vid.size.w;
- ctx->height = ff->param.enc_fmt.det.vid.size.h;
ctx->workaround_bugs = FF_BUG_AUTODETECT;
ctx->opaque = ff;
@@ -607,45 +910,35 @@
codec = ff->enc;
/* Encoding only attributes */
- ctx->time_base.num = ff->param.enc_fmt.det.vid.fps.denum;
- ctx->time_base.den = ff->param.enc_fmt.det.vid.fps.num;
- if (ff->param.enc_fmt.det.vid.avg_bps)
- ctx->bit_rate = ff->param.enc_fmt.det.vid.avg_bps;
- if (ff->param.enc_fmt.det.vid.max_bps)
- ctx->rc_max_rate = ff->param.enc_fmt.det.vid.max_bps;
-#if 0
- if (ff->param.enc_mtu) {
- //ctx->rtp_payload_size = ff->param.enc_mtu;
- //ctx->rtp_callback = &enc_got_payload;
+ ctx->width = vfd->size.w;
+ ctx->height = vfd->size.h;
+ ctx->time_base.num = vfd->fps.denum;
+ ctx->time_base.den = vfd->fps.num;
+ if (vfd->avg_bps)
+ ctx->bit_rate = vfd->avg_bps;
+ if (vfd->max_bps)
+ ctx->rc_max_rate = vfd->max_bps;
- /* Allocate frame array for RTP payload packing */
- if (ff->param.enc_fmt.det.vid.max_bps)
- ff->pack_frm_max_cnt = ff->param.enc_fmt.det.vid.max_bps /
- ff->param.enc_mtu + 1;
- else
- ff->pack_frm_max_cnt = 32;
-
- ff->pack_frms = (pjmedia_frame*)
- pj_pool_calloc(ff->pool, ff->pack_frm_max_cnt,
- sizeof(ff->pack_frms[0]));
- }
-#endif
-
- /* For encoder, should be better to be strict to the standards */
+ /* For encoder, should be better to be strict to the standards */
ctx->strict_std_compliance = FF_COMPLIANCE_STRICT;
}
+
if (dir & PJMEDIA_DIR_DECODING) {
codec = ff->dec;
/* Decoding only attributes */
- ctx->coded_width = ctx->width;
- ctx->coded_height = ctx->height;
+ // this setting will be automatically fetched from the bitstream.
+ //ctx->coded_width = ff->param.dec_fmt.det.vid.size.w;
+ //ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
/* For decoder, be more flexible */
- if (ff->param.dir!=PJMEDIA_DIR_ENCODING_DECODING || ff->enc!=ff->dec)
+ if (ff->param.dir!=PJMEDIA_DIR_ENCODING_DECODING ||
+ ff->enc!=ff->dec)
+ {
ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ }
- //ctx->get_format = &ffdec_nego_format;
+ ctx->get_format = &dec_get_format;
}
/* avcodec_open() should be protected */
@@ -679,27 +972,54 @@
PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
ff = (ffmpeg_private*)codec->codec_data;
- ff->vfi = pjmedia_get_video_format_info(NULL, attr->dec_fmt.id);
- if (!ff->vfi) {
- status = PJ_EINVAL;
- goto on_error;
- }
-
- pj_bzero(&ff->vafp, sizeof(ff->vafp));
- ff->vafp.size = attr->dec_fmt.det.vid.size;
- ff->vafp.buffer = 0;
- status = (*ff->vfi->apply_fmt)(ff->vfi, &ff->vafp);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
pj_memcpy(&ff->param, attr, sizeof(*attr));
+ /* Apply SDP fmtp attribute */
+ if (ff->desc->parse_fmtp) {
+ status = (*ff->desc->parse_fmtp)(ff);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* Open the codec */
ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
status = open_ffmpeg_codec(ff, ff_mutex);
if (status != PJ_SUCCESS)
goto on_error;
+ /* Init format info and apply-param of decoder */
+ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->dec_vfi) {
+ status = PJ_EINVAL;
+ goto on_error;
+ }
+ pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+ ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+ ff->dec_vafp.buffer = NULL;
+ status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Init format info and apply-param of encoder */
+ ff->enc_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->enc_vfi) {
+ status = PJ_EINVAL;
+ goto on_error;
+ }
+ pj_bzero(&ff->enc_vafp, sizeof(ff->enc_vafp));
+ ff->enc_vafp.size = ff->param.enc_fmt.det.vid.size;
+ ff->enc_vafp.buffer = NULL;
+ status = (*ff->enc_vfi->apply_fmt)(ff->enc_vfi, &ff->enc_vafp);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Update codec attributes, e.g: encoding format may be changed by
+ * SDP fmtp negotiation.
+ */
+ pj_memcpy(attr, &ff->param, sizeof(*attr));
+
return PJ_SUCCESS;
on_error:
@@ -728,13 +1048,12 @@
avcodec_close(ff->dec_ctx);
av_free(ff->dec_ctx);
}
- if (ff->dec_parser_ctx) {
- av_parser_close(ff->dec_parser_ctx);
-
+ if (ff->sws_ctx) {
+ sws_freeContext(ff->sws_ctx);
}
ff->enc_ctx = NULL;
ff->dec_ctx = NULL;
- ff->dec_parser_ctx = NULL;
+ ff->sws_ctx = NULL;
pj_mutex_unlock(ff_mutex);
return PJ_SUCCESS;
@@ -745,7 +1064,7 @@
* Modify codec settings.
*/
static pj_status_t ffmpeg_codec_modify( pjmedia_vid_codec *codec,
- const pjmedia_vid_codec_param *attr )
+ const pjmedia_vid_codec_param *attr)
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
@@ -755,6 +1074,20 @@
return PJ_ENOTSUP;
}
+static pj_status_t ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+ pjmedia_vid_codec_param *param)
+{
+ ffmpeg_private *ff;
+
+ PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+ ff = (ffmpeg_private*)codec->codec_data;
+ pj_memcpy(param, &ff->param, sizeof(*param));
+
+ return PJ_SUCCESS;
+}
+
+
static pj_status_t ffmpeg_packetize ( pjmedia_vid_codec *codec,
pj_uint8_t *buf,
pj_size_t buf_len,
@@ -764,15 +1097,13 @@
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
- switch (ff->param.enc_fmt.id) {
- case PJMEDIA_FORMAT_H263:
- return pjmedia_h263_packetize(buf, buf_len, pos,
- ff->param.enc_mtu, payload,
- payload_len);
- break;
- default:
- return PJ_ENOTSUP;
+ if (ff->desc->packetize) {
+ return (*ff->desc->packetize)(buf, buf_len, pos,
+ ff->param.enc_mtu, payload,
+ payload_len);
}
+
+ return PJ_ENOTSUP;
}
static pj_status_t ffmpeg_unpacketize(pjmedia_vid_codec *codec,
@@ -783,110 +1114,22 @@
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
- switch (ff->param.enc_fmt.id) {
- case PJMEDIA_FORMAT_H263:
- return pjmedia_h263_unpacketize(payload, payload_len,
- buf, buf_len);
- break;
- default:
- return PJ_ENOTSUP;
+ if (ff->desc->unpacketize) {
+ return (*ff->desc->unpacketize)(payload, payload_len,
+ buf, buf_len);
}
+
+ return PJ_ENOTSUP;
}
-#if 0
-/*
- * Pack encoded frame to RTP payload frames.
- */
-static pj_status_t ffmpeg_codec_pack ( pjmedia_vid_codec *codec,
- const pjmedia_frame *enc_frame,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
-{
- ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
- unsigned i;
-
- /* Can only work when encoding MTU is set */
- PJ_ASSERT_RETURN(ff->param.enc_mtu, PJ_EINVALIDOP);
-
- /* Validate available payload number */
- PJ_ASSERT_RETURN(ff->pack_frm_cnt <= *frame_cnt, PJ_EINVALIDOP);
-
- /* Validate encoded bitstream */
- PJ_ASSERT_RETURN(ff->pack_frm_cnt==0 ||
- ff->pack_frms[0].buf == enc_frame->buf,
- PJ_EINVAL);
-
- /* Return the payloads */
- *frame_cnt = ff->pack_frm_cnt;
- for (i = 0; i < *frame_cnt; ++i)
- frames[i] = ff->pack_frms[i];
-
- return PJ_SUCCESS;
-}
-
-/*
- * Get frames in the packet.
- */
-static pj_status_t ffmpeg_codec_parse( pjmedia_vid_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- const pj_timestamp *ts,
- pjmedia_frame *frame)
-{
- ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
- pj_uint8_t *buf = frame->buf;
- int buf_size = frame->size;
- int processed;
-
-
- if (!ff->dec_parser_ctx) {
- ff->dec_parser_ctx = av_parser_init(ff->dec->id);
- if (!ff->dec_parser_ctx)
- return PJ_ENOTSUP;
- }
-
-#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
- processed = av_parser_parse2(ff->dec_parser_ctx, ff->dec_ctx,
- &buf, &buf_size, (uint8_t*)pkt, pkt_size,
- ts->u64, ts->u64, AV_NOPTS_VALUE);
-#else
- processed = av_parser_parse (ff->dec_parser_ctx, ff->dec_ctx,
- &buf, &buf_size, (uint8_t*)pkt, pkt_size,
- ts->u64, ts->u64);
-#endif
-
- if (buf_size) {
- frame->timestamp = *ts;
- frame->size = buf_size;
- return PJ_SUCCESS;
- }
-
- return PJ_EPENDING;
-}
-
-static void enc_got_payload(struct AVCodecContext *avctx,
- void *data, int size, int mb_nb)
-{
- ffmpeg_private *ff = (ffmpeg_private*) avctx->opaque;
- pjmedia_frame *payload;
-
- pj_assert(ff->pack_frm_cnt < ff->pack_frm_max_cnt);
- payload = &ff->pack_frms[ff->pack_frm_cnt++];
- payload->buf = data;
- payload->size = size;
- payload->bit_info = mb_nb;
-}
-
-#endif
-
/*
* Encode frames.
*/
static pj_status_t ffmpeg_codec_encode( pjmedia_vid_codec *codec,
- const struct pjmedia_frame *input,
+ const pjmedia_frame *input,
unsigned output_buf_len,
- struct pjmedia_frame *output)
+ pjmedia_frame *output)
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
pj_uint8_t *p = (pj_uint8_t*)input->buf;
@@ -899,15 +1142,12 @@
/* Check if encoder has been opened */
PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP);
- /*
- ff->pack_frm_cnt = 0;
- */
avcodec_get_frame_defaults(&avframe);
- for (i = 0; i < ff->vfi->plane_cnt; ++i) {
+ for (i = 0; i < ff->enc_vfi->plane_cnt; ++i) {
avframe.data[i] = p;
- avframe.linesize[i] = ff->vafp.strides[i];
- p += ff->vafp.plane_bytes[i];
+ avframe.linesize[i] = ff->enc_vafp.strides[i];
+ p += ff->enc_vafp.plane_bytes[i];
}
#ifdef _MSC_VER
@@ -945,9 +1185,9 @@
* Decode frame.
*/
static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec,
- const struct pjmedia_frame *input,
+ const pjmedia_frame *input,
unsigned output_buf_len,
- struct pjmedia_frame *output)
+ pjmedia_frame *output)
{
ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
AVFrame avframe;
@@ -957,7 +1197,8 @@
/* Check if decoder has been opened */
PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
- PJ_UNUSED_ARG(output_buf_len);
+ /* Validate output buffer size */
+ PJ_ASSERT_RETURN(ff->dec_vafp.framebytes <= output_buf_len, PJ_ETOOSMALL);
/* Init frame to receive the decoded data, the ffmpeg codec context will
* automatically provide the decoded buffer (single buffer used for the
@@ -979,6 +1220,8 @@
*/
pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE);
+ output->bit_info = 0;
+
#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
avpacket.flags = AV_PKT_FLAG_KEY;
#else
@@ -996,32 +1239,102 @@
print_ffmpeg_err(err);
return PJ_EUNKNOWN;
} else if (got_picture) {
- pjmedia_video_apply_fmt_param *vafp = (pjmedia_video_apply_fmt_param*)
- &ff->vafp;
+ pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
pj_uint8_t *q = (pj_uint8_t*)output->buf;
- unsigned i;
+ unsigned i;
- /* Get the decoded data */
- for (i = 0; i < ff->vfi->plane_cnt; ++i) {
- pj_uint8_t *p = avframe.data[i];
+ /* Decoder output format is set by libavcodec, in case it is different
+ * to the configured param.
+ */
+ if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
+ ff->dec_ctx->coded_width != (int)vafp->size.w ||
+ ff->dec_ctx->coded_height != (int)vafp->size.h)
+ {
+#if 0
+ // it should not be the codec responsibility to do resizing
+ pj_uint8_t *data[PJMEDIA_MAX_VIDEO_PLANES] = {0};
+ unsigned i;
+ int h;
- /* The decoded data may contain padding */
- if (avframe.linesize[i]==vafp->strides[i]) {
- /* No padding, copy the whole plane */
- pj_memcpy(q, p, vafp->plane_bytes[i]);
- q += vafp->plane_bytes[i];
- } else {
- /* Padding exists, copy line by line */
- pj_uint8_t *q_end;
-
- q_end = q+vafp->plane_bytes[i];
- while(q < q_end) {
- pj_memcpy(q, p, vafp->strides[i]);
- q += vafp->strides[i];
- p += avframe.linesize[i];
- }
- }
- }
+ if (!ff->sws_ctx) {
+ pj_assert(sws_isSupportedInput(ff->dec_ctx->pix_fmt) > 0);
+ pj_assert(sws_isSupportedOutput(ff->expected_dec_fmt) > 0);
+ ff->sws_ctx = sws_getContext(ff->dec_ctx->coded_width,
+ ff->dec_ctx->coded_height,
+ ff->dec_ctx->pix_fmt,
+ vafp->size.w, vafp->size.h,
+ ff->expected_dec_fmt,
+ SWS_BILINEAR | SWS_PRINT_INFO,
+ NULL, NULL, NULL);
+ if (ff->sws_ctx == NULL) {
+ return PJ_EUNKNOWN;
+ }
+ }
+
+ for (i = 0; i < ff->vfi->plane_cnt; ++i) {
+ data[i] = q;
+ q += vafp->plane_bytes[i];
+ }
+ h = sws_scale(ff->sws_ctx, avframe.data, avframe.linesize, 0,
+ ff->dec_ctx->coded_height, data, vafp->strides);
+ pj_assert((int)vafp->size.h == h);
+#endif
+
+ pjmedia_format_id new_fmt_id;
+ pj_status_t status;
+
+ /* Get current raw format id from ffmpeg decoder context */
+ status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt,
+ &new_fmt_id);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Update decoder format in param */
+ ff->param.dec_fmt.id = new_fmt_id;
+ ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->coded_width;
+ ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->coded_height;
+
+ /* Re-init format info and apply-param of decoder */
+ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+ if (!ff->dec_vfi)
+ return PJ_EUNKNOWN;
+ pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+ ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+ ff->dec_vafp.buffer = NULL;
+ status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Notify application via the bit_info field of pjmedia_frame */
+ output->bit_info = PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED;
+ }
+
+ /* Check provided buffer size after format changed */
+ if (vafp->framebytes > output_buf_len)
+ return PJ_ETOOSMALL;
+
+ /* Get the decoded data */
+ for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) {
+ pj_uint8_t *p = avframe.data[i];
+
+ /* The decoded data may contain padding */
+ if (avframe.linesize[i]!=vafp->strides[i]) {
+ /* Padding exists, copy line by line */
+ pj_uint8_t *q_end;
+
+ q_end = q+vafp->plane_bytes[i];
+ while(q < q_end) {
+ pj_memcpy(q, p, vafp->strides[i]);
+ q += vafp->strides[i];
+ p += avframe.linesize[i];
+ }
+ } else {
+ /* No padding, copy the whole plane */
+ pj_memcpy(q, p, vafp->plane_bytes[i]);
+ q += vafp->plane_bytes[i];
+ }
+ }
+
output->size = vafp->framebytes;
} else {
return PJ_EUNKNOWN;
@@ -1035,19 +1348,18 @@
*/
static pj_status_t ffmpeg_codec_recover( pjmedia_vid_codec *codec,
unsigned output_buf_len,
- struct pjmedia_frame *output)
+ pjmedia_frame *output)
{
- ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
-
+ PJ_UNUSED_ARG(codec);
PJ_UNUSED_ARG(output_buf_len);
PJ_UNUSED_ARG(output);
- PJ_UNUSED_ARG(ff);
- return PJ_SUCCESS;
+ return PJ_ENOTSUP;
}
#ifdef _MSC_VER
# pragma comment( lib, "avcodec.lib")
+# pragma comment( lib, "swscale.lib")
#endif
#endif /* PJMEDIA_HAS_FFMPEG_CODEC */
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
index 9f5d2c4..74d0622 100644
--- a/pjmedia/src/pjmedia-videodev/colorbar_dev.c
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -80,12 +80,12 @@
/* Video stream. */
struct cbar_stream
{
- pjmedia_vid_stream base; /**< Base stream */
- pjmedia_vid_param param; /**< Settings */
- pj_pool_t *pool; /**< Memory pool. */
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
- pjmedia_vid_cb vid_cb; /**< Stream callback. */
- void *user_data; /**< Application data. */
+ pjmedia_vid_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
const struct cbar_fmt_info *cbfi;
const pjmedia_video_format_info *vfi;
@@ -105,25 +105,26 @@
pjmedia_vid_dev_factory *f,
unsigned index,
pjmedia_vid_param *param);
-static pj_status_t cbar_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm);
+static pj_status_t cbar_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
-static pj_status_t cbar_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *strm,
pjmedia_vid_param *param);
-static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
void *value);
-static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
const void *value);
-static pj_status_t cbar_stream_get_frame(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
pjmedia_frame *frame);
-static pj_status_t cbar_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t cbar_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t cbar_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm);
/* Operations */
static pjmedia_vid_dev_factory_op factory_op =
@@ -136,7 +137,7 @@
&cbar_factory_create_stream
};
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
{
&cbar_stream_get_param,
&cbar_stream_get_cap,
@@ -354,11 +355,12 @@
}
/* API: create stream */
-static pj_status_t cbar_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm)
+static pj_status_t cbar_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
{
struct cbar_factory *cf = (struct cbar_factory*)f;
pj_pool_t *pool;
@@ -419,7 +421,7 @@
}
/* API: Get stream info. */
-static pj_status_t cbar_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *s,
pjmedia_vid_param *pi)
{
struct cbar_stream *strm = (struct cbar_stream*)s;
@@ -438,7 +440,7 @@
}
/* API: get capability */
-static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
void *pval)
{
@@ -458,7 +460,7 @@
}
/* API: set capability */
-static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
const void *pval)
{
@@ -552,7 +554,7 @@
}
/* API: Get frame from stream */
-static pj_status_t cbar_stream_get_frame(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
pjmedia_frame *frame)
{
struct cbar_stream *stream = (struct cbar_stream*)strm;
@@ -561,7 +563,7 @@
}
/* API: Start stream. */
-static pj_status_t cbar_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm)
{
struct cbar_stream *stream = (struct cbar_stream*)strm;
@@ -573,7 +575,7 @@
}
/* API: Stop stream. */
-static pj_status_t cbar_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm)
{
struct cbar_stream *stream = (struct cbar_stream*)strm;
@@ -586,7 +588,7 @@
/* API: Destroy stream. */
-static pj_status_t cbar_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm)
{
struct cbar_stream *stream = (struct cbar_stream*)strm;
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index 7b14c82..2fdd547 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -89,19 +89,19 @@
/* Video stream. */
struct dshow_stream
{
- pjmedia_vid_stream base; /**< Base stream */
- pjmedia_vid_param param; /**< Settings */
- pj_pool_t *pool; /**< Memory pool. */
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
- pjmedia_vid_cb vid_cb; /**< Stream callback. */
- void *user_data; /**< Application data. */
+ pjmedia_vid_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
- pj_bool_t quit_flag;
- pj_bool_t rend_thread_exited;
- pj_bool_t cap_thread_exited;
- pj_bool_t cap_thread_initialized;
- pj_thread_desc cap_thread_desc;
- pj_thread_t *cap_thread;
+ pj_bool_t quit_flag;
+ pj_bool_t rend_thread_exited;
+ pj_bool_t cap_thread_exited;
+ pj_bool_t cap_thread_initialized;
+ pj_thread_desc cap_thread_desc;
+ pj_thread_t *cap_thread;
struct dshow_graph
{
@@ -126,25 +126,26 @@
pjmedia_vid_dev_factory *f,
unsigned index,
pjmedia_vid_param *param);
-static pj_status_t dshow_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm);
+static pj_status_t dshow_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
-static pj_status_t dshow_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
pjmedia_vid_param *param);
-static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
void *value);
-static pj_status_t dshow_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
const void *value);
-static pj_status_t dshow_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t dshow_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
const pjmedia_frame *frame);
-static pj_status_t dshow_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t dshow_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
/* Operations */
static pjmedia_vid_dev_factory_op factory_op =
@@ -157,7 +158,7 @@
&dshow_factory_create_stream
};
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
{
&dshow_stream_get_param,
&dshow_stream_get_cap,
@@ -395,8 +396,8 @@
/* Set the device capabilities here */
param->clock_rate = DEFAULT_CLOCK_RATE;
- param->frame_rate.num = DEFAULT_FPS;
- param->frame_rate.denum = 1;
+ //param->frame_rate.num = DEFAULT_FPS;
+ //param->frame_rate.denum = 1;
param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
pjmedia_format_copy(¶m->fmt, &di->info.fmt[0]);
@@ -438,7 +439,7 @@
}
/* API: Put frame from stream */
-static pj_status_t dshow_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
const pjmedia_frame *frame)
{
struct dshow_stream *stream = (struct dshow_stream*)strm;
@@ -697,11 +698,12 @@
}
/* API: create stream */
-static pj_status_t dshow_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm)
+static pj_status_t dshow_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
{
struct dshow_factory *df = (struct dshow_factory*)f;
pj_pool_t *pool;
@@ -752,12 +754,12 @@
return PJ_SUCCESS;
on_error:
- dshow_stream_destroy((pjmedia_vid_stream *)strm);
+ dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
return PJ_EUNKNOWN;
}
/* API: Get stream info. */
-static pj_status_t dshow_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
pjmedia_vid_param *pi)
{
struct dshow_stream *strm = (struct dshow_stream*)s;
@@ -776,7 +778,7 @@
}
/* API: get capability */
-static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
void *pval)
{
@@ -796,7 +798,7 @@
}
/* API: set capability */
-static pj_status_t dshow_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
const void *pval)
{
@@ -816,7 +818,7 @@
}
/* API: Start stream. */
-static pj_status_t dshow_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
{
struct dshow_stream *stream = (struct dshow_stream*)strm;
unsigned i;
@@ -842,7 +844,7 @@
}
/* API: Stop stream. */
-static pj_status_t dshow_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
{
struct dshow_stream *stream = (struct dshow_stream*)strm;
unsigned i;
@@ -868,7 +870,7 @@
/* API: Destroy stream. */
-static pj_status_t dshow_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
{
struct dshow_stream *stream = (struct dshow_stream*)strm;
unsigned i;
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
index abedbf8..6e6ff8e 100644
--- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -67,7 +67,7 @@
typedef struct ffmpeg_stream
{
- pjmedia_vid_stream base;
+ pjmedia_vid_dev_stream base;
ffmpeg_factory *factory;
pj_pool_t *pool;
pjmedia_vid_param param;
@@ -80,31 +80,32 @@
static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f);
static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f);
static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info);
+ unsigned index,
+ pjmedia_vid_dev_info *info);
static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
pjmedia_vid_dev_factory *f,
unsigned index,
pjmedia_vid_param *param);
-static pj_status_t ffmpeg_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm);
+static pj_status_t ffmpeg_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
-static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_stream *strm,
- pjmedia_vid_param *param);
-static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- void *value);
-static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- const void *value);
-static pj_status_t ffmpeg_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_param *param);
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
pjmedia_frame *frame);
-static pj_status_t ffmpeg_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *strm);
/* Operations */
static pjmedia_vid_dev_factory_op factory_op =
@@ -117,7 +118,7 @@
&ffmpeg_factory_create_stream
};
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
{
&ffmpeg_stream_get_param,
&ffmpeg_stream_get_cap,
@@ -152,14 +153,14 @@
const pjmedia_vid_param *param)
{
AVFormatParameters fp;
- pjmedia_video_format_detail *fmt_detail;
+ pjmedia_video_format_detail *vfd;
int err;
PJ_ASSERT_RETURN(ctx && ifmt && dev_name && param, PJ_EINVAL);
PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
PJ_EINVAL);
- fmt_detail = (pjmedia_video_format_detail*)param->fmt.detail;
+ vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE);
/* Init ffmpeg format context */
*ctx = avformat_alloc_context();
@@ -167,11 +168,11 @@
/* Init ffmpeg format param */
pj_bzero(&fp, sizeof(fp));
fp.prealloced_context = 1;
- fp.width = fmt_detail->size.w;
- fp.height = fmt_detail->size.h;
+ fp.width = vfd->size.w;
+ fp.height = vfd->size.h;
fp.pix_fmt = PIX_FMT_BGR24;
- fp.time_base.num = param->frame_rate.denum;
- fp.time_base.den = param->frame_rate.num;
+ fp.time_base.num = vfd->fps.denum;
+ fp.time_base.den = vfd->fps.num;
/* Open capture stream */
err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp);
@@ -288,8 +289,8 @@
/* API: get device info */
static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
- unsigned index,
- pjmedia_vid_dev_info *info)
+ unsigned index,
+ pjmedia_vid_dev_info *info)
{
ffmpeg_factory *ff = (ffmpeg_factory*)f;
@@ -308,9 +309,11 @@
{
ffmpeg_factory *ff = (ffmpeg_factory*)f;
ffmpeg_dev_info *info;
- pjmedia_video_format_detail *fmt_detail;
PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
info = &ff->dev_info[index];
pj_bzero(param, sizeof(*param));
@@ -321,13 +324,9 @@
/* Set the device capabilities here */
param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
- pj_memcpy(¶m->fmt, &info->base.fmt[0], sizeof(param->fmt));
param->clock_rate = 90000;
- pjmedia_format_init_video(pool, ¶m->fmt, 320, 240, 25, 1,
- 0, 0);
- fmt_detail = (pjmedia_video_format_detail*)param->fmt.detail;
- param->frame_rate.num = fmt_detail->fps.num;
- param->frame_rate.denum = fmt_detail->fps.denum;
+ pjmedia_format_init_video(¶m->fmt, 0, 320, 240, 25, 1);
+ param->fmt.id = info->base.fmt[0].id;
return PJ_SUCCESS;
}
@@ -335,11 +334,12 @@
/* API: create stream */
-static pj_status_t ffmpeg_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm)
+static pj_status_t ffmpeg_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
{
ffmpeg_factory *ff = (ffmpeg_factory*)f;
pj_pool_t *pool;
@@ -348,8 +348,8 @@
PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL);
PJ_ASSERT_RETURN((unsigned)param->cap_id < ff->dev_count, PJ_EINVAL);
- PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
- param->fmt.detail, PJ_EINVAL);
+ PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+ PJ_EINVAL);
PJ_UNUSED_ARG(cb);
PJ_UNUSED_ARG(user_data);
@@ -371,7 +371,7 @@
}
/* API: Get stream info. */
-static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *s,
pjmedia_vid_param *pi)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -384,9 +384,9 @@
}
/* API: get capability */
-static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_stream *s,
- pjmedia_vid_dev_cap cap,
- void *pval)
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -398,9 +398,9 @@
}
/* API: set capability */
-static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_stream *s,
- pjmedia_vid_dev_cap cap,
- const void *pval)
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -413,7 +413,7 @@
/* API: Start stream. */
-static pj_status_t ffmpeg_stream_start(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *s)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
ffmpeg_dev_info *info;
@@ -435,7 +435,7 @@
/* API: Get frame from stream */
-static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
pjmedia_frame *frame)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -458,7 +458,7 @@
/* API: Stop stream. */
-static pj_status_t ffmpeg_stream_stop(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *s)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -472,7 +472,7 @@
/* API: Destroy stream. */
-static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *s)
{
ffmpeg_stream *strm = (ffmpeg_stream*)s;
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
index a60060c..6a05029 100644
--- a/pjmedia/src/pjmedia-videodev/sdl_dev.c
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -90,28 +90,28 @@
/* Video stream. */
struct sdl_stream
{
- pjmedia_vid_stream base; /**< Base stream */
- pjmedia_vid_param param; /**< Settings */
- pj_pool_t *pool; /**< Memory pool. */
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
- pjmedia_vid_cb vid_cb; /**< Stream callback. */
- void *user_data; /**< Application data. */
+ pjmedia_vid_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
- pj_thread_t *sdl_thread; /**< SDL thread. */
- pj_bool_t is_quitting;
- pj_bool_t is_running;
- pj_bool_t render_exited;
- pj_status_t status;
+ pj_thread_t *sdl_thread; /**< SDL thread. */
+ pj_bool_t is_quitting;
+ pj_bool_t is_running;
+ pj_bool_t render_exited;
+ pj_status_t status;
- SDL_Rect rect; /**< Display rectangle. */
- SDL_Surface *screen; /**< Display screen. */
- SDL_Surface *surf; /**< RGB surface. */
- SDL_Overlay *overlay; /**< YUV overlay. */
+ SDL_Rect rect; /**< Display rectangle. */
+ SDL_Surface *screen; /**< Display screen. */
+ SDL_Surface *surf; /**< RGB surface. */
+ SDL_Overlay *overlay; /**< YUV overlay. */
/* For frame conversion */
- pjmedia_converter *conv;
- pjmedia_conversion_param conv_param;
- pjmedia_frame conv_buf;
+ pjmedia_converter *conv;
+ pjmedia_conversion_param conv_param;
+ pjmedia_frame conv_buf;
pjmedia_video_apply_fmt_param vafp;
};
@@ -128,25 +128,26 @@
pjmedia_vid_dev_factory *f,
unsigned index,
pjmedia_vid_param *param);
-static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm);
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
-static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
pjmedia_vid_param *param);
-static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
void *value);
-static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
pjmedia_vid_dev_cap cap,
const void *value);
-static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
const pjmedia_frame *frame);
-static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
/* Operations */
static pjmedia_vid_dev_factory_op factory_op =
@@ -159,7 +160,7 @@
&sdl_factory_create_stream
};
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
{
&sdl_stream_get_param,
&sdl_stream_get_cap,
@@ -407,9 +408,9 @@
break;
}
if (strm->is_running)
- pjmedia_vid_stream_stop(&strm->base);
+ pjmedia_vid_dev_stream_stop(&strm->base);
else
- pjmedia_vid_stream_start(&strm->base);
+ pjmedia_vid_dev_stream_start(&strm->base);
break;
case SDL_VIDEORESIZE:
pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
@@ -433,7 +434,8 @@
/**
* To process PJMEDIA_EVENT_WINDOW_CLOSE event,
* application should do this in the on_event_cb callback:
- * 1. stop further calls to #pjmedia_vid_stream_put_frame()
+ * 1. stop further calls to
+ * #pjmedia_vid_dev_stream_put_frame()
* 2. return PJ_SUCCESS
* Upon returning from the callback, SDL will destroy its
* own stream.
@@ -483,11 +485,12 @@
}
/* API: create stream */
-static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
- const pjmedia_vid_param *param,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm)
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ const pjmedia_vid_param *param,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
{
struct sdl_factory *sf = (struct sdl_factory*)f;
pj_pool_t *pool;
@@ -548,7 +551,7 @@
}
/* API: Get stream info. */
-static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
pjmedia_vid_param *pi)
{
struct sdl_stream *strm = (struct sdl_stream*)s;
@@ -567,7 +570,7 @@
}
/* API: get capability */
-static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
void *pval)
{
@@ -586,7 +589,7 @@
}
/* API: set capability */
-static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
pjmedia_vid_dev_cap cap,
const void *pval)
{
@@ -605,7 +608,7 @@
}
/* API: Put frame from stream */
-static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
const pjmedia_frame *frame)
{
struct sdl_stream *stream = (struct sdl_stream*)strm;
@@ -664,7 +667,7 @@
}
/* API: Start stream. */
-static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
{
struct sdl_stream *stream = (struct sdl_stream*)strm;
@@ -677,7 +680,7 @@
}
/* API: Stop stream. */
-static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
{
struct sdl_stream *stream = (struct sdl_stream*)strm;
unsigned i;
@@ -694,7 +697,7 @@
/* API: Destroy stream. */
-static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
{
struct sdl_stream *stream = (struct sdl_stream*)strm;
SDL_Event sevent;
diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c
index 7ca87cd..522f415 100644
--- a/pjmedia/src/pjmedia-videodev/videodev.c
+++ b/pjmedia/src/pjmedia-videodev/videodev.c
@@ -424,8 +424,8 @@
{
unsigned i;
- /* Allow shutdown() to be called multiple times as long as there is matching
- * number of init().
+ /* Allow shutdown() to be called multiple times as long as there is
+ * matching number of init().
*/
if (vid_subsys.init_count == 0) {
return PJ_SUCCESS;
@@ -546,7 +546,8 @@
PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
for (drv_idx=0; drv_idx<vid_subsys.drv_cnt; ++drv_idx) {
- if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name)) {
+ if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name))
+ {
f = vid_subsys.drv[drv_idx].f;
break;
}
@@ -555,7 +556,8 @@
if (!f)
return PJ_ENOTFOUND;
- for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
+ for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx)
+ {
pjmedia_vid_dev_info info;
pj_status_t status;
@@ -606,10 +608,11 @@
}
/* API: Open video stream object using the specified parameters. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_create(const pjmedia_vid_param *prm,
- const pjmedia_vid_cb *cb,
- void *user_data,
- pjmedia_vid_stream **p_vid_strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create(
+ const pjmedia_vid_param *prm,
+ const pjmedia_vid_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
{
pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
pjmedia_vid_param param;
@@ -674,8 +677,9 @@
}
/* API: Get the running parameters for the specified video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_param(pjmedia_vid_stream *strm,
- pjmedia_vid_param *param)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_param *param)
{
pj_status_t status;
@@ -694,49 +698,54 @@
}
/* API: Get the value of a specific capability of the video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- void *value)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value)
{
return strm->op->get_cap(strm, cap, value);
}
/* API: Set the value of a specific capability of the video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_set_cap(pjmedia_vid_stream *strm,
- pjmedia_vid_dev_cap cap,
- const void *value)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value)
{
return strm->op->set_cap(strm, cap, value);
}
/* API: Start the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
{
return strm->op->start(strm);
}
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_frame(pjmedia_vid_stream *strm,
- pjmedia_frame *frame)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+ pjmedia_vid_dev_stream *strm,
+ pjmedia_frame *frame)
{
pj_assert(strm->op->get_frame);
return strm->op->get_frame(strm, frame);
}
-PJ_DEF(pj_status_t) pjmedia_vid_stream_put_frame(pjmedia_vid_stream *strm,
- const pjmedia_frame *frame)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+ pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
{
pj_assert(strm->op->put_frame);
return strm->op->put_frame(strm, frame);
}
/* API: Stop the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_stop(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
{
return strm->op->stop(strm);
}
/* API: Destroy the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy(
+ pjmedia_vid_dev_stream *strm)
{
return strm->op->destroy(strm);
}
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 7a863f2..9eb98a2 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -68,6 +68,7 @@
int *frame_type; /**< frame type array */
pj_size_t *content_len; /**< frame length array */
pj_uint32_t *bit_info; /**< frame bit info array */
+ pj_uint32_t *ts; /**< timestamp array */
/* States */
unsigned head; /**< index of head, pointed frame
@@ -197,7 +198,10 @@
pj_pool_alloc(pool,
sizeof(framelist->bit_info[0])*
framelist->max_count);
-
+ framelist->ts = (pj_uint32_t*)
+ pj_pool_alloc(pool,
+ sizeof(framelist->ts[0])*
+ framelist->max_count);
return jb_framelist_reset(framelist);
@@ -258,7 +262,8 @@
static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
void *frame, pj_size_t *size,
pjmedia_jb_frame_type *p_type,
- pj_uint32_t *bit_info)
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts)
{
if (framelist->size) {
@@ -281,6 +286,8 @@
*size = framelist->content_len[framelist->head];
if (bit_info)
*bit_info = framelist->bit_info[framelist->head];
+ if (ts)
+ *ts = framelist->ts[framelist->head];
//pj_bzero(framelist->content +
// framelist->head * framelist->frame_size,
@@ -288,6 +295,7 @@
framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME;
framelist->content_len[framelist->head] = 0;
framelist->bit_info[framelist->head] = 0;
+ framelist->ts[framelist->head] = 0;
framelist->origin++;
framelist->head = (framelist->head + 1) % framelist->max_count;
@@ -304,6 +312,49 @@
}
+static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist,
+ unsigned idx,
+ const void **frame,
+ pj_size_t *size,
+ pjmedia_jb_frame_type *type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts)
+{
+ unsigned pos;
+
+ if (idx >= jb_framelist_eff_size(framelist))
+ return PJ_FALSE;
+
+ pos = framelist->head;
+
+ /* Find actual peek position, note there may be discarded frames */
+ while (1) {
+ if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) {
+ if (idx == 0)
+ break;
+ else
+ --idx;
+ }
+ pos = (pos + 1) % framelist->max_count;
+ }
+
+ /* Return the frame pointer */
+ if (frame)
+ *frame = framelist->content + pos*framelist->frame_size;
+ if (type)
+ *type = (pjmedia_jb_frame_type)
+ framelist->frame_type[pos];
+ if (size)
+ *size = framelist->content_len[pos];
+ if (bit_info)
+ *bit_info = framelist->bit_info[pos];
+ if (ts)
+ *ts = framelist->ts[pos];
+
+ return PJ_TRUE;
+}
+
+
/* Remove oldest frames as many as param 'count' */
static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
unsigned count)
@@ -372,6 +423,7 @@
const void *frame,
unsigned frame_size,
pj_uint32_t bit_info,
+ pj_uint32_t ts,
unsigned frame_type)
{
int distance;
@@ -425,6 +477,7 @@
framelist->frame_type[pos] = frame_type;
framelist->content_len[pos] = frame_size;
framelist->bit_info[pos] = bit_info;
+ framelist->ts[pos] = ts;
/* update framelist size */
if (framelist->origin + (int)framelist->size <= index)
@@ -734,7 +787,7 @@
pj_size_t frame_size,
int frame_seq)
{
- pjmedia_jbuf_put_frame2(jb, frame, frame_size, 0, frame_seq, NULL);
+ pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL);
}
PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
@@ -744,6 +797,18 @@
int frame_seq,
pj_bool_t *discarded)
{
+ pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0,
+ discarded);
+}
+
+PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb,
+ const void *frame,
+ pj_size_t frame_size,
+ pj_uint32_t bit_info,
+ int frame_seq,
+ pj_uint32_t ts,
+ pj_bool_t *discarded)
+{
pj_size_t min_frame_size;
int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
pj_status_t status;
@@ -811,7 +876,7 @@
/* Attempt to store the frame */
min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, frame_type);
+ min_frame_size, bit_info, ts, frame_type);
/* Jitter buffer is full, remove some older frames */
while (status == PJ_ETOOMANY) {
@@ -834,7 +899,7 @@
#endif
removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, frame_type);
+ min_frame_size, bit_info, ts, frame_type);
jb->jb_discard += removed;
}
@@ -866,7 +931,7 @@
void *frame,
char *p_frame_type)
{
- pjmedia_jbuf_get_frame2(jb, frame, NULL, p_frame_type, NULL);
+ pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL, NULL);
}
/*
@@ -878,6 +943,19 @@
char *p_frame_type,
pj_uint32_t *bit_info)
{
+ pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info, NULL);
+}
+
+/*
+ * Get frame from jitter buffer.
+ */
+PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
+ void *frame,
+ pj_size_t *size,
+ char *p_frame_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts)
+{
if (jb->jb_status == JB_STATUS_PREFETCHING) {
/* Can't return frame because jitter buffer is filling up
@@ -901,7 +979,7 @@
/* Try to retrieve a frame from frame list */
res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype,
- bit_info);
+ bit_info, ts);
if (res) {
/* We've successfully retrieved a frame from the frame list, but
* the frame could be a blank frame!
@@ -969,3 +1047,48 @@
return PJ_SUCCESS;
}
+
+PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb,
+ unsigned idx,
+ const void **frame,
+ pj_size_t *size,
+ char *p_frm_type,
+ pj_uint32_t *bit_info,
+ pj_uint32_t *ts)
+{
+ pjmedia_jb_frame_type ftype;
+ pj_bool_t res;
+
+ res = jb_framelist_peek(&jb->jb_framelist, idx, frame, size, &ftype, bit_info, ts);
+ if (!res)
+ *p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+ else if (ftype == PJMEDIA_JB_NORMAL_FRAME)
+ *p_frm_type = PJMEDIA_JB_NORMAL_FRAME;
+ else
+ *p_frm_type = PJMEDIA_JB_MISSING_FRAME;
+}
+
+
+PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb,
+ unsigned frame_cnt)
+{
+ unsigned count, last_discard_num;
+
+ last_discard_num = jb->jb_framelist.discarded_num;
+ count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+
+ /* Remove some more when there were discarded frames included */
+ while (jb->jb_framelist.discarded_num < last_discard_num) {
+ /* Calculate frames count to be removed next */
+ frame_cnt = last_discard_num - jb->jb_framelist.discarded_num;
+
+ /* Normalize non-discarded frames count just been removed */
+ count -= frame_cnt;
+
+ /* Remove more frames */
+ last_discard_num = jb->jb_framelist.discarded_num;
+ count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+ }
+
+ return count;
+}
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 74c27b3..f8468a2 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -22,6 +22,7 @@
#include <pjmedia/rtp.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/ctype.h>
@@ -2738,3 +2739,489 @@
return PJ_SUCCESS;
}
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_APPLICATION = { "application", 11};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_codec_mgr *mgr,
+ const pjmedia_sdp_media *local_m,
+ const pjmedia_sdp_media *rem_m)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_rtpmap *rtpmap;
+ unsigned i, fmti, pt = 0;
+ pj_status_t status;
+
+ /* Find the first codec which is not telephone-event */
+ for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
+ if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
+ return PJMEDIA_EINVALIDPT;
+ pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
+ pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
+ break;
+ }
+ if ( fmti >= local_m->desc.fmt_count )
+ return PJMEDIA_EINVALIDPT;
+
+ /* Get codec info.
+ * For static payload types, get the info from codec manager.
+ * For dynamic payload types, MUST get the rtpmap.
+ */
+ if (pt < 96) {
+ pj_bool_t has_rtpmap;
+
+ rtpmap = NULL;
+ has_rtpmap = PJ_TRUE;
+
+ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
+ &local_m->desc.fmt[fmti]);
+ if (attr == NULL) {
+ has_rtpmap = PJ_FALSE;
+ }
+ if (attr != NULL) {
+ status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+ if (status != PJ_SUCCESS)
+ has_rtpmap = PJ_FALSE;
+ }
+
+ /* Build codec format info: */
+ if (has_rtpmap) {
+ si->fmt.type = si->type;
+ si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+ si->fmt.clock_rate = rtpmap->clock_rate;
+
+#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
+ /* The session info should have the actual clock rate, because
+ * this info is used for calculationg buffer size, etc in stream
+ */
+ if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
+ si->fmt.clock_rate = 16000;
+#endif
+
+ /* For audio codecs, rtpmap parameters denotes the number of
+ * channels.
+ */
+ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+ si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+ } else {
+ si->fmt.channel_cnt = 1;
+ }
+
+ } else {
+ const pjmedia_codec_info *p_info;
+
+ status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
+ }
+
+ /* For static payload type, pt's are symetric */
+ si->tx_pt = pt;
+
+ } else {
+
+ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
+ &local_m->desc.fmt[fmti]);
+ if (attr == NULL)
+ return PJMEDIA_EMISSINGRTPMAP;
+
+ status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Build codec format info: */
+
+ si->fmt.type = si->type;
+ si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+ pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+ si->fmt.clock_rate = rtpmap->clock_rate;
+
+ /* For audio codecs, rtpmap parameters denotes the number of
+ * channels.
+ */
+ if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+ si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+ } else {
+ si->fmt.channel_cnt = 1;
+ }
+
+ /* Determine payload type for outgoing channel, by finding
+ * dynamic payload type in remote SDP that matches the answer.
+ */
+ si->tx_pt = 0xFFFF;
+ for (i=0; i<rem_m->desc.fmt_count; ++i) {
+ unsigned rpt;
+ pjmedia_sdp_attr *r_attr;
+ pjmedia_sdp_rtpmap r_rtpmap;
+
+ rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+ if (rpt < 96)
+ continue;
+
+ r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+ &rem_m->desc.fmt[i]);
+ if (!r_attr)
+ continue;
+
+ if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+ continue;
+
+ if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+ rtpmap->clock_rate == r_rtpmap.clock_rate)
+ {
+ /* Found matched codec. */
+ si->tx_pt = rpt;
+
+ break;
+ }
+ }
+
+ if (si->tx_pt == 0xFFFF)
+ return PJMEDIA_EMISSINGRTPMAP;
+ }
+
+
+ /* Now that we have codec info, get the codec param. */
+ si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
+ status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt,
+ si->param);
+
+ /* Get remote fmtp for our encoder. */
+ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+ &si->param->setting.enc_fmtp);
+
+ /* Get local fmtp for our decoder. */
+ pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt,
+ &si->param->setting.dec_fmtp);
+
+ /* Get the remote ptime for our encoder. */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "ptime", NULL);
+ if (attr) {
+ pj_str_t tmp_val = attr->value;
+ unsigned frm_per_pkt;
+
+ pj_strltrim(&tmp_val);
+
+ /* Round up ptime when the specified is not multiple of frm_ptime */
+ frm_per_pkt = (pj_strtoul(&tmp_val) +
+ si->param->info.frm_ptime/2) /
+ si->param->info.frm_ptime;
+ if (frm_per_pkt != 0) {
+ si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt;
+ }
+ }
+
+ /* Get remote maxptime for our encoder. */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "maxptime", NULL);
+ if (attr) {
+ pj_str_t tmp_val = attr->value;
+
+ pj_strltrim(&tmp_val);
+ si->tx_maxptime = pj_strtoul(&tmp_val);
+ }
+
+ /* When direction is NONE (it means SDP negotiation has failed) we don't
+ * need to return a failure here, as returning failure will cause
+ * the whole SDP to be rejected. See ticket #:
+ * http://
+ *
+ * Thanks Alain Totouom
+ */
+ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+ return status;
+
+
+ /* Get incomming payload type for telephone-events */
+ si->rx_event_pt = -1;
+ for (i=0; i<local_m->attr_count; ++i) {
+ pjmedia_sdp_rtpmap r;
+
+ attr = local_m->attr[i];
+ if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+ continue;
+ if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+ si->rx_event_pt = pj_strtoul(&r.pt);
+ break;
+ }
+ }
+
+ /* Get outgoing payload type for telephone-events */
+ si->tx_event_pt = -1;
+ for (i=0; i<rem_m->attr_count; ++i) {
+ pjmedia_sdp_rtpmap r;
+
+ attr = rem_m->attr[i];
+ if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+ continue;
+ if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+ si->tx_event_pt = pj_strtoul(&r.pt);
+ break;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
+ pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx)
+{
+ pjmedia_codec_mgr *mgr;
+ const pjmedia_sdp_attr *attr;
+ const pjmedia_sdp_media *local_m;
+ const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_conn *local_conn;
+ const pjmedia_sdp_conn *rem_conn;
+ int rem_af, local_af;
+ pj_sockaddr local_addr;
+ pj_status_t status;
+
+
+ /* Validate arguments: */
+ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+ /* Keep SDP shortcuts */
+ local_m = local->media[stream_idx];
+ rem_m = remote->media[stream_idx];
+
+ local_conn = local_m->conn ? local_m->conn : local->conn;
+ if (local_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+ if (rem_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ /* Media type must be audio */
+ if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0)
+ return PJMEDIA_EINVALIMEDIATYPE;
+
+ /* Get codec manager. */
+ mgr = pjmedia_endpt_get_codec_mgr(endpt);
+
+ /* Reset: */
+
+ pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+ /* Set default RTCP XR enabled/disabled */
+ si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+ /* Media type: */
+ si->type = PJMEDIA_TYPE_AUDIO;
+
+ /* Transport protocol */
+
+ /* At this point, transport type must be compatible,
+ * the transport instance will do more validation later.
+ */
+ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
+ &local_m->desc.transport);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDPNEG_EINVANSTP;
+
+ if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+ } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+ } else {
+
+ si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+ return PJ_SUCCESS;
+ }
+
+
+ /* Check address family in remote SDP */
+ rem_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+ rem_af = pj_AF_INET();
+ } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+ rem_af = pj_AF_INET6();
+ }
+ }
+
+ if (rem_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_EAFNOTSUP;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
+ rem_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Check address family of local info */
+ local_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+ local_af = pj_AF_INET();
+ } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+ local_af = pj_AF_INET6();
+ }
+ }
+
+ if (local_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_SUCCESS;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
+ local_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Local and remote address family must match */
+ if (local_af != rem_af)
+ return PJ_EAFNOTSUP;
+
+ /* Media direction: */
+
+ if (local_m->desc.port == 0 ||
+ pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+ pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+ pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+ {
+ /* Inactive stream. */
+
+ si->dir = PJMEDIA_DIR_NONE;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+ /* Send only stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+ /* Recv only stream. */
+
+ si->dir = PJMEDIA_DIR_DECODING;
+
+ } else {
+
+ /* Send and receive stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+ }
+
+ /* No need to do anything else if stream is rejected */
+ if (local_m->desc.port == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* If "rtcp" attribute is present in the SDP, set the RTCP address
+ * from that attribute. Otherwise, calculate from RTP address.
+ */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "rtcp", NULL);
+ if (attr) {
+ pjmedia_sdp_rtcp_attr rtcp;
+ status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+ if (status == PJ_SUCCESS) {
+ if (rtcp.addr.slen) {
+ status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+ (pj_uint16_t)rtcp.port);
+ } else {
+ pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
+ (pj_uint16_t)rtcp.port);
+ pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+ pj_sockaddr_get_addr(&si->rem_addr),
+ pj_sockaddr_get_addr_len(&si->rem_addr));
+ }
+ }
+ }
+
+ if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+ int rtcp_port;
+
+ pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+ rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+ pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+ }
+
+
+ /* Get the payload number for receive channel. */
+ /*
+ Previously we used to rely on fmt[0] being the selected codec,
+ but some UA sends telephone-event as fmt[0] and this would
+ cause assert failure below.
+
+ Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.
+
+ // And codec must be numeric!
+ if (!pj_isdigit(*local_m->desc.fmt[0].ptr) ||
+ !pj_isdigit(*rem_m->desc.fmt[0].ptr))
+ {
+ return PJMEDIA_EINVALIDPT;
+ }
+
+ pt = pj_strtoul(&local_m->desc.fmt[0]);
+ pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
+ pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
+ */
+
+ /* Get codec info and param */
+ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m);
+
+ /* Leave SSRC to random. */
+ si->ssrc = pj_rand();
+
+ /* Set default jitter buffer parameter. */
+ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+ return status;
+}
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c
index 9eaca92..567418b 100644
--- a/pjmedia/src/pjmedia/vid_codec.c
+++ b/pjmedia/src/pjmedia/vid_codec.c
@@ -28,13 +28,41 @@
static pjmedia_vid_codec_mgr *def_vid_codec_mgr;
-/* Definition of default codecs parameters */
-struct pjmedia_vid_codec_default_param
+
+/*
+ * Codec manager maintains array of these structs for each supported
+ * codec.
+ */
+typedef struct pjmedia_vid_codec_desc
{
- pjmedia_vid_codec_param *param;
+ pjmedia_vid_codec_info info; /**< Codec info. */
+ pjmedia_codec_id id; /**< Fully qualified name */
+ pjmedia_codec_priority prio; /**< Priority. */
+ pjmedia_vid_codec_factory *factory; /**< The factory. */
+ pjmedia_vid_codec_param *def_param; /**< Default codecs
+ parameters. */
+} pjmedia_vid_codec_desc;
+
+
+/* The declaration of video codec manager */
+struct pjmedia_vid_codec_mgr
+{
+ /** Codec manager mutex. */
+ pj_mutex_t *mutex;
+
+ /** List of codec factories registered to codec manager. */
+ pjmedia_vid_codec_factory factory_list;
+
+ /** Number of supported codecs. */
+ unsigned codec_cnt;
+
+ /** Array of codec descriptor. */
+ pjmedia_vid_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
+
};
+
/* Sort codecs in codec manager based on priorities */
static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
@@ -93,7 +121,7 @@
PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void)
{
- pj_assert(def_vid_codec_mgr);
+ //pj_assert(def_vid_codec_mgr);
return def_vid_codec_mgr;
}
@@ -615,11 +643,11 @@
pj_strdup(pool, &p->dec_fmtp.param[i].val,
¶m->dec_fmtp.param[i].val);
}
- for (i = 0; i < param->dec_fmtp.cnt; ++i) {
- pj_strdup(pool, &p->dec_fmtp.param[i].name,
- ¶m->dec_fmtp.param[i].name);
- pj_strdup(pool, &p->dec_fmtp.param[i].val,
- ¶m->dec_fmtp.param[i].val);
+ for (i = 0; i < param->enc_fmtp.cnt; ++i) {
+ pj_strdup(pool, &p->enc_fmtp.param[i].name,
+ ¶m->enc_fmtp.param[i].name);
+ pj_strdup(pool, &p->enc_fmtp.param[i].val,
+ ¶m->enc_fmtp.param[i].val);
}
pj_mutex_unlock(mgr->mutex);
diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c
new file mode 100644
index 0000000..adbc980
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_stream.c
@@ -0,0 +1,1888 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/vid_stream.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/sock_select.h>
+#include <pj/string.h> /* memcpy() */
+
+
+#define THIS_FILE "vid_stream.c"
+#define ERRLEVEL 1
+#define LOGERR_(expr) stream_perror expr
+#define TRC_(expr) PJ_LOG(5,expr)
+
+/* Tracing jitter buffer operations in a stream session to a CSV file.
+ * The trace will contain JB operation timestamp, frame info, RTP info, and
+ * the JB state right after the operation.
+ */
+#define TRACE_JB 0 /* Enable/disable trace. */
+#define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix
+ for the CSV filename. */
+#if TRACE_JB
+# include <pj/file_io.h>
+# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1)
+# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD)
+#endif
+
+
+
+/**
+ * Media channel.
+ */
+typedef struct pjmedia_vid_channel
+{
+ pjmedia_vid_stream *stream; /**< Parent stream. */
+ pjmedia_dir dir; /**< Channel direction. */
+ pjmedia_port port; /**< Port interface. */
+ unsigned pt; /**< Payload type. */
+ pj_bool_t paused; /**< Paused?. */
+ void *buf; /**< Output buffer. */
+ unsigned buf_size; /**< Size of output buffer. */
+ unsigned buf_len; /**< Length of data in buffer. */
+ pjmedia_rtp_session rtp; /**< RTP session. */
+} pjmedia_vid_channel;
+
+
+/**
+ * This structure describes media stream.
+ * A media stream is bidirectional media transmission between two endpoints.
+ * It consists of two channels, i.e. encoding and decoding channels.
+ * A media stream corresponds to a single "m=" line in a SDP session
+ * description.
+ */
+struct pjmedia_vid_stream
+{
+ pjmedia_endpt *endpt; /**< Media endpoint. */
+ pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */
+
+ pjmedia_vid_channel *enc; /**< Encoding channel. */
+ pjmedia_vid_channel *dec; /**< Decoding channel. */
+
+ pjmedia_dir dir; /**< Stream direction. */
+ void *user_data; /**< User data. */
+ pj_str_t name; /**< Stream name */
+ pj_str_t cname; /**< SDES CNAME */
+
+ pjmedia_transport *transport; /**< Stream transport. */
+
+ pj_mutex_t *jb_mutex;
+ pjmedia_jbuf *jb; /**< Jitter buffer. */
+ char jb_last_frm; /**< Last frame type from jb */
+ unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/
+
+ pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */
+ pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */
+ pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */
+ pj_bool_t initial_rr; /**< Initial RTCP RR sent */
+
+ unsigned frame_size; /**< Size of encoded base frame.*/
+ unsigned frame_ts_len; /**< Frame length in timestamp. */
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time
+ in timestamp. */
+ pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */
+ pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR
+ dest. If sin_family is
+ zero, it will be ignored*/
+ unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest
+ address */
+#endif
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ pj_bool_t use_ka; /**< Stream keep-alive with non-
+ codec-VAD mechanism is
+ enabled? */
+ pj_timestamp last_frm_ts_sent; /**< Timestamp of last sending
+ packet */
+#endif
+
+#if TRACE_JB
+ pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
+ char *trace_jb_buf; /**< Jitter tracing buffer. */
+#endif
+
+ pjmedia_vid_codec *codec; /**< Codec instance being used. */
+ pjmedia_vid_codec_info codec_info; /**< Codec param. */
+ pjmedia_vid_codec_param codec_param; /**< Codec param. */
+
+ pjmedia_vid_stream_info info;
+};
+
+
+/*
+ * Print error.
+ */
+static void stream_perror(const char *sender, const char *title,
+ pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));
+}
+
+
+#if TRACE_JB
+
+PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len)
+{
+ pj_time_val now;
+ pj_parsed_time ptime;
+ char *p = *buf;
+
+ if (len < 14)
+ return -1;
+
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &ptime);
+ p += pj_utoa_pad(ptime.hour, p, 2, '0');
+ *p++ = ':';
+ p += pj_utoa_pad(ptime.min, p, 2, '0');
+ *p++ = ':';
+ p += pj_utoa_pad(ptime.sec, p, 2, '0');
+ *p++ = '.';
+ p += pj_utoa_pad(ptime.msec, p, 3, '0');
+ *p++ = ',';
+
+ *buf = p;
+
+ return 0;
+}
+
+PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream,
+ char **buf, pj_ssize_t len)
+{
+ char *p = *buf;
+ char *endp = *buf + len;
+ pjmedia_jb_state state;
+
+ pjmedia_jbuf_get_state(stream->jb, &state);
+
+ len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d",
+ state.size, state.burst, state.prefetch);
+ if ((len < 0) || (len >= endp-p))
+ return -1;
+
+ p += len;
+ *buf = p;
+ return 0;
+}
+
+static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft,
+ pj_size_t fsize)
+{
+ char *p = stream->trace_jb_buf;
+ char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+ pj_ssize_t len = 0;
+ const char* ft_st;
+
+ if (!TRACE_JB_OPENED(stream))
+ return;
+
+ /* Print timestamp. */
+ if (trace_jb_print_timestamp(&p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print frame type and size */
+ switch(ft) {
+ case PJMEDIA_JB_MISSING_FRAME:
+ ft_st = "missing";
+ break;
+ case PJMEDIA_JB_NORMAL_FRAME:
+ ft_st = "normal";
+ break;
+ case PJMEDIA_JB_ZERO_PREFETCH_FRAME:
+ ft_st = "prefetch";
+ break;
+ case PJMEDIA_JB_ZERO_EMPTY_FRAME:
+ ft_st = "empty";
+ break;
+ default:
+ ft_st = "unknown";
+ break;
+ }
+
+ /* Print operation, size, frame count, frame type */
+ len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st);
+ if ((len < 0) || (len >= endp-p))
+ goto on_insuff_buffer;
+ p += len;
+
+ /* Print JB state */
+ if (trace_jb_print_state(stream, &p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print end of line */
+ if (endp-p < 2)
+ goto on_insuff_buffer;
+ *p++ = '\n';
+
+ /* Write and flush */
+ len = p - stream->trace_jb_buf;
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ return;
+
+on_insuff_buffer:
+ pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+static void trace_jb_put(pjmedia_vid_stream *stream,
+ const pjmedia_rtp_hdr *hdr,
+ unsigned payloadlen, unsigned frame_cnt)
+{
+ char *p = stream->trace_jb_buf;
+ char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+ pj_ssize_t len = 0;
+
+ if (!TRACE_JB_OPENED(stream))
+ return;
+
+ /* Print timestamp. */
+ if (trace_jb_print_timestamp(&p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print operation, size, frame count, RTP info */
+ len = pj_ansi_snprintf(p, endp-p,
+ "PUT,%d,%d,,%d,%d,%d,",
+ payloadlen, frame_cnt,
+ pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m);
+ if ((len < 0) || (len >= endp-p))
+ goto on_insuff_buffer;
+ p += len;
+
+ /* Print JB state */
+ if (trace_jb_print_state(stream, &p, endp-p))
+ goto on_insuff_buffer;
+
+ /* Print end of line */
+ if (endp-p < 2)
+ goto on_insuff_buffer;
+ *p++ = '\n';
+
+ /* Write and flush */
+ len = p - stream->trace_jb_buf;
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ return;
+
+on_insuff_buffer:
+ pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+#endif /* TRACE_JB */
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+/*
+ * Send keep-alive packet using non-codec frame.
+ */
+static void send_keep_alive_packet(pjmedia_vid_stream *stream)
+{
+#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP
+
+ /* Keep-alive packet is empty RTP */
+ pj_status_t status;
+ void *pkt;
+ int pkt_len;
+
+ TRC_((channel->port.info.name.ptr,
+ "Sending keep-alive (RTCP and empty RTP)"));
+
+ /* Send RTP */
+ status = pjmedia_rtp_encode_rtp( &stream->enc->rtp,
+ stream->enc->pt, 0,
+ 1,
+ 0,
+ (const void**)&pkt,
+ &pkt_len);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_memcpy(stream->enc->buf, pkt, pkt_len);
+ pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+ /* Send RTCP */
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &pkt, &pkt_len);
+ pjmedia_transport_send_rtcp(stream->transport, pkt, pkt_len);
+
+#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
+
+ /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */
+ int pkt_len;
+ const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT;
+
+ TRC_((channel->port.info.name.ptr,
+ "Sending keep-alive (custom RTP/RTCP packets)"));
+
+ /* Send to RTP port */
+ pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen);
+ pkt_len = str_ka.slen;
+ pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+ /* Send to RTCP port */
+ pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf,
+ pkt_len);
+
+#else
+
+ PJ_UNUSED_ARG(stream);
+
+#endif
+}
+#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */
+
+
+/**
+ * check_tx_rtcp()
+ *
+ * This function is can be called by either put_frame() or get_frame(),
+ * to transmit periodic RTCP SR/RR report.
+ */
+static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
+{
+ /* Note that timestamp may represent local or remote timestamp,
+ * depending on whether this function is called from put_frame()
+ * or get_frame().
+ */
+
+
+ if (stream->rtcp_last_tx == 0) {
+
+ stream->rtcp_last_tx = timestamp;
+
+ } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
+
+ void *rtcp_pkt;
+ int len;
+
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+
+ pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+ stream->rtcp_last_tx = timestamp;
+ }
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ if (stream->rtcp.xr_enabled) {
+
+ if (stream->rtcp_xr_last_tx == 0) {
+
+ stream->rtcp_xr_last_tx = timestamp;
+
+ } else if (timestamp - stream->rtcp_xr_last_tx >=
+ stream->rtcp_xr_interval)
+ {
+ int i;
+ pjmedia_jb_state jb_state;
+ void *rtcp_pkt;
+ int len;
+
+ /* Update RTCP XR with current JB states */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+
+ i = jb_state.avg_delay;
+ pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
+ PJMEDIA_RTCP_XR_INFO_JB_NOM,
+ i);
+
+ i = jb_state.max_delay;
+ pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
+ PJMEDIA_RTCP_XR_INFO_JB_MAX,
+ i);
+
+ /* Build RTCP XR packet */
+ pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
+ &rtcp_pkt, &len);
+
+ /* Send the RTCP XR to remote address */
+ pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+ /* Send the RTCP XR to third-party destination if specified */
+ if (stream->rtcp_xr_dest_len) {
+ pjmedia_transport_send_rtcp2(stream->transport,
+ &stream->rtcp_xr_dest,
+ stream->rtcp_xr_dest_len,
+ rtcp_pkt, len);
+ }
+
+ /* Update last tx RTCP XR */
+ stream->rtcp_xr_last_tx = timestamp;
+ }
+ }
+#endif
+}
+
+/* Build RTCP SDES packet */
+static unsigned create_rtcp_sdes(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+ unsigned max_len)
+{
+ pjmedia_rtcp_common hdr;
+ pj_uint8_t *p = pkt;
+
+ /* SDES header */
+ hdr.version = 2;
+ hdr.p = 0;
+ hdr.count = 1;
+ hdr.pt = 202;
+ hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
+ if (max_len < (hdr.length << 2)) {
+ pj_assert(!"Not enough buffer for SDES packet");
+ return 0;
+ }
+ hdr.length = pj_htons((pj_uint16_t)hdr.length);
+ hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+ pj_memcpy(p, &hdr, sizeof(hdr));
+ p += sizeof(hdr);
+
+ /* CNAME item */
+ *p++ = 1;
+ *p++ = (pj_uint8_t)stream->cname.slen;
+ pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
+ p += stream->cname.slen;
+
+ /* END */
+ *p++ = '\0';
+ *p++ = '\0';
+
+ /* Pad to 32bit */
+ while ((p-pkt) % 4)
+ *p++ = '\0';
+
+ return (p - pkt);
+}
+
+/* Build RTCP BYE packet */
+static unsigned create_rtcp_bye(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+ unsigned max_len)
+{
+ pjmedia_rtcp_common hdr;
+
+ /* BYE header */
+ hdr.version = 2;
+ hdr.p = 0;
+ hdr.count = 1;
+ hdr.pt = 203;
+ hdr.length = 1;
+ if (max_len < (hdr.length << 2)) {
+ pj_assert(!"Not enough buffer for SDES packet");
+ return 0;
+ }
+ hdr.length = pj_htons((pj_uint16_t)hdr.length);
+ hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+ pj_memcpy(pkt, &hdr, sizeof(hdr));
+
+ return sizeof(hdr);
+}
+
+
+#if 0
+static void dump_bin(const char *buf, unsigned len)
+{
+ unsigned i;
+
+ PJ_LOG(3,(THIS_FILE, "begin dump"));
+ for (i=0; i<len; ++i) {
+ int j;
+ char bits[9];
+ unsigned val = buf[i] & 0xFF;
+
+ bits[8] = '\0';
+ for (j=0; j<8; ++j) {
+ if (val & (1 << (7-j)))
+ bits[j] = '1';
+ else
+ bits[j] = '0';
+ }
+
+ PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
+ }
+ PJ_LOG(3,(THIS_FILE, "end dump"));
+}
+#endif
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTP socket.
+ */
+static void on_rx_rtp( void *data,
+ void *pkt,
+ pj_ssize_t bytes_read)
+
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+ pjmedia_vid_channel *channel = stream->dec;
+ const pjmedia_rtp_hdr *hdr;
+ const void *payload;
+ unsigned payloadlen;
+ pjmedia_rtp_status seq_st;
+ pj_status_t status;
+ pj_bool_t pkt_discarded = PJ_FALSE;
+
+ /* Check for errors */
+ if (bytes_read < 0) {
+ LOGERR_((channel->port.info.name.ptr, "RTP recv() error", -bytes_read));
+ return;
+ }
+
+ /* Ignore keep-alive packets */
+ if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr))
+ return;
+
+ /* Update RTP and RTCP session. */
+ status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, bytes_read,
+ &hdr, &payload, &payloadlen);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr, "RTP decode error", status));
+ stream->rtcp.stat.rx.discard++;
+ return;
+ }
+
+ /* Ignore the packet if decoder is paused */
+ if (channel->paused)
+ goto on_return;
+
+ /* Update RTP session (also checks if RTP session can accept
+ * the incoming packet.
+ */
+ pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE);
+ if (seq_st.status.value) {
+ TRC_ ((channel->port.info.name.ptr,
+ "RTP status: badpt=%d, badssrc=%d, dup=%d, "
+ "outorder=%d, probation=%d, restart=%d",
+ seq_st.status.flag.badpt,
+ seq_st.status.flag.badssrc,
+ seq_st.status.flag.dup,
+ seq_st.status.flag.outorder,
+ seq_st.status.flag.probation,
+ seq_st.status.flag.restart));
+
+ if (seq_st.status.flag.badpt) {
+ PJ_LOG(4,(channel->port.info.name.ptr,
+ "Bad RTP pt %d (expecting %d)",
+ hdr->pt, channel->rtp.out_pt));
+ }
+
+ if (seq_st.status.flag.badssrc) {
+ PJ_LOG(4,(channel->port.info.name.ptr,
+ "Changed RTP peer SSRC %d (previously %d)",
+ channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc));
+ stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+ }
+
+
+ }
+
+ /* Skip bad RTP packet */
+ if (seq_st.status.flag.bad) {
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+ /* Ignore if payloadlen is zero */
+ if (payloadlen == 0) {
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+
+ /* Put "good" packet to jitter buffer, or reset the jitter buffer
+ * when RTP session is restarted.
+ */
+ pj_mutex_lock( stream->jb_mutex );
+ if (seq_st.status.flag.restart) {
+ status = pjmedia_jbuf_reset(stream->jb);
+ PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset"));
+
+ } else {
+
+ /* Video stream */
+
+ /* Just put the payload into jitter buffer */
+ pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0,
+ pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
+
+#if TRACE_JB
+ trace_jb_put(stream, hdr, payloadlen, count);
+#endif
+
+ }
+ pj_mutex_unlock( stream->jb_mutex );
+
+
+ /* Check if now is the time to transmit RTCP SR/RR report.
+ * We only do this when stream direction is "decoding only",
+ * because otherwise check_tx_rtcp() will be handled by put_frame()
+ */
+ if (stream->dir == PJMEDIA_DIR_DECODING) {
+ check_tx_rtcp(stream, pj_ntohl(hdr->ts));
+ }
+
+ if (status != 0) {
+ LOGERR_((channel->port.info.name.ptr, "Jitter buffer put() error",
+ status));
+ pkt_discarded = PJ_TRUE;
+ goto on_return;
+ }
+
+on_return:
+ /* Update RTCP session */
+ if (stream->rtcp.peer_ssrc == 0)
+ stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+
+ pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq),
+ pj_ntohl(hdr->ts), payloadlen, pkt_discarded);
+
+ /* Send RTCP RR and SDES after we receive some RTP packets */
+ if (stream->rtcp.received >= 10 && !stream->initial_rr) {
+ void *sr_rr_pkt;
+ pj_uint8_t *pkt;
+ int len;
+
+ /* Build RR or SR */
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
+ pkt = (pj_uint8_t*) stream->enc->buf;
+ pj_memcpy(pkt, sr_rr_pkt, len);
+ pkt += len;
+
+ /* Append SDES */
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt,
+ stream->enc->buf_size - len);
+ if (len > 0) {
+ pkt += len;
+ len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->buf);
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+
+ stream->initial_rr = PJ_TRUE;
+ }
+}
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTCP socket.
+ */
+static void on_rx_rtcp( void *data,
+ void *pkt,
+ pj_ssize_t bytes_read)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+
+ /* Check for errors */
+ if (bytes_read < 0) {
+ LOGERR_((stream->cname.ptr, "RTCP recv() error",
+ -bytes_read));
+ return;
+ }
+
+ pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read);
+}
+
+static pj_status_t put_frame(pjmedia_port *port,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+ pjmedia_vid_channel *channel = stream->enc;
+ pj_status_t status = 0;
+ pjmedia_frame frame_out;
+ unsigned rtp_ts_len;
+ void *rtphdr;
+ int rtphdrlen;
+ unsigned processed = 0;
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+ /* If the interval since last sending packet is greater than
+ * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet.
+ */
+ if (stream->use_ka)
+ {
+ pj_uint32_t dtx_duration;
+
+ dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent,
+ &frame->timestamp);
+ if (dtx_duration >
+ PJMEDIA_STREAM_KA_INTERVAL * channel->port.info.clock_rate)
+ {
+ send_keep_alive_packet(stream);
+ stream->last_frm_ts_sent = frame->timestamp;
+ }
+ }
+#endif
+
+ /* Don't do anything if stream is paused */
+ if (channel->paused) {
+ return PJ_SUCCESS;
+ }
+
+ /* Get frame length in timestamp unit */
+ rtp_ts_len = stream->frame_ts_len;
+
+ /* Init frame_out buffer. */
+ frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr);
+ frame_out.size = 0;
+
+ /* Encode! */
+ status = (*stream->codec->op->encode)(stream->codec, frame,
+ channel->buf_size -
+ sizeof(pjmedia_rtp_hdr),
+ &frame_out);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "Codec encode() error", status));
+ return status;
+ }
+
+
+ while (processed < frame_out.size) {
+ pj_uint8_t *payload, *rtp_pkt;
+ pj_size_t payload_len;
+
+ /* Generate RTP payload */
+ status = (*stream->codec->op->packetize)(
+ stream->codec,
+ (pj_uint8_t*)frame_out.buf,
+ frame_out.size,
+ &processed,
+ &payload, &payload_len);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "Codec pack() error", status));
+ return status;
+ }
+
+ /* Encapsulate. */
+ status = pjmedia_rtp_encode_rtp( &channel->rtp,
+ channel->pt,
+ (processed==frame_out.size?1:0),
+ payload_len,
+ rtp_ts_len,
+ (const void**)&rtphdr,
+ &rtphdrlen);
+
+ if (status != PJ_SUCCESS) {
+ LOGERR_((channel->port.info.name.ptr,
+ "RTP encode_rtp() error", status));
+ return status;
+ }
+
+ /* Next packets use same timestamp */
+ rtp_ts_len = 0;
+
+ rtp_pkt = payload - sizeof(pjmedia_rtp_hdr);
+
+ /* Copy RTP header to the beginning of packet */
+ pj_memcpy(rtp_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
+
+ /* Send the RTP packet to the transport. */
+ pjmedia_transport_send_rtp(stream->transport, rtp_pkt,
+ payload_len + sizeof(pjmedia_rtp_hdr));
+ }
+
+ /* Check if now is the time to transmit RTCP SR/RR report.
+ * We only do this when stream direction is not "decoding only", because
+ * when it is, check_tx_rtcp() will be handled by get_frame().
+ */
+ if (stream->dir != PJMEDIA_DIR_DECODING) {
+ check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
+ }
+
+ /* Do nothing if we have nothing to transmit */
+ if (frame_out.size == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Update stat */
+ pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
+ stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+ stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq);
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ /* Update timestamp of last sending packet. */
+ stream->last_frm_ts_sent = frame->timestamp;
+#endif
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t get_frame(pjmedia_port *port,
+ pjmedia_frame *frame)
+{
+ pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+ pjmedia_vid_channel *channel = stream->dec;
+ pjmedia_frame frame_in;
+ pj_status_t status;
+
+ /* Return no frame is channel is paused */
+ if (channel->paused) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ return PJ_SUCCESS;
+ }
+
+ /* Repeat get payload from the jitter buffer until all payloads with same
+ * timestamp are collected (a complete frame unpacketized).
+ */
+ {
+ pj_uint8_t *p, *data;
+ char ptype;
+ pj_size_t psize, data_len;
+ pj_uint32_t ts, last_ts;
+ pj_bool_t got_frame;
+ unsigned i;
+
+ channel->buf_len = 0;
+ last_ts = 0;
+ got_frame = PJ_FALSE;
+
+ /* Lock jitter buffer mutex first */
+ pj_mutex_lock( stream->jb_mutex );
+
+ for (i=0; ; ++i) {
+ /* Get frame from jitter buffer. */
+ pjmedia_jbuf_peek_frame(stream->jb, i, &p, &psize, &ptype,
+ NULL, &ts);
+ if (ptype == PJMEDIA_JB_NORMAL_FRAME) {
+ if (last_ts == 0)
+ last_ts = ts;
+
+ if (ts != last_ts) {
+ got_frame = PJ_TRUE;
+ pjmedia_jbuf_remove_frame(stream->jb, i);
+ break;
+ }
+
+ data = (pj_uint8_t*)channel->buf + channel->buf_len;
+ data_len = channel->buf_size - channel->buf_len;
+ status = (*stream->codec->op->unpacketize)(stream->codec,
+ p, psize,
+ data, &data_len);
+ channel->buf_len += data_len;
+ } else if (ptype == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+ /* No more packet in the jitter buffer */
+ break;
+ }
+ }
+
+ /* Unlock jitter buffer mutex. */
+ pj_mutex_unlock( stream->jb_mutex );
+
+ if (!got_frame) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Decode */
+ frame_in.buf = channel->buf;
+ frame_in.size = channel->buf_len;
+ frame_in.bit_info = 0;
+ frame_in.type = PJMEDIA_FRAME_TYPE_VIDEO;
+
+ status = stream->codec->op->decode(stream->codec, &frame_in,
+ frame->size, frame);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((port->info.name.ptr, "codec decode() error",
+ status));
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ }
+
+ /* Check if the decoder format is changed */
+ if (frame->bit_info & PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED) {
+ /* Update param from codec */
+ stream->codec->op->get_param(stream->codec, &stream->codec_param);
+
+ /* Update decoding channel port info */
+ stream->dec->port.info.fmt = stream->codec_param.dec_fmt;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create media channel.
+ */
+static pj_status_t create_channel( pj_pool_t *pool,
+ pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ unsigned pt,
+ const pjmedia_vid_stream_info *info,
+ pjmedia_vid_channel **p_channel)
+{
+ enum { M = 32 };
+ pjmedia_vid_channel *channel;
+ pj_status_t status;
+ unsigned min_out_pkt_size;
+ pj_str_t name;
+ const char *type_name;
+ pjmedia_format *fmt;
+
+ pj_assert(info->type == PJMEDIA_TYPE_VIDEO);
+ pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING);
+
+ /* Allocate memory for channel descriptor */
+ channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel);
+ PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM);
+
+ /* Init vars */
+ if (dir==PJMEDIA_DIR_DECODING) {
+ type_name = "vstrmdec";
+ fmt = &info->codec_param->dec_fmt;
+ } else {
+ type_name = "vstrmenc";
+ fmt = &info->codec_param->enc_fmt;
+ }
+
+ name.ptr = (char*) pj_pool_alloc(pool, M);
+ name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream);
+
+ /* Init channel info. */
+ channel->stream = stream;
+ channel->dir = dir;
+ channel->paused = 1;
+ channel->pt = pt;
+
+ /* Allocate buffer for outgoing packet. */
+ channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size;
+
+ /* It should big enough to hold (minimally) RTCP SR with an SDES. */
+ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
+ sizeof(pjmedia_rtcp_common) +
+ (4 + stream->cname.slen) +
+ 32;
+
+ if (channel->buf_size < min_out_pkt_size)
+ channel->buf_size = min_out_pkt_size;
+
+ channel->buf = pj_pool_alloc(pool, channel->buf_size);
+ PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM);
+
+ /* Create RTP and RTCP sessions: */
+ if (info->rtp_seq_ts_set == 0) {
+ status = pjmedia_rtp_session_init(&channel->rtp, pt, info->ssrc);
+ } else {
+ pjmedia_rtp_session_setting settings;
+
+ settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | 3);
+ settings.default_pt = pt;
+ settings.sender_ssrc = info->ssrc;
+ settings.seq = info->rtp_seq;
+ settings.ts = info->rtp_ts;
+ status = pjmedia_rtp_session_init2(&channel->rtp, settings);
+ }
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init port. */
+ pjmedia_port_info_init2(&channel->port.info, &name,
+ PJMEDIA_PORT_SIGNATURE('V', 'C', 'H', 'N'),
+ dir, fmt);
+ if (dir == PJMEDIA_DIR_DECODING) {
+ channel->port.get_frame = &get_frame;
+ } else {
+ channel->port.put_frame = &put_frame;
+ }
+
+ /* Init port. */
+ channel->port.port_data.pdata = stream;
+
+ /* Done. */
+ *p_channel = channel;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
+ pjmedia_endpt *endpt,
+ pj_pool_t *pool,
+ const pjmedia_vid_stream_info *info,
+ pjmedia_transport *tp,
+ void *user_data,
+ pjmedia_vid_stream **p_stream)
+{
+ enum { M = 32 };
+ pjmedia_vid_stream *stream;
+ unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+ int frm_ptime, chunks_per_frm;
+ pjmedia_video_format_detail *vfd_enc;
+ char *p;
+ pj_status_t status;
+
+ /* Allocate stream */
+ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream);
+ PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+
+ /* Get codec manager */
+ stream->codec_mgr = pjmedia_vid_codec_mgr_instance();
+ PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED);
+
+ /* Init stream/port name */
+ stream->name.ptr = (char*) pj_pool_alloc(pool, M);
+ stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M,
+ "vstrm%p", stream);
+
+ /* Create and initialize codec: */
+ stream->codec_info = info->codec_info;
+ status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr,
+ &info->codec_info,
+ &stream->codec);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Get codec param: */
+ if (info->codec_param)
+ stream->codec_param = *info->codec_param;
+ else {
+ status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr,
+ &info->codec_info,
+ &stream->codec_param);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ vfd_enc = pjmedia_format_get_video_format_detail(
+ &stream->codec_param.enc_fmt, 1);
+
+ /* Init stream: */
+ stream->endpt = endpt;
+ stream->dir = info->dir;
+ stream->user_data = user_data;
+ stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
+ info->codec_info.clock_rate / 1000;
+
+ stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ stream->use_ka = info->use_ka;
+#endif
+
+ /* Build random RTCP CNAME. CNAME has user@host format */
+ stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20);
+ pj_create_random_string(p, 5);
+ p += 5;
+ *p++ = '@'; *p++ = 'p'; *p++ = 'j';
+ pj_create_random_string(p, 6);
+ p += 6;
+ *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g';
+ stream->cname.slen = p - stream->cname.ptr;
+
+
+ /* Create mutex to protect jitter buffer: */
+
+ status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init codec param */
+ stream->codec_param.dir = info->dir;
+ stream->codec_param.enc_mtu = PJMEDIA_MAX_MTU - sizeof(pjmedia_rtp_hdr);
+
+ /* Init and open the codec. */
+ status = stream->codec->op->init(stream->codec, pool);
+ if (status != PJ_SUCCESS)
+ return status;
+ status = stream->codec->op->open(stream->codec, &stream->codec_param);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Get the frame size */
+ stream->frame_size = vfd_enc->max_bps * vfd_enc->fps.denum /
+ vfd_enc->fps.num;
+
+ /* As the maximum frame_size is not represented directly by maximum bps
+ * (which includes intra and predicted frames), let's increase the
+ * frame size value for safety.
+ */
+ stream->frame_size <<= 2;
+
+ /* Validate the frame size */
+ if (stream->frame_size == 0 ||
+ stream->frame_size > PJMEDIA_MAX_VIDEO_FRAME_SIZE)
+ {
+ stream->frame_size = PJMEDIA_MAX_VIDEO_FRAME_SIZE;
+ }
+
+ /* Get frame length in timestamp unit */
+ stream->frame_ts_len = info->codec_info.clock_rate *
+ vfd_enc->fps.denum / vfd_enc->fps.num;
+
+ /* Create decoder channel */
+ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
+ info->codec_info.pt, info, &stream->dec);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Create encoder channel */
+ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING,
+ info->tx_pt, info, &stream->enc);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init jitter buffer parameters: */
+ frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num;
+ chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MTU;
+
+ /* JB max count, default 500ms */
+ if (info->jb_max >= frm_ptime)
+ jb_max = info->jb_max * chunks_per_frm / frm_ptime;
+ else
+ jb_max = 500 * chunks_per_frm / frm_ptime;
+
+ /* JB min prefetch, default 1 frame */
+ if (info->jb_min_pre >= frm_ptime)
+ jb_min_pre = info->jb_min_pre * chunks_per_frm / frm_ptime;
+ else
+ jb_min_pre = 1;
+
+ /* JB max prefetch, default 4/5 JB max count */
+ if (info->jb_max_pre >= frm_ptime)
+ jb_max_pre = info->jb_max_pre * chunks_per_frm / frm_ptime;
+ else
+ jb_max_pre = jb_max * 4 / 5;
+
+ /* JB init prefetch, default 0 */
+ if (info->jb_init >= frm_ptime)
+ jb_init = info->jb_init * chunks_per_frm / frm_ptime;
+ else
+ jb_init = 0;
+
+ /* Create jitter buffer */
+ status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name,
+ PJMEDIA_MAX_MTU,
+ 1000 * vfd_enc->fps.denum / vfd_enc->fps.num,
+ jb_max, &stream->jb);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Set up jitter buffer */
+ pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
+ //pjmedia_jbuf_enable_discard(stream->jb, PJ_FALSE);
+
+ /* Init RTCP session: */
+ {
+ pjmedia_rtcp_session_setting rtcp_setting;
+
+ pjmedia_rtcp_session_setting_default(&rtcp_setting);
+ rtcp_setting.name = stream->name.ptr;
+ rtcp_setting.ssrc = info->ssrc;
+ rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+ rtcp_setting.clock_rate = info->codec_info.clock_rate;
+ rtcp_setting.samples_per_frame = 1;
+
+ pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
+ }
+
+ /* Only attach transport when stream is ready. */
+ status = pjmedia_transport_attach(tp, stream, &info->rem_addr,
+ &info->rem_rtcp,
+ pj_sockaddr_get_len(&info->rem_addr),
+ &on_rx_rtp, &on_rx_rtcp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ stream->transport = tp;
+
+ /* Send RTCP SDES */
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->buf,
+ stream->enc->buf_size);
+ if (len != 0) {
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+ /* NAT hole punching by sending KA packet via RTP transport. */
+ if (stream->use_ka)
+ send_keep_alive_packet(stream);
+#endif
+
+#if TRACE_JB
+ {
+ char trace_name[PJ_MAXPATH];
+ pj_ssize_t len;
+
+ pj_ansi_snprintf(trace_name, sizeof(trace_name),
+ TRACE_JB_PATH_PREFIX "%s.csv",
+ channel->port.info.name.ptr);
+ status = pj_file_open(pool, trace_name, PJ_O_RDWR,
+ &stream->trace_jb_fd);
+ if (status != PJ_SUCCESS) {
+ stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+ PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'",
+ trace_name));
+ } else {
+ stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE);
+
+ /* Print column header */
+ len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE,
+ "Time, Operation, Size, Frame Count, "
+ "Frame type, RTP Seq, RTP TS, RTP M, "
+ "JB size, JB burst level, JB prefetch\n");
+ pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+ pj_file_flush(stream->trace_jb_fd);
+ }
+ }
+#endif
+
+ /* Success! */
+ *p_stream = stream;
+
+ PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->name.ptr));
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream )
+{
+ unsigned len;
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ /* Send RTCP XR on stream destroy */
+ if (stream->rtcp.xr_enabled) {
+ int i;
+ pjmedia_jb_state jb_state;
+ void *rtcp_pkt;
+ int len;
+
+ /* Update RTCP XR with current JB states */
+ pjmedia_jbuf_get_state(stream->jb, &jb_state);
+
+ i = jb_state.avg_delay;
+ pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
+ PJMEDIA_RTCP_XR_INFO_JB_NOM,
+ i);
+
+ i = jb_state.max_delay;
+ pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
+ PJMEDIA_RTCP_XR_INFO_JB_MAX,
+ i);
+
+ /* Build RTCP XR packet */
+ pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
+ &rtcp_pkt, &len);
+
+ /* Send the RTCP XR to remote address */
+ pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+ /* Send the RTCP XR to third-party destination if specified */
+ if (stream->rtcp_xr_dest_len) {
+ pjmedia_transport_send_rtcp2(stream->transport,
+ &stream->rtcp_xr_dest,
+ stream->rtcp_xr_dest_len,
+ rtcp_pkt, len);
+ }
+ }
+#endif
+
+ /* Send RTCP BYE */
+ if (stream->enc && stream->transport) {
+ len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->buf,
+ stream->enc->buf_size);
+ if (len != 0) {
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->buf, len);
+ }
+ }
+
+ /* Detach from transport
+ * MUST NOT hold stream mutex while detaching from transport, as
+ * it may cause deadlock. See ticket #460 for the details.
+ */
+ if (stream->transport) {
+ pjmedia_transport_detach(stream->transport, stream);
+ stream->transport = NULL;
+ }
+
+ /* This function may be called when stream is partly initialized. */
+ if (stream->jb_mutex)
+ pj_mutex_lock(stream->jb_mutex);
+
+
+ /* Free codec. */
+ if (stream->codec) {
+ stream->codec->op->close(stream->codec);
+ pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
+ stream->codec = NULL;
+ }
+
+ /* Free mutex */
+
+ if (stream->jb_mutex) {
+ pj_mutex_destroy(stream->jb_mutex);
+ stream->jb_mutex = NULL;
+ }
+
+ /* Destroy jitter buffer */
+ if (stream->jb)
+ pjmedia_jbuf_destroy(stream->jb);
+
+#if TRACE_JB
+ if (TRACE_JB_OPENED(stream)) {
+ pj_file_close(stream->trace_jb_fd);
+ stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Get the port interface.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream,
+ pjmedia_dir dir,
+ pjmedia_port **p_port )
+{
+ PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING,
+ PJ_EINVAL);
+
+ if (dir == PJMEDIA_DIR_ENCODING)
+ *p_port = &stream->enc->port;
+ else
+ *p_port = &stream->dec->port;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the transport object
+ */
+PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+ pjmedia_vid_stream *st)
+{
+ return st->transport;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
+{
+
+ PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
+
+ if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
+ stream->enc->paused = 0;
+ //pjmedia_snd_stream_start(stream->enc->snd_stream);
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
+ } else {
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+ }
+
+ if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
+ stream->dec->paused = 0;
+ //pjmedia_snd_stream_start(stream->dec->snd_stream);
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
+ } else {
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get stream statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_stat *stat)
+{
+ PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+ pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Reset the stream statistics in the middle of a stream session.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ pjmedia_rtcp_init_stat(&stream->rtcp.stat);
+
+ return PJ_SUCCESS;
+}
+
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+/*
+ * Get stream extended statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr(
+ const pjmedia_vid_stream *stream,
+ pjmedia_rtcp_xr_stat *stat)
+{
+ PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+ if (stream->rtcp.xr_enabled) {
+ pj_memcpy(stat, &stream->rtcp.xr_session.stat,
+ sizeof(pjmedia_rtcp_xr_stat));
+ return PJ_SUCCESS;
+ }
+ return PJ_ENOTFOUND;
+}
+#endif
+
+/*
+ * Get jitter buffer state.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+ const pjmedia_vid_stream *stream,
+ pjmedia_jb_state *state)
+{
+ PJ_ASSERT_RETURN(stream && state, PJ_EINVAL);
+ return pjmedia_jbuf_get_state(stream->jb, state);
+}
+
+/*
+ * Pause stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+ pjmedia_dir dir)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+ stream->enc->paused = 1;
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+ }
+
+ if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+ stream->dec->paused = 1;
+
+ /* Also reset jitter buffer */
+ pj_mutex_lock( stream->jb_mutex );
+ pjmedia_jbuf_reset(stream->jb);
+ pj_mutex_unlock( stream->jb_mutex );
+
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Resume stream
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+ pjmedia_dir dir)
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+ stream->enc->paused = 0;
+ PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed"));
+ }
+
+ if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+ stream->dec->paused = 0;
+ PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed"));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_APPLICATION = { "application", 11};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_vid_codec_mgr *mgr,
+ const pjmedia_sdp_media *local_m,
+ const pjmedia_sdp_media *rem_m)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_rtpmap *rtpmap;
+ unsigned i, pt = 0;
+ pj_status_t status;
+
+ /* Get codec info.
+ * For static payload types, get the info from codec manager.
+ * For dynamic payload types, MUST get the rtpmap.
+ */
+ pt = pj_strtoul(&local_m->desc.fmt[0]);
+ if (pt < 96) {
+ const pjmedia_vid_codec_info *p_info;
+
+ status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ si->codec_info = *p_info;
+
+ /* For static payload type, pt's are symetric */
+ si->tx_pt = pt;
+
+ } else {
+ unsigned info_cnt = 1;
+ const pjmedia_vid_codec_info *p_info;
+
+ attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
+ &local_m->desc.fmt[0]);
+ if (attr == NULL)
+ return PJMEDIA_EMISSINGRTPMAP;
+
+ status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Get codec info from codec id */
+ status = pjmedia_vid_codec_mgr_find_codecs_by_id(
+ mgr, &rtpmap->enc_name,
+ &info_cnt, &p_info, NULL);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ si->codec_info = *p_info;
+
+ /* Determine payload type for outgoing channel, by finding
+ * dynamic payload type in remote SDP that matches the answer.
+ */
+ si->tx_pt = 0xFFFF;
+ for (i=0; i<rem_m->desc.fmt_count; ++i) {
+ unsigned rpt;
+ pjmedia_sdp_attr *r_attr;
+ pjmedia_sdp_rtpmap r_rtpmap;
+
+ rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+ if (rpt < 96)
+ continue;
+
+ r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+ &rem_m->desc.fmt[i]);
+ if (!r_attr)
+ continue;
+
+ if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+ continue;
+
+ if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+ rtpmap->clock_rate == r_rtpmap.clock_rate)
+ {
+ /* Found matched codec. */
+ si->tx_pt = rpt;
+
+ break;
+ }
+ }
+
+ if (si->tx_pt == 0xFFFF)
+ return PJMEDIA_EMISSINGRTPMAP;
+ }
+
+
+ /* Now that we have codec info, get the codec param. */
+ si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param);
+ status = pjmedia_vid_codec_mgr_get_default_param(mgr,
+ &si->codec_info,
+ si->codec_param);
+
+ /* Get remote fmtp for our encoder. */
+ pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+ &si->codec_param->enc_fmtp);
+
+ /* Get local fmtp for our decoder. */
+ pjmedia_stream_info_parse_fmtp(pool, local_m, pt,
+ &si->codec_param->dec_fmtp);
+
+ /* When direction is NONE (it means SDP negotiation has failed) we don't
+ * need to return a failure here, as returning failure will cause
+ * the whole SDP to be rejected. See ticket #:
+ * http://
+ *
+ * Thanks Alain Totouom
+ */
+ if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
+ pjmedia_vid_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx)
+{
+ const pjmedia_sdp_attr *attr;
+ const pjmedia_sdp_media *local_m;
+ const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_conn *local_conn;
+ const pjmedia_sdp_conn *rem_conn;
+ int rem_af, local_af;
+ pj_sockaddr local_addr;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(endpt);
+
+ /* Validate arguments: */
+ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+ /* Keep SDP shortcuts */
+ local_m = local->media[stream_idx];
+ rem_m = remote->media[stream_idx];
+
+ local_conn = local_m->conn ? local_m->conn : local->conn;
+ if (local_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+ if (rem_conn == NULL)
+ return PJMEDIA_SDP_EMISSINGCONN;
+
+ /* Media type must be audio */
+ if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0)
+ return PJMEDIA_EINVALIMEDIATYPE;
+
+
+ /* Reset: */
+
+ pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+ /* Set default RTCP XR enabled/disabled */
+ si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+ /* Media type: */
+ si->type = PJMEDIA_TYPE_VIDEO;
+
+ /* Transport protocol */
+
+ /* At this point, transport type must be compatible,
+ * the transport instance will do more validation later.
+ */
+ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
+ &local_m->desc.transport);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_SDPNEG_EINVANSTP;
+
+ if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+ } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+ si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+ } else {
+
+ si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+ return PJ_SUCCESS;
+ }
+
+
+ /* Check address family in remote SDP */
+ rem_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+ rem_af = pj_AF_INET();
+ } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+ rem_af = pj_AF_INET6();
+ }
+ }
+
+ if (rem_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_EAFNOTSUP;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
+ rem_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Check address family of local info */
+ local_af = pj_AF_UNSPEC();
+ if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+ if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+ local_af = pj_AF_INET();
+ } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+ local_af = pj_AF_INET6();
+ }
+ }
+
+ if (local_af==pj_AF_UNSPEC()) {
+ /* Unsupported address family */
+ return PJ_SUCCESS;
+ }
+
+ /* Set remote address: */
+ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
+ local_m->desc.port);
+ if (status != PJ_SUCCESS) {
+ /* Invalid IP address. */
+ return PJMEDIA_EINVALIDIP;
+ }
+
+ /* Local and remote address family must match */
+ if (local_af != rem_af)
+ return PJ_EAFNOTSUP;
+
+ /* Media direction: */
+
+ if (local_m->desc.port == 0 ||
+ pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+ pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+ pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+ {
+ /* Inactive stream. */
+
+ si->dir = PJMEDIA_DIR_NONE;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+ /* Send only stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING;
+
+ } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+ /* Recv only stream. */
+
+ si->dir = PJMEDIA_DIR_DECODING;
+
+ } else {
+
+ /* Send and receive stream. */
+
+ si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+ }
+
+ /* No need to do anything else if stream is rejected */
+ if (local_m->desc.port == 0) {
+ return PJ_SUCCESS;
+ }
+
+ /* If "rtcp" attribute is present in the SDP, set the RTCP address
+ * from that attribute. Otherwise, calculate from RTP address.
+ */
+ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+ "rtcp", NULL);
+ if (attr) {
+ pjmedia_sdp_rtcp_attr rtcp;
+ status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+ if (status == PJ_SUCCESS) {
+ if (rtcp.addr.slen) {
+ status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+ (pj_uint16_t)rtcp.port);
+ } else {
+ pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
+ (pj_uint16_t)rtcp.port);
+ pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+ pj_sockaddr_get_addr(&si->rem_addr),
+ pj_sockaddr_get_addr_len(&si->rem_addr));
+ }
+ }
+ }
+
+ if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+ int rtcp_port;
+
+ pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+ rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+ pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+ }
+
+ /* Get codec info and param */
+ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m);
+
+ /* Leave SSRC to random. */
+ si->ssrc = pj_rand();
+
+ /* Set default jitter buffer parameter. */
+ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+ return status;
+}
+
diff --git a/pjmedia/src/pjmedia/videoport.c b/pjmedia/src/pjmedia/videoport.c
index 1e66b57..9ff2340 100644
--- a/pjmedia/src/pjmedia/videoport.c
+++ b/pjmedia/src/pjmedia/videoport.c
@@ -37,7 +37,7 @@
pj_str_t dev_name;
pjmedia_dir dir;
pjmedia_rect_size cap_size;
- pjmedia_vid_stream *strm;
+ pjmedia_vid_dev_stream *strm;
pjmedia_vid_cb strm_cb;
void *strm_cb_data;
enum role role,
@@ -74,13 +74,13 @@
pjmedia_vid_port *vp;
};
-static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame);
-static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame);
-static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_event_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_vid_event *event);
@@ -170,8 +170,8 @@
vid_cb.render_cb = &vidstream_render_cb;
vid_cb.on_event_cb = &vidstream_event_cb;
- status = pjmedia_vid_stream_create(&prm->vidparam, &vid_cb, vp,
- &vp->strm);
+ status = pjmedia_vid_dev_stream_create(&prm->vidparam, &vid_cb, vp,
+ &vp->strm);
if (status != PJ_SUCCESS)
goto on_error;
@@ -277,7 +277,7 @@
vid_port->strm_cb_data = user_data;
}
-PJ_DEF(pjmedia_vid_stream*)
+PJ_DEF(pjmedia_vid_dev_stream*)
pjmedia_vid_port_get_stream(pjmedia_vid_port *vp)
{
PJ_ASSERT_RETURN(vp, NULL);
@@ -356,7 +356,7 @@
PJ_ASSERT_RETURN(vp, PJ_EINVAL);
- status = pjmedia_vid_stream_start(vp->strm);
+ status = pjmedia_vid_dev_stream_start(vp->strm);
if (status != PJ_SUCCESS)
goto on_error;
@@ -385,7 +385,7 @@
PJ_ASSERT_RETURN(vp, PJ_EINVAL);
- status = pjmedia_vid_stream_stop(vp->strm);
+ status = pjmedia_vid_dev_stream_stop(vp->strm);
if (vp->enc_clock) {
status = pjmedia_clock_stop(vp->enc_clock);
@@ -413,7 +413,7 @@
vp->dec_clock = NULL;
}
if (vp->strm) {
- pjmedia_vid_stream_destroy(vp->strm);
+ pjmedia_vid_dev_stream_destroy(vp->strm);
vp->strm = NULL;
}
if (vp->client_port) {
@@ -477,7 +477,7 @@
if (!vp->client_port)
return;
- status = pjmedia_vid_stream_get_frame(vp->strm, vp->enc_frm_buf);
+ status = pjmedia_vid_dev_stream_get_frame(vp->strm, vp->enc_frm_buf);
if (status != PJ_SUCCESS)
return;
@@ -580,6 +580,7 @@
ndrop = nsync_frame;
} else
vp->rend_sync_clocksrc.nsync_progress += ndrop;
+
for (i = 0; i < ndrop; i++) {
status = pjmedia_port_get_frame(vp->client_port,
vp->dec_frm_buf);
@@ -602,7 +603,7 @@
pj_add_timestamp32(&vp->rend_clocksrc.timestamp, frame_ts);
pjmedia_clock_src_update(&vp->rend_clocksrc, NULL);
- status = pjmedia_vid_stream_put_frame(vp->strm, vp->dec_frm_buf);
+ status = pjmedia_vid_dev_stream_put_frame(vp->strm, vp->dec_frm_buf);
}
static void copy_frame(pjmedia_frame *dst, const pjmedia_frame *src)
@@ -614,7 +615,7 @@
dst->size = src->size;
}
-static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame)
{
@@ -633,7 +634,7 @@
return PJ_SUCCESS;
}
-static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_frame *frame)
{
@@ -652,7 +653,7 @@
return PJ_SUCCESS;
}
-static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_event_cb(pjmedia_vid_dev_stream *stream,
void *user_data,
pjmedia_vid_event *event)
{
@@ -670,7 +671,7 @@
pjmedia_vid_port *vp = vpp->vp;
if (vp->stream_role==ROLE_PASSIVE) {
- return pjmedia_vid_stream_put_frame(vp->strm, frame);
+ return pjmedia_vid_dev_stream_put_frame(vp->strm, frame);
} else {
pj_mutex_lock(vp->dec_frm_mutex);
copy_frame(vp->dec_frm_buf, frame);
@@ -687,7 +688,7 @@
pjmedia_vid_port *vp = vpp->vp;
if (vp->stream_role==ROLE_PASSIVE) {
- return pjmedia_vid_stream_get_frame(vp->strm, frame);
+ return pjmedia_vid_dev_stream_get_frame(vp->strm, frame);
} else {
pj_mutex_lock(vp->enc_frm_mutex);
copy_frame(frame, vp->enc_frm_buf);