summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/codecs/vpx/tdav_codec_vp8.c')
-rwxr-xr-xtinyDAV/src/codecs/vpx/tdav_codec_vp8.c1643
1 files changed, 824 insertions, 819 deletions
diff --git a/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c b/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
index 5ed2f80..25434c8 100755
--- a/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
+++ b/tinyDAV/src/codecs/vpx/tdav_codec_vp8.c
@@ -70,43 +70,42 @@
#endif
/* VP8 codec */
-typedef struct tdav_codec_vp8_s
-{
- TMEDIA_DECLARE_CODEC_VIDEO;
-
- // Encoder
- struct {
- vpx_codec_enc_cfg_t cfg;
- tsk_bool_t initialized;
- vpx_codec_pts_t pts;
- vpx_codec_ctx_t context;
- unsigned pic_id : 15;
- uint64_t frame_count;
- tsk_bool_t force_idr;
- int rotation;
-
- struct {
- uint8_t* ptr;
- tsk_size_t size;
- } rtp;
-
- tsk_mutex_handle_t* mutex;
- } encoder;
-
- // decoder
- struct {
- vpx_codec_dec_cfg_t cfg;
- unsigned initialized : 1;
- vpx_codec_ctx_t context;
- void* accumulator;
- tsk_size_t accumulator_pos;
- tsk_size_t accumulator_size;
- tsk_size_t first_part_size;
- uint16_t last_seq;
- uint32_t last_timestamp;
- tsk_bool_t idr;
- tsk_bool_t corrupted;
- } decoder;
+typedef struct tdav_codec_vp8_s {
+ TMEDIA_DECLARE_CODEC_VIDEO;
+
+ // Encoder
+ struct {
+ vpx_codec_enc_cfg_t cfg;
+ tsk_bool_t initialized;
+ vpx_codec_pts_t pts;
+ vpx_codec_ctx_t context;
+ unsigned pic_id : 15;
+ uint64_t frame_count;
+ tsk_bool_t force_idr;
+ int rotation;
+
+ struct {
+ uint8_t* ptr;
+ tsk_size_t size;
+ } rtp;
+
+ tsk_mutex_handle_t* mutex;
+ } encoder;
+
+ // decoder
+ struct {
+ vpx_codec_dec_cfg_t cfg;
+ unsigned initialized : 1;
+ vpx_codec_ctx_t context;
+ void* accumulator;
+ tsk_size_t accumulator_pos;
+ tsk_size_t accumulator_size;
+ tsk_size_t first_part_size;
+ uint16_t last_seq;
+ uint32_t last_timestamp;
+ tsk_bool_t idr;
+ tsk_bool_t corrupted;
+ } decoder;
}
tdav_codec_vp8_t;
@@ -125,500 +124,508 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
{
- tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
- vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
- tsk_bool_t reconf = tsk_false;
-
- if (param->value_type == tmedia_pvt_int32) {
- if (tsk_striequals(param->key, "action")) {
- tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
- switch (action) {
- case tmedia_codec_action_encode_idr:
- {
- vp8->encoder.force_idr = tsk_true;
- return 0;
- }
- case tmedia_codec_action_bw_down:
- {
- vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate << 1) / 3), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
- TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
- reconf = tsk_true;
- break;
- }
- case tmedia_codec_action_bw_up:
- {
- vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate * 3) >> 1), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
- TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
- reconf = tsk_true;
- break;
- }
- }
- }
- else if (tsk_striequals(param->key, "bw_kbps")) { // both up and down (from the SDP)
- int32_t max_bw_userdefine = tmedia_defaults_get_bandwidth_video_upload_max();
- int32_t max_bw_new = *((int32_t*)param->value);
- if (max_bw_userdefine > 0) {
- // do not use more than what the user defined in it's configuration
- TMEDIA_CODEC(vp8)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine);
- }
- else {
- TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
- }
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
+ tsk_bool_t reconf = tsk_false;
+
+ if (param->value_type == tmedia_pvt_int32) {
+ if (tsk_striequals(param->key, "action")) {
+ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
+ switch (action) {
+ case tmedia_codec_action_encode_idr: {
+ vp8->encoder.force_idr = tsk_true;
+ return 0;
+ }
+ case tmedia_codec_action_bw_down: {
+ vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate << 1) / 3), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
+ TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
+ reconf = tsk_true;
+ break;
+ }
+ case tmedia_codec_action_bw_up: {
+ vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (int32_t)((vp8->encoder.cfg.rc_target_bitrate * 3) >> 1), TMEDIA_CODEC(vp8)->bandwidth_max_upload);
+ TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
+ reconf = tsk_true;
+ break;
+ }
+ }
+ }
+ else if (tsk_striequals(param->key, "bw_kbps")) { // both up and down (from the SDP)
+ int32_t max_bw_userdefine = tmedia_defaults_get_bandwidth_video_upload_max();
+ int32_t max_bw_new = *((int32_t*)param->value);
+ if (max_bw_userdefine > 0) {
+ // do not use more than what the user defined in it's configuration
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine);
+ }
+ else {
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
+ }
vp8->encoder.cfg.rc_target_bitrate = TSK_CLAMP(0, (unsigned int)vp8->encoder.cfg.rc_target_bitrate, (unsigned int)TMEDIA_CODEC(vp8)->bandwidth_max_upload);
TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
- reconf = tsk_true;
- }
- else if (tsk_striequals(param->key, "bandwidth-max-upload")) {
- int32_t bw_max_upload = *((int32_t*)param->value);
- TSK_DEBUG_INFO("VP8 codec: bandwidth-max-upload=%d", bw_max_upload);
- TMEDIA_CODEC(vp8)->bandwidth_max_upload = bw_max_upload;
- reconf = tsk_true;
- }
- else if (tsk_striequals(param->key, "rotation")) {
- // IMPORTANT: changing resolution requires at least libvpx v1.1.0 "Eider"
- int32_t rotation = *((int32_t*)param->value);
- if (vp8->encoder.rotation != rotation) {
- vp8->encoder.rotation = rotation;
- vp8->encoder.cfg.g_w = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.height : TMEDIA_CODEC_VIDEO(vp8)->out.width;
- vp8->encoder.cfg.g_h = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.width : TMEDIA_CODEC_VIDEO(vp8)->out.height;
- reconf = tsk_true;
- }
- }
- }
-
- if (reconf) {
- if (vp8->encoder.initialized) {
- // The encoder isn't thread safe. Without this lock (and the one in the encode() function) we may have corruptions in the video (issue report from GE).
- // Google says the encoder is thread-safe but this is not the case. But it is *multi-instance* thread-safe.
- tsk_mutex_lock(vp8->encoder.mutex);
- if ((vpx_ret = vpx_codec_enc_config_set(&vp8->encoder.context, &vp8->encoder.cfg)) != VPX_CODEC_OK) {
- TSK_DEBUG_ERROR("vpx_codec_enc_config_set failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- }
- tsk_mutex_unlock(vp8->encoder.mutex);
- }
- return (vpx_ret == VPX_CODEC_OK) ? 0 : -2;
- }
-
- return -1;
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "bandwidth-max-upload")) {
+ int32_t bw_max_upload = *((int32_t*)param->value);
+ TSK_DEBUG_INFO("VP8 codec: bandwidth-max-upload=%d", bw_max_upload);
+ TMEDIA_CODEC(vp8)->bandwidth_max_upload = bw_max_upload;
+ reconf = tsk_true;
+ }
+ else if (tsk_striequals(param->key, "rotation")) {
+ // IMPORTANT: changing resolution requires at least libvpx v1.1.0 "Eider"
+ int32_t rotation = *((int32_t*)param->value);
+ if (vp8->encoder.rotation != rotation) {
+ vp8->encoder.rotation = rotation;
+ vp8->encoder.cfg.g_w = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.height : TMEDIA_CODEC_VIDEO(vp8)->out.width;
+ vp8->encoder.cfg.g_h = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(vp8)->out.width : TMEDIA_CODEC_VIDEO(vp8)->out.height;
+ reconf = tsk_true;
+ }
+ }
+ }
+
+ if (reconf) {
+ if (vp8->encoder.initialized) {
+ // The encoder isn't thread safe. Without this lock (and the one in the encode() function) we may have corruptions in the video (issue report from GE).
+ // Google says the encoder is thread-safe but this is not the case. But it is *multi-instance* thread-safe.
+ tsk_mutex_lock(vp8->encoder.mutex);
+ if ((vpx_ret = vpx_codec_enc_config_set(&vp8->encoder.context, &vp8->encoder.cfg)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_config_set failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ }
+ tsk_mutex_unlock(vp8->encoder.mutex);
+ }
+ return (vpx_ret == VPX_CODEC_OK) ? 0 : -2;
+ }
+
+ return -1;
}
static int tdav_codec_vp8_open(tmedia_codec_t* self)
{
- tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
- int ret;
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ int ret;
- if (!vp8) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
+ if (!vp8) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
- /* the caller (base class) already checked that the codec is not opened */
+ /* the caller (base class) already checked that the codec is not opened */
- // Encoder
- if ((ret = tdav_codec_vp8_open_encoder(vp8))) {
- return ret;
- }
+ // Encoder
+ if ((ret = tdav_codec_vp8_open_encoder(vp8))) {
+ return ret;
+ }
- // Decoder
- if ((ret = tdav_codec_vp8_open_decoder(vp8))) {
- return ret;
- }
+ // Decoder
+ if ((ret = tdav_codec_vp8_open_decoder(vp8))) {
+ return ret;
+ }
- return ret;
+ return ret;
}
static int tdav_codec_vp8_close(tmedia_codec_t* self)
{
- tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
- if (!vp8) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
+ if (!vp8) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
- tdav_codec_vp8_close_encoder(vp8);
- tdav_codec_vp8_close_decoder(vp8);
+ tdav_codec_vp8_close_encoder(vp8);
+ tdav_codec_vp8_close_decoder(vp8);
- return 0;
+ return 0;
}
static tsk_size_t tdav_codec_vp8_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
{
- tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
- vpx_enc_frame_flags_t flags = 0;
- vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
- const vpx_codec_cx_pkt_t *pkt;
- vpx_codec_iter_t iter = tsk_null;
- vpx_image_t image = {0};
-
- if (!vp8 || !in_data || !in_size) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return 0;
- }
-
- if (in_size != (vp8->encoder.context.config.enc->g_w * vp8->encoder.context.config.enc->g_h * 3) >> 1) {
- TSK_DEBUG_ERROR("Invalid size");
- return 0;
- }
-
- // wrap yuv420 buffer
- if (!vpx_img_wrap(&image, VPX_IMG_FMT_I420, vp8->encoder.context.config.enc->g_w, vp8->encoder.context.config.enc->g_h, 1, (unsigned char*)in_data)) {
- TSK_DEBUG_ERROR("vpx_img_wrap failed");
- return 0;
- }
-
- // encode data
- ++vp8->encoder.pts;
- if (vp8->encoder.force_idr) {
- flags |= VPX_EFLAG_FORCE_KF;
- vp8->encoder.force_idr = tsk_false;
- }
- tsk_mutex_lock(vp8->encoder.mutex); // must
- vpx_ret = vpx_codec_encode(&vp8->encoder.context, &image, vp8->encoder.pts, 1, flags, VPX_DL_REALTIME);
- tsk_mutex_unlock(vp8->encoder.mutex);
-
- if (vpx_ret != VPX_CODEC_OK) {
- TSK_DEBUG_ERROR("vpx_codec_encode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- goto bail;
- }
-
- ++vp8->encoder.frame_count;
- ++vp8->encoder.pic_id;
-
- while ((pkt = vpx_codec_get_cx_data(&vp8->encoder.context, &iter))) {
- switch (pkt->kind) {
- case VPX_CODEC_CX_FRAME_PKT:
- {
- tdav_codec_vp8_encap(vp8, pkt);
- break;
- }
- default:
- case VPX_CODEC_STATS_PKT: /**< Two-pass statistics for this frame */
- case VPX_CODEC_PSNR_PKT: /**< PSNR statistics for this frame */
- case VPX_CODEC_CUSTOM_PKT: /**< Algorithm extensions */
- {
- TSK_DEBUG_INFO("pkt->kind=%d not supported", (int)pkt->kind);
- break;
- }
- }
- }
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ vpx_enc_frame_flags_t flags = 0;
+ vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
+ const vpx_codec_cx_pkt_t *pkt;
+ vpx_codec_iter_t iter = tsk_null;
+ vpx_image_t image = {0};
+
+ if (!vp8 || !in_data || !in_size) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ if (in_size != (vp8->encoder.context.config.enc->g_w * vp8->encoder.context.config.enc->g_h * 3) >> 1) {
+ TSK_DEBUG_ERROR("Invalid size");
+ return 0;
+ }
+
+ // wrap yuv420 buffer
+ if (!vpx_img_wrap(&image, VPX_IMG_FMT_I420, vp8->encoder.context.config.enc->g_w, vp8->encoder.context.config.enc->g_h, 1, (unsigned char*)in_data)) {
+ TSK_DEBUG_ERROR("vpx_img_wrap failed");
+ return 0;
+ }
+
+ // encode data
+ ++vp8->encoder.pts;
+ if (vp8->encoder.force_idr) {
+ flags |= VPX_EFLAG_FORCE_KF;
+ vp8->encoder.force_idr = tsk_false;
+ }
+ tsk_mutex_lock(vp8->encoder.mutex); // must
+ vpx_ret = vpx_codec_encode(&vp8->encoder.context, &image, vp8->encoder.pts, 1, flags, VPX_DL_REALTIME);
+ tsk_mutex_unlock(vp8->encoder.mutex);
+
+ if (vpx_ret != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_encode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ goto bail;
+ }
+
+ ++vp8->encoder.frame_count;
+ ++vp8->encoder.pic_id;
+
+ while ((pkt = vpx_codec_get_cx_data(&vp8->encoder.context, &iter))) {
+ switch (pkt->kind) {
+ case VPX_CODEC_CX_FRAME_PKT: {
+ tdav_codec_vp8_encap(vp8, pkt);
+ break;
+ }
+ default:
+ case VPX_CODEC_STATS_PKT: /**< Two-pass statistics for this frame */
+ case VPX_CODEC_PSNR_PKT: /**< PSNR statistics for this frame */
+ case VPX_CODEC_CUSTOM_PKT: { /**< Algorithm extensions */
+ TSK_DEBUG_INFO("pkt->kind=%d not supported", (int)pkt->kind);
+ break;
+ }
+ }
+ }
bail:
- vpx_img_free(&image);
- return 0;
+ vpx_img_free(&image);
+ return 0;
}
static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
{
- tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
- const trtp_rtp_header_t* rtp_hdr = proto_hdr;
- const uint8_t* pdata = in_data;
- const uint8_t* pdata_end = (pdata + in_size);
- tsk_size_t ret = 0;
- tsk_bool_t fatal_error = tsk_false;
- static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
- uint8_t S, PartID;
-
- if (!self || !in_data || in_size < 1 || !out_data || !vp8->decoder.initialized) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return 0;
- }
-
- { /* 4.2. VP8 Payload Descriptor */
- uint8_t X, R, N, I, L, T, K;//TODO: store
-
- X = ((*pdata & 0x80) >> 7);
- R = ((*pdata & 0x40) >> 6);
- if (R) {
- TSK_DEBUG_ERROR("R<>0");
- fatal_error = tsk_true;
- goto bail;
- }
- N = ((*pdata & 0x20) >> 5);
- S = ((*pdata & 0x10) >> 4);
- PartID = (*pdata & 0x0F);
- // skip "REQUIRED" header
- if (++pdata >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
- // check "OPTIONAL" headers
- if (X) {
- I = (*pdata & 0x80);
- L = (*pdata & 0x40);
- T = (*pdata & 0x20);
- K = (*pdata & 0x10);
- if (++pdata >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
-
- if (I) {
- if (*pdata & 0x80) { // M
- // PictureID on 16bits
- if ((pdata += 2) >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
- }
- else {
- // PictureID on 8bits
- if (++pdata >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
- }
- }
- if (L) {
- if (++pdata >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
- }
- if (T || K) {
- if (++pdata >= pdata_end) {
- TSK_DEBUG_ERROR("Too short"); goto bail;
- }
- }
- }
- }
-
- in_size = (pdata_end - pdata);
-
- // Packet lost?
- if (vp8->decoder.last_seq && (vp8->decoder.last_seq + 1) != rtp_hdr->seq_num) {
- TSK_DEBUG_INFO("[VP8] Packet loss, seq_num=%d", (vp8->decoder.last_seq + 1));
- vp8->decoder.corrupted = tsk_true;
- }
- vp8->decoder.last_seq = rtp_hdr->seq_num;
-
- // New frame ?
- if (vp8->decoder.last_timestamp != rtp_hdr->timestamp) {
- /* 4.3. VP8 Payload Header
- Note that the header is present only in packets
- which have the S bit equal to one and the PartID equal to zero in the
- payload descriptor. Subsequent packets for the same frame do not
- carry the payload header.
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |Size0|H| VER |P|
- +-+-+-+-+-+-+-+-+
- | Size1 |
- +-+-+-+-+-+-+-+-+
- | Size2 |
- +-+-+-+-+-+-+-+-+
- | Bytes 4..N of |
- | VP8 payload |
- : :
- +-+-+-+-+-+-+-+-+
- | OPTIONAL RTP |
- | padding |
- : :
- +-+-+-+-+-+-+-+-+
- P: Inverse key frame flag. When set to 0 the current frame is a key
- frame. When set to 1 the current frame is an interframe. Defined
- in [RFC6386]
- */
-
- // Reset accumulator position
- vp8->decoder.accumulator_pos = 0;
-
- // Make sure the header is present
- if (S != 1 || PartID != 0 || in_size < 3) {
- TSK_DEBUG_WARN("VP8 payload header is missing");
+ tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
+ const trtp_rtp_header_t* rtp_hdr = proto_hdr;
+ const uint8_t* pdata = in_data;
+ const uint8_t* pdata_end = (pdata + in_size);
+ tsk_size_t ret = 0;
+ tsk_bool_t fatal_error = tsk_false;
+ static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
+ uint8_t S, PartID;
+
+ if (!self || !in_data || in_size < 1 || !out_data || !vp8->decoder.initialized) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return 0;
+ }
+
+ { /* 4.2. VP8 Payload Descriptor */
+ uint8_t X, R, N, I, L, T, K;//TODO: store
+
+ X = ((*pdata & 0x80) >> 7);
+ R = ((*pdata & 0x40) >> 6);
+ if (R) {
+ TSK_DEBUG_ERROR("R<>0");
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ N = ((*pdata & 0x20) >> 5);
+ S = ((*pdata & 0x10) >> 4);
+ PartID = (*pdata & 0x0F);
+ // skip "REQUIRED" header
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+ // check "OPTIONAL" headers
+ if (X) {
+ I = (*pdata & 0x80);
+ L = (*pdata & 0x40);
+ T = (*pdata & 0x20);
+ K = (*pdata & 0x10);
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+
+ if (I) {
+ if (*pdata & 0x80) { // M
+ // PictureID on 16bits
+ if ((pdata += 2) >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+ }
+ else {
+ // PictureID on 8bits
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+ }
+ }
+ if (L) {
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+ }
+ if (T || K) {
+ if (++pdata >= pdata_end) {
+ TSK_DEBUG_ERROR("Too short");
+ goto bail;
+ }
+ }
+ }
+ }
+
+ in_size = (pdata_end - pdata);
+
+ // Packet lost?
+ if (vp8->decoder.last_seq && (vp8->decoder.last_seq + 1) != rtp_hdr->seq_num) {
+ TSK_DEBUG_INFO("[VP8] Packet loss, seq_num=%d", (vp8->decoder.last_seq + 1));
+ vp8->decoder.corrupted = tsk_true;
+ }
+ vp8->decoder.last_seq = rtp_hdr->seq_num;
+
+ // New frame ?
+ if (vp8->decoder.last_timestamp != rtp_hdr->timestamp) {
+ /* 4.3. VP8 Payload Header
+ Note that the header is present only in packets
+ which have the S bit equal to one and the PartID equal to zero in the
+ payload descriptor. Subsequent packets for the same frame do not
+ carry the payload header.
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |Size0|H| VER |P|
+ +-+-+-+-+-+-+-+-+
+ | Size1 |
+ +-+-+-+-+-+-+-+-+
+ | Size2 |
+ +-+-+-+-+-+-+-+-+
+ | Bytes 4..N of |
+ | VP8 payload |
+ : :
+ +-+-+-+-+-+-+-+-+
+ | OPTIONAL RTP |
+ | padding |
+ : :
+ +-+-+-+-+-+-+-+-+
+ P: Inverse key frame flag. When set to 0 the current frame is a key
+ frame. When set to 1 the current frame is an interframe. Defined
+ in [RFC6386]
+ */
+
+ // Reset accumulator position
+ vp8->decoder.accumulator_pos = 0;
+
+ // Make sure the header is present
+ if (S != 1 || PartID != 0 || in_size < 3) {
+ TSK_DEBUG_WARN("VP8 payload header is missing");
#if 0
- if (in_size < 3)
+ if (in_size < 3)
#endif
- {
- fatal_error = tsk_true;
- goto bail;
- }
- }
- {
- /* SizeN: The size of the first partition in bytes is calculated from
- the 19 bits in Size0, Size1, and Size2 as 1stPartitionSize = Size0
- + 8 * Size1 + 2048 * Size2. [RFC6386]. */
- vp8->decoder.first_part_size = ((pdata[0] >> 5) & 0xFF) + 8 * pdata[1] + 2048 * pdata[2];
- }
-
- // Starting new frame...reset "corrupted" value
- vp8->decoder.corrupted = tsk_false;
-
- // Key frame?
- vp8->decoder.idr = !(pdata[0] & 0x01);
-
- // Update timestamp
- vp8->decoder.last_timestamp = rtp_hdr->timestamp;
- }
-
- if (in_size > xmax_size) {
- vp8->decoder.accumulator_pos = 0;
- TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", (unsigned)in_size, (unsigned)xmax_size);
- fatal_error = tsk_true;
- goto bail;
- }
- // start-accumulator
- if (!vp8->decoder.accumulator) {
- if (!(vp8->decoder.accumulator = tsk_calloc(in_size, sizeof(uint8_t)))) {
- TSK_DEBUG_ERROR("Failed to allocated new buffer");
- fatal_error = tsk_true;
- goto bail;
- }
- vp8->decoder.accumulator_size = in_size;
- }
- if ((vp8->decoder.accumulator_pos + in_size) >= xmax_size) {
- TSK_DEBUG_ERROR("BufferOverflow");
- vp8->decoder.accumulator_pos = 0;
- fatal_error = tsk_true;
- goto bail;
- }
- if ((vp8->decoder.accumulator_pos + in_size) > vp8->decoder.accumulator_size) {
- if (!(vp8->decoder.accumulator = tsk_realloc(vp8->decoder.accumulator, (vp8->decoder.accumulator_pos + in_size)))) {
- TSK_DEBUG_ERROR("Failed to reallocated new buffer");
- vp8->decoder.accumulator_pos = 0;
- vp8->decoder.accumulator_size = 0;
- fatal_error = tsk_true;
- goto bail;
- }
- vp8->decoder.accumulator_size = (vp8->decoder.accumulator_pos + in_size);
- }
-
- memcpy(&((uint8_t*)vp8->decoder.accumulator)[vp8->decoder.accumulator_pos], pdata, in_size);
- vp8->decoder.accumulator_pos += in_size;
- // end-accumulator
-
- // Decode the frame if we have a marker or the first partition is complete and not corrupted
- if (rtp_hdr->marker /*|| (!vp8->decoder.corrupted && vp8->decoder.first_part_size == vp8->decoder.accumulator_pos)*/) {
- vpx_image_t *img;
- vpx_codec_iter_t iter = tsk_null;
- vpx_codec_err_t vpx_ret;
- const uint8_t* pay_ptr = (const uint8_t*)vp8->decoder.accumulator;
- const tsk_size_t pay_size = vp8->decoder.accumulator_pos;
-
- // in all cases: reset accumulator position
- vp8->decoder.accumulator_pos = 0;
+ {
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ }
+ {
+ /* SizeN: The size of the first partition in bytes is calculated from
+ the 19 bits in Size0, Size1, and Size2 as 1stPartitionSize = Size0
+ + 8 * Size1 + 2048 * Size2. [RFC6386]. */
+ vp8->decoder.first_part_size = ((pdata[0] >> 5) & 0xFF) + 8 * pdata[1] + 2048 * pdata[2];
+ }
+
+ // Starting new frame...reset "corrupted" value
+ vp8->decoder.corrupted = tsk_false;
+
+ // Key frame?
+ vp8->decoder.idr = !(pdata[0] & 0x01);
+
+ // Update timestamp
+ vp8->decoder.last_timestamp = rtp_hdr->timestamp;
+ }
+
+ if (in_size > xmax_size) {
+ vp8->decoder.accumulator_pos = 0;
+ TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", (unsigned)in_size, (unsigned)xmax_size);
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ // start-accumulator
+ if (!vp8->decoder.accumulator) {
+ if (!(vp8->decoder.accumulator = tsk_calloc(in_size, sizeof(uint8_t)))) {
+ TSK_DEBUG_ERROR("Failed to allocated new buffer");
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ vp8->decoder.accumulator_size = in_size;
+ }
+ if ((vp8->decoder.accumulator_pos + in_size) >= xmax_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ vp8->decoder.accumulator_pos = 0;
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ if ((vp8->decoder.accumulator_pos + in_size) > vp8->decoder.accumulator_size) {
+ if (!(vp8->decoder.accumulator = tsk_realloc(vp8->decoder.accumulator, (vp8->decoder.accumulator_pos + in_size)))) {
+ TSK_DEBUG_ERROR("Failed to reallocated new buffer");
+ vp8->decoder.accumulator_pos = 0;
+ vp8->decoder.accumulator_size = 0;
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ vp8->decoder.accumulator_size = (vp8->decoder.accumulator_pos + in_size);
+ }
+
+ memcpy(&((uint8_t*)vp8->decoder.accumulator)[vp8->decoder.accumulator_pos], pdata, in_size);
+ vp8->decoder.accumulator_pos += in_size;
+ // end-accumulator
+
+ // Decode the frame if we have a marker or the first partition is complete and not corrupted
+ if (rtp_hdr->marker /*|| (!vp8->decoder.corrupted && vp8->decoder.first_part_size == vp8->decoder.accumulator_pos)*/) {
+ vpx_image_t *img;
+ vpx_codec_iter_t iter = tsk_null;
+ vpx_codec_err_t vpx_ret;
+ const uint8_t* pay_ptr = (const uint8_t*)vp8->decoder.accumulator;
+ const tsk_size_t pay_size = vp8->decoder.accumulator_pos;
+
+ // in all cases: reset accumulator position
+ vp8->decoder.accumulator_pos = 0;
#if 0 /* http://groups.google.com/a/webmproject.org/group/apps-devel/browse_thread/thread/c84438e70fe122fa/2dfc322018aa22a8 */
- // libvpx will crash very ofen when the frame is corrupted => for now we decided not to decode such frame
- // according to the latest release there is a function to check if the frame
- // is corrupted or not => To be checked
- if(vp8->decoder.corrupted) {
- vp8->decoder.corrupted = tsk_false;
- goto bail;
- }
+ // libvpx will crash very ofen when the frame is corrupted => for now we decided not to decode such frame
+ // according to the latest release there is a function to check if the frame
+ // is corrupted or not => To be checked
+ if(vp8->decoder.corrupted) {
+ vp8->decoder.corrupted = tsk_false;
+ goto bail;
+ }
#endif
- if (pay_size < vp8->decoder.first_part_size) {
- TSK_DEBUG_WARN("[VP8] No enough bytes for the first part: %u < %u", (unsigned)pay_size, (unsigned)vp8->decoder.first_part_size);
- // Not a fatal error
- goto bail;
- }
-
- vpx_ret = vpx_codec_decode(&vp8->decoder.context, pay_ptr, (int)pay_size, tsk_null, 0);
-
- if (vpx_ret != VPX_CODEC_OK) {
- TSK_DEBUG_INFO("vpx_codec_decode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- fatal_error = tsk_true;
- goto bail;
- }
- else if (vp8->decoder.idr) {
- TSK_DEBUG_INFO("Decoded VP8 IDR");
- if (TMEDIA_CODEC_VIDEO(self)->in.callback) {
- TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
- TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
- TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
- }
- }
-
- // copy decoded data
- ret = 0;
- while ((img = vpx_codec_get_frame(&vp8->decoder.context, &iter))) {
- unsigned int plane, y;
- tsk_size_t xsize;
-
- // update sizes
- TMEDIA_CODEC_VIDEO(vp8)->in.width = img->d_w;
- TMEDIA_CODEC_VIDEO(vp8)->in.height = img->d_h;
- xsize = (TMEDIA_CODEC_VIDEO(vp8)->in.width * TMEDIA_CODEC_VIDEO(vp8)->in.height * 3) >> 1;
- // allocate destination buffer
- if (*out_max_size < xsize) {
- if (!(*out_data = tsk_realloc(*out_data, xsize))) {
- TSK_DEBUG_ERROR("Failed to allocate new buffer");
- *out_max_size = 0;
- goto bail;
- }
- *out_max_size = xsize;
- }
-
- // layout picture
- for (plane = 0; plane < 3; plane++) {
- unsigned char *buf = img->planes[plane];
- for (y = 0; y < img->d_h >> (plane ? 1 : 0); y++) {
- unsigned int w_count = img->d_w >> (plane ? 1 : 0);
- if ((ret + w_count) > *out_max_size) {
- TSK_DEBUG_ERROR("BufferOverflow");
- ret = 0;
- goto bail;
- }
- memcpy(((uint8_t*)*out_data) + ret, buf, w_count);
- ret += w_count;
- buf += img->stride[plane];
- }
- }
- }
- }
+ if (pay_size < vp8->decoder.first_part_size) {
+ TSK_DEBUG_WARN("[VP8] No enough bytes for the first part: %u < %u", (unsigned)pay_size, (unsigned)vp8->decoder.first_part_size);
+ // Not a fatal error
+ goto bail;
+ }
+
+ vpx_ret = vpx_codec_decode(&vp8->decoder.context, pay_ptr, (int)pay_size, tsk_null, 0);
+
+ if (vpx_ret != VPX_CODEC_OK) {
+ TSK_DEBUG_INFO("vpx_codec_decode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ fatal_error = tsk_true;
+ goto bail;
+ }
+ else if (vp8->decoder.idr) {
+ TSK_DEBUG_INFO("Decoded VP8 IDR");
+ if (TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_idr;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+ }
+
+ // copy decoded data
+ ret = 0;
+ while ((img = vpx_codec_get_frame(&vp8->decoder.context, &iter))) {
+ unsigned int plane, y;
+ tsk_size_t xsize;
+
+ // update sizes
+ TMEDIA_CODEC_VIDEO(vp8)->in.width = img->d_w;
+ TMEDIA_CODEC_VIDEO(vp8)->in.height = img->d_h;
+ xsize = (TMEDIA_CODEC_VIDEO(vp8)->in.width * TMEDIA_CODEC_VIDEO(vp8)->in.height * 3) >> 1;
+ // allocate destination buffer
+ if (*out_max_size < xsize) {
+ if (!(*out_data = tsk_realloc(*out_data, xsize))) {
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ *out_max_size = 0;
+ goto bail;
+ }
+ *out_max_size = xsize;
+ }
+
+ // layout picture
+ for (plane = 0; plane < 3; plane++) {
+ unsigned char *buf = img->planes[plane];
+ for (y = 0; y < img->d_h >> (plane ? 1 : 0); y++) {
+ unsigned int w_count = img->d_w >> (plane ? 1 : 0);
+ if ((ret + w_count) > *out_max_size) {
+ TSK_DEBUG_ERROR("BufferOverflow");
+ ret = 0;
+ goto bail;
+ }
+ memcpy(((uint8_t*)*out_data) + ret, buf, w_count);
+ ret += w_count;
+ buf += img->stride[plane];
+ }
+ }
+ }
+ }
bail:
- if (fatal_error && TMEDIA_CODEC_VIDEO(self)->in.callback) {
- TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
- TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
- TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
- }
-
- // vp8->decoder.last_PartID = PartID;
- // vp8->decoder.last_S = S;
- // vp8->decoder.last_N = N;
- return ret;
+ if (fatal_error && TMEDIA_CODEC_VIDEO(self)->in.callback) {
+ TMEDIA_CODEC_VIDEO(self)->in.result.type = tmedia_video_decode_result_type_error;
+ TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
+ TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
+ }
+
+ // vp8->decoder.last_PartID = PartID;
+ // vp8->decoder.last_S = S;
+ // vp8->decoder.last_N = N;
+ return ret;
}
static tsk_bool_t tdav_codec_vp8_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
{
#if 0
- if(tsk_striequals(att_name, "fmtp")) {
- unsigned width, height, fps;
- if(tmedia_parse_video_fmtp(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &width, &height, &fps)) {
- TSK_DEBUG_ERROR("Failed to match fmtp=%s", att_value);
- return tsk_false;
- }
- TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = width;
- TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = height;
- TMEDIA_CODEC_VIDEO(codec)->in.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps = fps;
- }
- else
+ if(tsk_striequals(att_name, "fmtp")) {
+ unsigned width, height, fps;
+ if(tmedia_parse_video_fmtp(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &width, &height, &fps)) {
+ TSK_DEBUG_ERROR("Failed to match fmtp=%s", att_value);
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = TMEDIA_CODEC_VIDEO(codec)->out.width = width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = TMEDIA_CODEC_VIDEO(codec)->out.height = height;
+ TMEDIA_CODEC_VIDEO(codec)->in.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps = fps;
+ }
+ else
#endif
- if (tsk_striequals(att_name, "imageattr")) {
- unsigned in_width, in_height, out_width, out_height;
- if (tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0) {
- return tsk_false;
- }
- TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
- TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
- TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
- TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
- }
-
- return tsk_true;
+ if (tsk_striequals(att_name, "imageattr")) {
+ unsigned in_width, in_height, out_width, out_height;
+ if (tmedia_parse_video_imageattr(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &in_width, &in_height, &out_width, &out_height) != 0) {
+ return tsk_false;
+ }
+ TMEDIA_CODEC_VIDEO(codec)->in.width = in_width;
+ TMEDIA_CODEC_VIDEO(codec)->in.height = in_height;
+ TMEDIA_CODEC_VIDEO(codec)->out.width = out_width;
+ TMEDIA_CODEC_VIDEO(codec)->out.height = out_height;
+ // clamp the output size to the defined max range
+ if (tmedia_defaults_get_adapt_video_size_range_enabled()) {
+ if (tmedia_codec_video_clamp_out_size_to_range_max(TMEDIA_CODEC_VIDEO(codec)) != 0) {
+ return tsk_false;
+ }
+ }
+ }
+
+
+ return tsk_true;
}
static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
{
#if 0
- if(tsk_striequals(att_name, "fmtp")) {
- return tmedia_get_video_fmtp(TMEDIA_CODEC_VIDEO(codec)->pref_size);
- }
- else
+ if(tsk_striequals(att_name, "fmtp")) {
+ return tmedia_get_video_fmtp(TMEDIA_CODEC_VIDEO(codec)->pref_size);
+ }
+ else
#endif
- if (tsk_striequals(att_name, "imageattr")) {
- return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size,
- TMEDIA_CODEC_VIDEO(codec)->in.width, TMEDIA_CODEC_VIDEO(codec)->in.height, TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
- }
- return tsk_null;
+ if (tsk_striequals(att_name, "imageattr")) {
+ return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size,
+ TMEDIA_CODEC_VIDEO(codec)->in.width, TMEDIA_CODEC_VIDEO(codec)->in.height, TMEDIA_CODEC_VIDEO(codec)->out.width, TMEDIA_CODEC_VIDEO(codec)->out.height);
+ }
+ return tsk_null;
}
/* ============ VP8 object definition ================= */
@@ -626,62 +633,60 @@ static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char*
/* constructor */
static tsk_object_t* tdav_codec_vp8_ctor(tsk_object_t * self, va_list * app)
{
- tdav_codec_vp8_t *vp8 = self;
- if (vp8) {
- /* init base: called by tmedia_codec_create() */
- /* init self */
- }
- return self;
+ tdav_codec_vp8_t *vp8 = self;
+ if (vp8) {
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
}
/* destructor */
static tsk_object_t* tdav_codec_vp8_dtor(tsk_object_t * self)
{
- tdav_codec_vp8_t *vp8 = self;
- TSK_DEBUG_INFO("*** tdav_codec_vp8_dtor destroyed ***");
- if (vp8) {
- /* deinit base */
- tmedia_codec_video_deinit(vp8);
- /* deinit self */
- tdav_codec_vp8_close_encoder(vp8);
- tdav_codec_vp8_close_decoder(vp8);
- }
-
- return self;
+ tdav_codec_vp8_t *vp8 = self;
+ TSK_DEBUG_INFO("*** tdav_codec_vp8_dtor destroyed ***");
+ if (vp8) {
+ /* deinit base */
+ tmedia_codec_video_deinit(vp8);
+ /* deinit self */
+ tdav_codec_vp8_close_encoder(vp8);
+ tdav_codec_vp8_close_decoder(vp8);
+ }
+
+ return self;
}
/* object definition */
-static const tsk_object_def_t tdav_codec_vp8_def_s =
-{
- sizeof(tdav_codec_vp8_t),
- tdav_codec_vp8_ctor,
- tdav_codec_vp8_dtor,
- tmedia_codec_cmp,
+static const tsk_object_def_t tdav_codec_vp8_def_s = {
+ sizeof(tdav_codec_vp8_t),
+ tdav_codec_vp8_ctor,
+ tdav_codec_vp8_dtor,
+ tmedia_codec_cmp,
};
/* plugin definition*/
-static const tmedia_codec_plugin_def_t tdav_codec_vp8_plugin_def_s =
-{
- &tdav_codec_vp8_def_s,
-
- tmedia_video,
- tmedia_codec_id_vp8,
- "VP8",
- "VP8 codec (libvpx)",
- TMEDIA_CODEC_FORMAT_VP8,
- tsk_true,
- 90000, // rate
-
- /* audio */
- { 0 },
-
- /* video (defaul width,height,fps) */
- { 176, 144, 0 }, // fps is @deprecated
-
- tdav_codec_vp8_set,
- tdav_codec_vp8_open,
- tdav_codec_vp8_close,
- tdav_codec_vp8_encode,
- tdav_codec_vp8_decode,
- tdav_codec_vp8_sdp_att_match,
- tdav_codec_vp8_sdp_att_get
+static const tmedia_codec_plugin_def_t tdav_codec_vp8_plugin_def_s = {
+ &tdav_codec_vp8_def_s,
+
+ tmedia_video,
+ tmedia_codec_id_vp8,
+ "VP8",
+ "VP8 codec (libvpx)",
+ TMEDIA_CODEC_FORMAT_VP8,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video (defaul width,height,fps) */
+ { 176, 144, 0 }, // fps is @deprecated
+
+ tdav_codec_vp8_set,
+ tdav_codec_vp8_open,
+ tdav_codec_vp8_close,
+ tdav_codec_vp8_encode,
+ tdav_codec_vp8_decode,
+ tdav_codec_vp8_sdp_att_match,
+ tdav_codec_vp8_sdp_att_get
};
const tmedia_codec_plugin_def_t *tdav_codec_vp8_plugin_def_t = &tdav_codec_vp8_plugin_def_s;
@@ -689,193 +694,193 @@ const tmedia_codec_plugin_def_t *tdav_codec_vp8_plugin_def_t = &tdav_codec_vp8_p
int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
{
- vpx_codec_err_t vpx_ret;
- vpx_enc_frame_flags_t enc_flags = 0; // VPX_EFLAG_XXX
-
- if (self->encoder.initialized) {
- TSK_DEBUG_ERROR("VP8 encoder already inialized");
- return -1;
- }
-
- if ((vpx_ret = vpx_codec_enc_config_default(vp8_interface_enc, &self->encoder.cfg, 0)) != VPX_CODEC_OK) {
- TSK_DEBUG_ERROR("vpx_codec_enc_config_default failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- return -2;
- }
- self->encoder.cfg.g_timebase.num = 1;
- self->encoder.cfg.g_timebase.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
- self->encoder.cfg.rc_target_bitrate = TSK_CLAMP(
- 0,
- tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
- TMEDIA_CODEC(self)->bandwidth_max_upload
- );
- self->encoder.cfg.g_w = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
- self->encoder.cfg.g_h = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
- self->encoder.cfg.kf_mode = VPX_KF_AUTO;
- /*self->encoder.cfg.kf_min_dist =*/ self->encoder.cfg.kf_max_dist = (TDAV_VP8_GOP_SIZE_IN_SECONDS * TMEDIA_CODEC_VIDEO(self)->out.fps);
+ vpx_codec_err_t vpx_ret;
+ vpx_enc_frame_flags_t enc_flags = 0; // VPX_EFLAG_XXX
+
+ if (self->encoder.initialized) {
+ TSK_DEBUG_ERROR("VP8 encoder already inialized");
+ return -1;
+ }
+
+ if ((vpx_ret = vpx_codec_enc_config_default(vp8_interface_enc, &self->encoder.cfg, 0)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_config_default failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -2;
+ }
+ self->encoder.cfg.g_timebase.num = 1;
+ self->encoder.cfg.g_timebase.den = TMEDIA_CODEC_VIDEO(self)->out.fps;
+ self->encoder.cfg.rc_target_bitrate = TSK_CLAMP(
+ 0,
+ tmedia_get_video_bandwidth_kbps_2(TMEDIA_CODEC_VIDEO(self)->out.width, TMEDIA_CODEC_VIDEO(self)->out.height, TMEDIA_CODEC_VIDEO(self)->out.fps),
+ TMEDIA_CODEC(self)->bandwidth_max_upload
+ );
+ self->encoder.cfg.g_w = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.height : TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->encoder.cfg.g_h = (self->encoder.rotation == 90 || self->encoder.rotation == 270) ? TMEDIA_CODEC_VIDEO(self)->out.width : TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->encoder.cfg.kf_mode = VPX_KF_AUTO;
+ /*self->encoder.cfg.kf_min_dist =*/ self->encoder.cfg.kf_max_dist = (TDAV_VP8_GOP_SIZE_IN_SECONDS * TMEDIA_CODEC_VIDEO(self)->out.fps);
#if defined(VPX_ERROR_RESILIENT_DEFAULT)
- self->encoder.cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;
+ self->encoder.cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT;
#else
- self->encoder.cfg.g_error_resilient = 1;
+ self->encoder.cfg.g_error_resilient = 1;
#endif
#if defined(VPX_ERROR_RESILIENT_PARTITIONS)
- self->encoder.cfg.g_error_resilient |= VPX_ERROR_RESILIENT_PARTITIONS;
+ self->encoder.cfg.g_error_resilient |= VPX_ERROR_RESILIENT_PARTITIONS;
#endif
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
- enc_flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
+ enc_flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
#endif
- self->encoder.cfg.g_lag_in_frames = 0;
+ self->encoder.cfg.g_lag_in_frames = 0;
#if TDAV_UNDER_WINDOWS
- {
- SYSTEM_INFO SystemInfo;
- GetSystemInfo(&SystemInfo);
- self->encoder.cfg.g_threads = SystemInfo.dwNumberOfProcessors;
- }
+ {
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ self->encoder.cfg.g_threads = SystemInfo.dwNumberOfProcessors;
+ }
#endif
- self->encoder.cfg.rc_end_usage = VPX_CBR;
- self->encoder.cfg.g_pass = VPX_RC_ONE_PASS;
+ self->encoder.cfg.rc_end_usage = VPX_CBR;
+ self->encoder.cfg.g_pass = VPX_RC_ONE_PASS;
#if 0
- self->encoder.cfg.rc_dropframe_thresh = 30;
- self->encoder.cfg.rc_resize_allowed = 0;
- self->encoder.cfg.rc_min_quantizer = 2;
- self->encoder.cfg.rc_max_quantizer = 56;
- self->encoder.cfg.rc_undershoot_pct = 100;
- self->encoder.cfg.rc_overshoot_pct = 15;
- self->encoder.cfg.rc_buf_initial_sz = 500;
- self->encoder.cfg.rc_buf_optimal_sz = 600;
- self->encoder.cfg.rc_buf_sz = 1000;
+ self->encoder.cfg.rc_dropframe_thresh = 30;
+ self->encoder.cfg.rc_resize_allowed = 0;
+ self->encoder.cfg.rc_min_quantizer = 2;
+ self->encoder.cfg.rc_max_quantizer = 56;
+ self->encoder.cfg.rc_undershoot_pct = 100;
+ self->encoder.cfg.rc_overshoot_pct = 15;
+ self->encoder.cfg.rc_buf_initial_sz = 500;
+ self->encoder.cfg.rc_buf_optimal_sz = 600;
+ self->encoder.cfg.rc_buf_sz = 1000;
#endif
- if ((vpx_ret = vpx_codec_enc_init(&self->encoder.context, vp8_interface_enc, &self->encoder.cfg, enc_flags)) != VPX_CODEC_OK) {
- TSK_DEBUG_ERROR("vpx_codec_enc_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- return -3;
- }
- self->encoder.pic_id = /*(rand() ^ rand()) % 0x7FFF*/0/*Use zero: why do you want to make your life harder?*/;
+ if ((vpx_ret = vpx_codec_enc_init(&self->encoder.context, vp8_interface_enc, &self->encoder.cfg, enc_flags)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_enc_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -3;
+ }
+ self->encoder.pic_id = /*(rand() ^ rand()) % 0x7FFF*/0/*Use zero: why do you want to make your life harder?*/;
- /* vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 800); */
+ /* vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 800); */
#if !TDAV_UNDER_MOBILE /* must not remove: crash on Android for sure and probably on iOS also (all ARM devices ?) */
- vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 2);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 2);
#elif TDAV_UNDER_WINDOWS_CE
- vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 16);
- vpx_codec_control(&self->encoder.context, VP8E_SET_CPUUSED, 16);
- vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 16);
- vpx_codec_control(&self->encoder.context, VP8E_SET_SHARPNESS, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_NOISE_SENSITIVITY, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_CPUUSED, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_STATIC_THRESHOLD, 16);
+ vpx_codec_control(&self->encoder.context, VP8E_SET_SHARPNESS, 16);
#endif
- // Set number of partitions
+ // Set number of partitions
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
- {
- unsigned _s = TMEDIA_CODEC_VIDEO(self)->out.height * TMEDIA_CODEC_VIDEO(self)->out.width;
- if (_s < (352 * 288)) {
- vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
- }
- else if (_s < (352 * 288) * 2 * 2) {
- vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_TWO_TOKENPARTITION);
- }
- else if (_s < (352 * 288) * 4 * 4) {
- vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_FOUR_TOKENPARTITION);
- }
- else if (_s < (352 * 288) * 8 * 8) {
- vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_EIGHT_TOKENPARTITION);
- }
- }
+ {
+ unsigned _s = TMEDIA_CODEC_VIDEO(self)->out.height * TMEDIA_CODEC_VIDEO(self)->out.width;
+ if (_s < (352 * 288)) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 2 * 2) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_TWO_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 4 * 4) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_FOUR_TOKENPARTITION);
+ }
+ else if (_s < (352 * 288) * 8 * 8) {
+ vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_EIGHT_TOKENPARTITION);
+ }
+ }
#endif
- // Create the mutex if not already done
- if (!self->encoder.mutex && !(self->encoder.mutex = tsk_mutex_create())) {
- vpx_codec_destroy(&self->encoder.context);
- TSK_DEBUG_ERROR("Failed to create mutex");
- return -4;
- }
+ // Create the mutex if not already done
+ if (!self->encoder.mutex && !(self->encoder.mutex = tsk_mutex_create())) {
+ vpx_codec_destroy(&self->encoder.context);
+ TSK_DEBUG_ERROR("Failed to create mutex");
+ return -4;
+ }
- self->encoder.frame_count = 0;
+ self->encoder.frame_count = 0;
- self->encoder.initialized = tsk_true;
+ self->encoder.initialized = tsk_true;
- TSK_DEBUG_INFO("[VP8] target_bitrate=%d kbps", self->encoder.cfg.rc_target_bitrate);
+ TSK_DEBUG_INFO("[VP8] target_bitrate=%d kbps", self->encoder.cfg.rc_target_bitrate);
- return 0;
+ return 0;
}
int tdav_codec_vp8_open_decoder(tdav_codec_vp8_t* self)
{
- vpx_codec_err_t vpx_ret;
- vpx_codec_caps_t dec_caps;
- vpx_codec_flags_t dec_flags = 0;
+ vpx_codec_err_t vpx_ret;
+ vpx_codec_caps_t dec_caps;
+ vpx_codec_flags_t dec_flags = 0;
#if !TDAV_UNDER_MOBILE
- static vp8_postproc_cfg_t __pp = { VP8_DEBLOCK | VP8_DEMACROBLOCK, 4, 0 };
+ static vp8_postproc_cfg_t __pp = { VP8_DEBLOCK | VP8_DEMACROBLOCK, 4, 0 };
#endif
- if (self->decoder.initialized) {
- TSK_DEBUG_ERROR("VP8 decoder already initialized");
- return -1;
- }
+ if (self->decoder.initialized) {
+ TSK_DEBUG_ERROR("VP8 decoder already initialized");
+ return -1;
+ }
- self->decoder.cfg.w = TMEDIA_CODEC_VIDEO(self)->out.width;
- self->decoder.cfg.h = TMEDIA_CODEC_VIDEO(self)->out.height;
+ self->decoder.cfg.w = TMEDIA_CODEC_VIDEO(self)->out.width;
+ self->decoder.cfg.h = TMEDIA_CODEC_VIDEO(self)->out.height;
#if TDAV_UNDER_WINDOWS
- {
- SYSTEM_INFO SystemInfo;
- GetSystemInfo(&SystemInfo);
- self->decoder.cfg.threads = SystemInfo.dwNumberOfProcessors;
- }
+ {
+ SYSTEM_INFO SystemInfo;
+ GetSystemInfo(&SystemInfo);
+ self->decoder.cfg.threads = SystemInfo.dwNumberOfProcessors;
+ }
#endif
- dec_caps = vpx_codec_get_caps(&vpx_codec_vp8_dx_algo);
+ dec_caps = vpx_codec_get_caps(&vpx_codec_vp8_dx_algo);
#if !TDAV_UNDER_MOBILE
- if (dec_caps & VPX_CODEC_CAP_POSTPROC) {
- dec_flags |= VPX_CODEC_USE_POSTPROC;
- }
+ if (dec_caps & VPX_CODEC_CAP_POSTPROC) {
+ dec_flags |= VPX_CODEC_USE_POSTPROC;
+ }
#endif
#if defined(VPX_CODEC_CAP_ERROR_CONCEALMENT)
- if (dec_caps & VPX_CODEC_CAP_ERROR_CONCEALMENT) {
- dec_flags |= VPX_CODEC_USE_ERROR_CONCEALMENT;
- }
+ if (dec_caps & VPX_CODEC_CAP_ERROR_CONCEALMENT) {
+ dec_flags |= VPX_CODEC_USE_ERROR_CONCEALMENT;
+ }
#endif
- if ((vpx_ret = vpx_codec_dec_init(&self->decoder.context, vp8_interface_dec, &self->decoder.cfg, dec_flags)) != VPX_CODEC_OK) {
- TSK_DEBUG_ERROR("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- return -4;
- }
+ if ((vpx_ret = vpx_codec_dec_init(&self->decoder.context, vp8_interface_dec, &self->decoder.cfg, dec_flags)) != VPX_CODEC_OK) {
+ TSK_DEBUG_ERROR("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ return -4;
+ }
#if !TDAV_UNDER_MOBILE
- if ((vpx_ret = vpx_codec_control(&self->decoder.context, VP8_SET_POSTPROC, &__pp))) {
- TSK_DEBUG_WARN("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
- }
+ if ((vpx_ret = vpx_codec_control(&self->decoder.context, VP8_SET_POSTPROC, &__pp))) {
+ TSK_DEBUG_WARN("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
+ }
#endif
- self->decoder.initialized = tsk_true;
+ self->decoder.initialized = tsk_true;
- return 0;
+ return 0;
}
int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self)
{
- TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(begin)");
- if (self->encoder.initialized) {
- vpx_codec_destroy(&self->encoder.context);
- self->encoder.initialized = tsk_false;
- }
- if (self->encoder.mutex) {
- tsk_mutex_destroy(&self->encoder.mutex);
- }
- TSK_FREE(self->encoder.rtp.ptr);
- self->encoder.rtp.size = 0;
- self->encoder.rotation = 0; // reset rotation
- TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(end)");
- return 0;
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(begin)");
+ if (self->encoder.initialized) {
+ vpx_codec_destroy(&self->encoder.context);
+ self->encoder.initialized = tsk_false;
+ }
+ if (self->encoder.mutex) {
+ tsk_mutex_destroy(&self->encoder.mutex);
+ }
+ TSK_FREE(self->encoder.rtp.ptr);
+ self->encoder.rtp.size = 0;
+ self->encoder.rotation = 0; // reset rotation
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(end)");
+ return 0;
}
int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self)
{
- TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)");
- if (self->decoder.initialized) {
- vpx_codec_destroy(&self->decoder.context);
- self->decoder.initialized = tsk_false;
- }
- TSK_FREE(self->decoder.accumulator);
- self->decoder.accumulator_size = 0;
- self->decoder.accumulator_pos = 0;
- TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(end)");
-
- return 0;
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)");
+ if (self->decoder.initialized) {
+ vpx_codec_destroy(&self->decoder.context);
+ self->decoder.initialized = tsk_false;
+ }
+ TSK_FREE(self->decoder.accumulator);
+ self->decoder.accumulator_size = 0;
+ self->decoder.accumulator_pos = 0;
+ TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(end)");
+
+ return 0;
}
/* ============ VP8 RTP packetizer/depacketizer ================= */
@@ -883,177 +888,177 @@ int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self)
static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_t *pkt)
{
- tsk_bool_t non_ref, is_keyframe, part_start;
- uint8_t *frame_ptr;
- uint32_t part_size, part_ID, pkt_size, index;
+ tsk_bool_t non_ref, is_keyframe, part_start;
+ uint8_t *frame_ptr;
+ uint32_t part_size, part_ID, pkt_size, index;
- if (!self || !pkt || !pkt->data.frame.buf || !pkt->data.frame.sz) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return;
- }
+ if (!self || !pkt || !pkt->data.frame.buf || !pkt->data.frame.sz) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return;
+ }
- index = 0;
- frame_ptr = pkt->data.frame.buf;
- pkt_size = (uint32_t)pkt->data.frame.sz;
- non_ref = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE);
- is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
+ index = 0;
+ frame_ptr = pkt->data.frame.buf;
+ pkt_size = (uint32_t)pkt->data.frame.sz;
+ non_ref = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE);
+ is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
- part_ID = pkt->data.frame.partition_id;
- part_start = tsk_true;
- part_size = pkt_size;
- while (index < part_size) {
- uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
- tdav_codec_vp8_rtp_callback(
- self,
- &frame_ptr[index],
- frag_size,
- part_ID,
- part_start,
- non_ref,
- ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0 && (index + frag_size) == part_size) // RTP marker?
- );
- part_start = tsk_false;
- index += frag_size;
- }
+ part_ID = pkt->data.frame.partition_id;
+ part_start = tsk_true;
+ part_size = pkt_size;
+ while (index < part_size) {
+ uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
+ tdav_codec_vp8_rtp_callback(
+ self,
+ &frame_ptr[index],
+ frag_size,
+ part_ID,
+ part_start,
+ non_ref,
+ ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0 && (index + frag_size) == part_size) // RTP marker?
+ );
+ part_start = tsk_false;
+ index += frag_size;
+ }
#else
- // first partition (contains modes and motion vectors)
- part_ID = 0; // The first VP8 partition(containing modes and motion vectors) MUST be labeled with PartID = 0
- part_start = tsk_true;
- part_size = (frame_ptr[2] << 16) | (frame_ptr[1] << 8) | frame_ptr[0];
- part_size = (part_size >> 5) & 0x7FFFF;
- if (part_size > pkt_size) {
- TSK_DEBUG_ERROR("part_size is > pkt_size(%u,%u)", part_size, pkt_size);
- return;
- }
-
- // first,first,....partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
- while (index<part_size) {
- uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
- tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], frag_size, part_ID, part_start, non_ref, tsk_false);
- part_start = tsk_false;
- index += frag_size;
- }
-
- // second,third,... partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
- // FIXME: low FEC
- part_start = tsk_true;
- while (index<pkt_size) {
- if (part_start) {
- /* PartID SHOULD be incremented for each subsequent partition,
- but MAY be kept at 0 for all packets. PartID MUST NOT be larger
- than 8.
- */
- part_ID++;
- }
- part_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (pkt_size - index));
-
- tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], part_size, part_ID, part_start, non_ref, (index + part_size)==pkt_size);
- index += part_size;
- /*
- If more than one packet in an encoded frame contains the
- same PartID, the S bit MUST NOT be set for any other packet than
- the first packet with that PartID.
- */
- part_start = tsk_false;
- }
+ // first partition (contains modes and motion vectors)
+ part_ID = 0; // The first VP8 partition(containing modes and motion vectors) MUST be labeled with PartID = 0
+ part_start = tsk_true;
+ part_size = (frame_ptr[2] << 16) | (frame_ptr[1] << 8) | frame_ptr[0];
+ part_size = (part_size >> 5) & 0x7FFFF;
+ if (part_size > pkt_size) {
+ TSK_DEBUG_ERROR("part_size is > pkt_size(%u,%u)", part_size, pkt_size);
+ return;
+ }
+
+ // first,first,....partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
+ while (index<part_size) {
+ uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
+ tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], frag_size, part_ID, part_start, non_ref, tsk_false);
+ part_start = tsk_false;
+ index += frag_size;
+ }
+
+ // second,third,... partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
+ // FIXME: low FEC
+ part_start = tsk_true;
+ while (index<pkt_size) {
+ if (part_start) {
+ /* PartID SHOULD be incremented for each subsequent partition,
+ but MAY be kept at 0 for all packets. PartID MUST NOT be larger
+ than 8.
+ */
+ part_ID++;
+ }
+ part_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (pkt_size - index));
+
+ tdav_codec_vp8_rtp_callback(self, &frame_ptr[index], part_size, part_ID, part_start, non_ref, (index + part_size)==pkt_size);
+ index += part_size;
+ /*
+ If more than one packet in an encoded frame contains the
+ same PartID, the S bit MUST NOT be set for any other packet than
+ the first packet with that PartID.
+ */
+ part_start = tsk_false;
+ }
#endif /* VPX_CODEC_USE_OUTPUT_PARTITION */
}
static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data, tsk_size_t size, uint32_t partID, tsk_bool_t part_start, tsk_bool_t non_ref, tsk_bool_t last)
{
- tsk_size_t paydesc_and_hdr_size = TDAV_VP8_PAY_DESC_SIZE;
- tsk_bool_t has_hdr;
- /* draft-ietf-payload-vp8-04 - 4.2. VP8 Payload Descriptor
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |X|R|N|S|PartID | (REQUIRED)
- +-+-+-+-+-+-+-+-+
- X: |I|L|T|K| RSV | (OPTIONAL)
- +-+-+-+-+-+-+-+-+
- I: |M| PictureID | (OPTIONAL)
- +-+-+-+-+-+-+-+-+
- L: | TL0PICIDX | (OPTIONAL)
- +-+-+-+-+-+-+-+-+
- T/K: |TID|Y| KEYIDX | (OPTIONAL)
- +-+-+-+-+-+-+-+-+
-
- draft-ietf-payload-vp8-04 - 4.3. VP8 Payload Header
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |Size0|H| VER |P|
- +-+-+-+-+-+-+-+-+
- | Size1 |
- +-+-+-+-+-+-+-+-+
- | Size2 |
- +-+-+-+-+-+-+-+-+
- | Bytes 4..N of |
- | VP8 payload |
- : :
- +-+-+-+-+-+-+-+-+
- | OPTIONAL RTP |
- | padding |
- : :
- +-+-+-+-+-+-+-+-+
- */
-
- /*
- Note that the header is present only in packets which have the S bit equal to one and the
- PartID equal to zero in the payload descriptor.
- */
- if ((has_hdr = (part_start && partID == 0))) {
- has_hdr = tsk_true;
- paydesc_and_hdr_size += 0; // encoded data already contains payload header?
- }
-
- if (!data || !size) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return;
- }
- if (self->encoder.rtp.size < (size + paydesc_and_hdr_size)) {
- if (!(self->encoder.rtp.ptr = tsk_realloc(self->encoder.rtp.ptr, (size + paydesc_and_hdr_size)))) {
- TSK_DEBUG_ERROR("Failed to allocate new buffer");
- return;
- }
- self->encoder.rtp.size = (size + paydesc_and_hdr_size);
- }
- memcpy((self->encoder.rtp.ptr + paydesc_and_hdr_size), data, size);
-
- /* VP8 Payload Descriptor */
- // |X|R|N|S|PartID|
- self->encoder.rtp.ptr[0] = (partID & 0x0F) // PartID
- | ((part_start << 4) & 0x10)// S
- | ((non_ref << 5) & 0x20) // N
- // R = 0
+ tsk_size_t paydesc_and_hdr_size = TDAV_VP8_PAY_DESC_SIZE;
+ tsk_bool_t has_hdr;
+ /* draft-ietf-payload-vp8-04 - 4.2. VP8 Payload Descriptor
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |X|R|N|S|PartID | (REQUIRED)
+ +-+-+-+-+-+-+-+-+
+ X: |I|L|T|K| RSV | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ I: |M| PictureID | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ L: | TL0PICIDX | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+ T/K: |TID|Y| KEYIDX | (OPTIONAL)
+ +-+-+-+-+-+-+-+-+
+
+ draft-ietf-payload-vp8-04 - 4.3. VP8 Payload Header
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |Size0|H| VER |P|
+ +-+-+-+-+-+-+-+-+
+ | Size1 |
+ +-+-+-+-+-+-+-+-+
+ | Size2 |
+ +-+-+-+-+-+-+-+-+
+ | Bytes 4..N of |
+ | VP8 payload |
+ : :
+ +-+-+-+-+-+-+-+-+
+ | OPTIONAL RTP |
+ | padding |
+ : :
+ +-+-+-+-+-+-+-+-+
+ */
+
+ /*
+ Note that the header is present only in packets which have the S bit equal to one and the
+ PartID equal to zero in the payload descriptor.
+ */
+ if ((has_hdr = (part_start && partID == 0))) {
+ has_hdr = tsk_true;
+ paydesc_and_hdr_size += 0; // encoded data already contains payload header?
+ }
+
+ if (!data || !size) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return;
+ }
+ if (self->encoder.rtp.size < (size + paydesc_and_hdr_size)) {
+ if (!(self->encoder.rtp.ptr = tsk_realloc(self->encoder.rtp.ptr, (size + paydesc_and_hdr_size)))) {
+ TSK_DEBUG_ERROR("Failed to allocate new buffer");
+ return;
+ }
+ self->encoder.rtp.size = (size + paydesc_and_hdr_size);
+ }
+ memcpy((self->encoder.rtp.ptr + paydesc_and_hdr_size), data, size);
+
+ /* VP8 Payload Descriptor */
+ // |X|R|N|S|PartID|
+ self->encoder.rtp.ptr[0] = (partID & 0x0F) // PartID
+ | ((part_start << 4) & 0x10)// S
+ | ((non_ref << 5) & 0x20) // N
+ // R = 0
#if TDAV_VP8_DISABLE_EXTENSION
- | (0x00) // X=0
+ | (0x00) // X=0
#else
- | (0x80) // X=1
+ | (0x80) // X=1
#endif
- ;
+ ;
#if !TDAV_VP8_DISABLE_EXTENSION
- // X: |I|L|T|K| RSV |
- self->encoder.rtp.ptr[1] = 0x80; // I = 1, L = 0, T = 0, K = 0, RSV = 0
- // I: |M| PictureID |
- self->encoder.rtp.ptr[2] = (0x80 | ((self->encoder.pic_id >> 8) & 0x7F)); // M = 1 (PictureID on 15 bits)
- self->encoder.rtp.ptr[3] = (self->encoder.pic_id & 0xFF);
+ // X: |I|L|T|K| RSV |
+ self->encoder.rtp.ptr[1] = 0x80; // I = 1, L = 0, T = 0, K = 0, RSV = 0
+ // I: |M| PictureID |
+ self->encoder.rtp.ptr[2] = (0x80 | ((self->encoder.pic_id >> 8) & 0x7F)); // M = 1 (PictureID on 15 bits)
+ self->encoder.rtp.ptr[3] = (self->encoder.pic_id & 0xFF);
#endif
- /* 4.2. VP8 Payload Header */
- //if(has_hdr) {
- // already part of the encoded stream
- //}
-
- // Send data over the network
- if (TMEDIA_CODEC_VIDEO(self)->out.callback) {
- TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->encoder.rtp.ptr;
- TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (size + TDAV_VP8_PAY_DESC_SIZE);
- TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1. / (double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
- TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = last;
- TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
- }
+ /* 4.2. VP8 Payload Header */
+ //if(has_hdr) {
+ // already part of the encoded stream
+ //}
+
+ // Send data over the network
+ if (TMEDIA_CODEC_VIDEO(self)->out.callback) {
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->encoder.rtp.ptr;
+ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (size + TDAV_VP8_PAY_DESC_SIZE);
+ TMEDIA_CODEC_VIDEO(self)->out.result.duration = (uint32_t)((1. / (double)TMEDIA_CODEC_VIDEO(self)->out.fps) * TMEDIA_CODEC(self)->plugin->rate);
+ TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = last;
+ TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
+ }
}
#endif /* HAVE_LIBVPX */
OpenPOWER on IntegriCloud