Make VP8 encoder thread-safe (fix video corruption issue when the configuration is updated while we're encoding a frame)

This commit is contained in:
bossiel 2015-05-26 23:47:58 +00:00
parent f7ca47efc6
commit 231fdb89ac
1 changed files with 335 additions and 326 deletions

View File

@ -1,7 +1,5 @@
/* /*
* Copyright (C) 2011 Doubango Telecom <http://www.doubango.org> * Copyright (C) 2011-2015 Doubango Telecom <http://www.doubango.org>
*
* Contact: Mamadou Diop <diopmamadou(at)doubango(dot)org>
* *
* This file is part of Open Source Doubango Framework. * This file is part of Open Source Doubango Framework.
* *
@ -77,35 +75,37 @@ typedef struct tdav_codec_vp8_s
TMEDIA_DECLARE_CODEC_VIDEO; TMEDIA_DECLARE_CODEC_VIDEO;
// Encoder // Encoder
struct{ struct {
vpx_codec_enc_cfg_t cfg; vpx_codec_enc_cfg_t cfg;
tsk_bool_t initialized; tsk_bool_t initialized;
vpx_codec_pts_t pts; vpx_codec_pts_t pts;
vpx_codec_ctx_t context; vpx_codec_ctx_t context;
unsigned pic_id:15; unsigned pic_id : 15;
uint64_t frame_count; uint64_t frame_count;
tsk_bool_t force_idr; tsk_bool_t force_idr;
int rotation; int rotation;
struct{ struct {
uint8_t* ptr; uint8_t* ptr;
tsk_size_t size; tsk_size_t size;
} rtp; } rtp;
tsk_mutex_handle_t* mutex;
} encoder; } encoder;
// decoder // decoder
struct{ struct {
vpx_codec_dec_cfg_t cfg; vpx_codec_dec_cfg_t cfg;
unsigned initialized:1; unsigned initialized : 1;
vpx_codec_ctx_t context; vpx_codec_ctx_t context;
void* accumulator; void* accumulator;
tsk_size_t accumulator_pos; tsk_size_t accumulator_pos;
tsk_size_t accumulator_size; tsk_size_t accumulator_size;
tsk_size_t first_part_size; tsk_size_t first_part_size;
uint16_t last_seq; uint16_t last_seq;
uint32_t last_timestamp; uint32_t last_timestamp;
tsk_bool_t idr; tsk_bool_t idr;
tsk_bool_t corrupted; tsk_bool_t corrupted;
} decoder; } decoder;
} }
tdav_codec_vp8_t; tdav_codec_vp8_t;
@ -133,28 +133,28 @@ static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
if (tsk_striequals(param->key, "action")) { if (tsk_striequals(param->key, "action")) {
tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value); tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
switch (action) { switch (action) {
case tmedia_codec_action_encode_idr: case tmedia_codec_action_encode_idr:
{ {
vp8->encoder.force_idr = tsk_true; vp8->encoder.force_idr = tsk_true;
return 0; return 0;
} }
case tmedia_codec_action_bw_down: 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); 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); TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
reconf = tsk_true; reconf = tsk_true;
break; break;
} }
case tmedia_codec_action_bw_up: 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); 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); TSK_DEBUG_INFO("New target bitrate = %d kbps", vp8->encoder.cfg.rc_target_bitrate);
reconf = tsk_true; reconf = tsk_true;
break; break;
} }
} }
} }
else if(tsk_striequals(param->key, "bw_kbps")) { // both up and down (from the SDP) 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_userdefine = tmedia_defaults_get_bandwidth_video_upload_max();
int32_t max_bw_new = *((int32_t*)param->value); int32_t max_bw_new = *((int32_t*)param->value);
if (max_bw_userdefine > 0) { if (max_bw_userdefine > 0) {
@ -162,7 +162,7 @@ static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
TMEDIA_CODEC(vp8)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine); TMEDIA_CODEC(vp8)->bandwidth_max_upload = TSK_MIN(max_bw_new, max_bw_userdefine);
} }
else { else {
TMEDIA_CODEC(vp8)->bandwidth_max_upload= max_bw_new; TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
} }
reconf = tsk_true; reconf = tsk_true;
} }
@ -186,9 +186,13 @@ static int tdav_codec_vp8_set(tmedia_codec_t* self, const tmedia_param_t* param)
if (reconf) { if (reconf) {
if (vp8->encoder.initialized) { 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) { 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_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 (vpx_ret == VPX_CODEC_OK) ? 0 : -2;
} }
@ -201,7 +205,7 @@ static int tdav_codec_vp8_open(tmedia_codec_t* self)
tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self; tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
int ret; int ret;
if(!vp8){ if (!vp8) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return -1; return -1;
} }
@ -210,12 +214,12 @@ static int tdav_codec_vp8_open(tmedia_codec_t* self)
// Encoder // Encoder
if((ret = tdav_codec_vp8_open_encoder(vp8))){ if ((ret = tdav_codec_vp8_open_encoder(vp8))) {
return ret; return ret;
} }
// Decoder // Decoder
if((ret = tdav_codec_vp8_open_decoder(vp8))){ if ((ret = tdav_codec_vp8_open_decoder(vp8))) {
return ret; return ret;
} }
@ -226,7 +230,7 @@ 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){ if (!vp8) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return -1; return -1;
} }
@ -244,57 +248,61 @@ static tsk_size_t tdav_codec_vp8_encode(tmedia_codec_t* self, const void* in_dat
vpx_codec_err_t vpx_ret = VPX_CODEC_OK; vpx_codec_err_t vpx_ret = VPX_CODEC_OK;
const vpx_codec_cx_pkt_t *pkt; const vpx_codec_cx_pkt_t *pkt;
vpx_codec_iter_t iter = tsk_null; vpx_codec_iter_t iter = tsk_null;
vpx_image_t image; vpx_image_t image = {0};
if(!vp8 || !in_data || !in_size){ if (!vp8 || !in_data || !in_size) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return 0; return 0;
} }
if(in_size != (vp8->encoder.context.config.enc->g_w * vp8->encoder.context.config.enc->g_h * 3)>>1){ if (in_size != (vp8->encoder.context.config.enc->g_w * vp8->encoder.context.config.enc->g_h * 3) >> 1) {
TSK_DEBUG_ERROR("Invalid size"); TSK_DEBUG_ERROR("Invalid size");
return 0; return 0;
} }
// wrap yuv420 buffer // 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)){ 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"); TSK_DEBUG_ERROR("vpx_img_wrap failed");
return 0; return 0;
} }
// encode data // encode data
++vp8->encoder.pts; ++vp8->encoder.pts;
if(vp8->encoder.force_idr){ if (vp8->encoder.force_idr) {
flags |= VPX_EFLAG_FORCE_KF; flags |= VPX_EFLAG_FORCE_KF;
vp8->encoder.force_idr = tsk_false; vp8->encoder.force_idr = tsk_false;
} }
if((vpx_ret = vpx_codec_encode(&vp8->encoder.context, &image, vp8->encoder.pts, 1, flags, VPX_DL_REALTIME)) != VPX_CODEC_OK){ 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)); TSK_DEBUG_ERROR("vpx_codec_encode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
vpx_img_free(&image); goto bail;
return 0;
} }
++vp8->encoder.frame_count; ++vp8->encoder.frame_count;
++vp8->encoder.pic_id; ++vp8->encoder.pic_id;
while ((pkt = vpx_codec_get_cx_data(&vp8->encoder.context, &iter))) { while ((pkt = vpx_codec_get_cx_data(&vp8->encoder.context, &iter))) {
switch(pkt->kind){ switch (pkt->kind) {
case VPX_CODEC_CX_FRAME_PKT: case VPX_CODEC_CX_FRAME_PKT:
{ {
tdav_codec_vp8_encap(vp8, pkt); tdav_codec_vp8_encap(vp8, pkt);
break; break;
} }
default: default:
case VPX_CODEC_STATS_PKT: /**< Two-pass statistics for this frame */ case VPX_CODEC_STATS_PKT: /**< Two-pass statistics for this frame */
case VPX_CODEC_PSNR_PKT: /**< PSNR statistics for this frame */ case VPX_CODEC_PSNR_PKT: /**< PSNR statistics for this frame */
case VPX_CODEC_CUSTOM_PKT: /**< Algorithm extensions */ case VPX_CODEC_CUSTOM_PKT: /**< Algorithm extensions */
{ {
TSK_DEBUG_INFO("pkt->kind=%d not supported", (int)pkt->kind); TSK_DEBUG_INFO("pkt->kind=%d not supported", (int)pkt->kind);
break; break;
} }
} }
} }
bail:
vpx_img_free(&image); vpx_img_free(&image);
return 0; return 0;
} }
@ -306,11 +314,11 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
const uint8_t* pdata = in_data; const uint8_t* pdata = in_data;
const uint8_t* pdata_end = (pdata + in_size); const uint8_t* pdata_end = (pdata + in_size);
tsk_size_t ret = 0; tsk_size_t ret = 0;
tsk_bool_t fatal_error = tsk_false; 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) static const tsk_size_t xmax_size = (3840 * 2160 * 3) >> 3; // >>3 instead of >>1 (not an error)
uint8_t S, PartID; uint8_t S, PartID;
if (!self || !in_data || in_size<1 || !out_data || !vp8->decoder.initialized) { if (!self || !in_data || in_size < 1 || !out_data || !vp8->decoder.initialized) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return 0; return 0;
} }
@ -318,14 +326,14 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
{ /* 4.2. VP8 Payload Descriptor */ { /* 4.2. VP8 Payload Descriptor */
uint8_t X, R, N, I, L, T, K;//FIXME: store uint8_t X, R, N, I, L, T, K;//FIXME: store
X = (*pdata & 0x80)>>7; X = (*pdata & 0x80) >> 7;
R = (*pdata & 0x40)>>6; R = (*pdata & 0x40) >> 6;
if (R) { if (R) {
TSK_DEBUG_ERROR("R<>0"); TSK_DEBUG_ERROR("R<>0");
return 0; return 0;
} }
N = (*pdata & 0x20)>>5; N = (*pdata & 0x20) >> 5;
S = (*pdata & 0x10)>>4; S = (*pdata & 0x10) >> 4;
PartID = (*pdata & 0x0F); PartID = (*pdata & 0x0F);
// skip "REQUIRED" header // skip "REQUIRED" header
if (++pdata >= pdata_end) { if (++pdata >= pdata_end) {
@ -370,79 +378,79 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
in_size = (pdata_end - pdata); in_size = (pdata_end - pdata);
// Packet lost? // Packet lost?
if (vp8->decoder.last_seq && (vp8->decoder.last_seq + 1) != rtp_hdr->seq_num) { 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)); TSK_DEBUG_INFO("[VP8] Packet loss, seq_num=%d", (vp8->decoder.last_seq + 1));
vp8->decoder.corrupted = tsk_true; vp8->decoder.corrupted = tsk_true;
} }
vp8->decoder.last_seq = rtp_hdr->seq_num; vp8->decoder.last_seq = rtp_hdr->seq_num;
// New frame ? // New frame ?
if(vp8->decoder.last_timestamp != rtp_hdr->timestamp){ if (vp8->decoder.last_timestamp != rtp_hdr->timestamp) {
/* 4.3. VP8 Payload Header /* 4.3. VP8 Payload Header
Note that the header is present only in packets 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 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 payload descriptor. Subsequent packets for the same frame do not
carry the payload header. carry the payload header.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|Size0|H| VER |P| |Size0|H| VER |P|
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Size1 | | Size1 |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Size2 | | Size2 |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Bytes 4..N of | | Bytes 4..N of |
| VP8 payload | | VP8 payload |
: : : :
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| OPTIONAL RTP | | OPTIONAL RTP |
| padding | | padding |
: : : :
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
P: Inverse key frame flag. When set to 0 the current frame is a key 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 frame. When set to 1 the current frame is an interframe. Defined
in [RFC6386] in [RFC6386]
*/ */
// Reset accumulator position // Reset accumulator position
vp8->decoder.accumulator_pos = 0; vp8->decoder.accumulator_pos = 0;
// Make sure the header is present // Make sure the header is present
if (S != 1 || PartID != 0 || in_size < 3) { if (S != 1 || PartID != 0 || in_size < 3) {
TSK_DEBUG_WARN("VP8 payload header is missing"); TSK_DEBUG_WARN("VP8 payload header is missing");
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
{ {
/* SizeN: The size of the first partition in bytes is calculated from /* SizeN: The size of the first partition in bytes is calculated from
the 19 bits in Size0, Size1, and Size2 as 1stPartitionSize = Size0 the 19 bits in Size0, Size1, and Size2 as 1stPartitionSize = Size0
+ 8 * Size1 + 2048 * Size2. [RFC6386]. */ + 8 * Size1 + 2048 * Size2. [RFC6386]. */
vp8->decoder.first_part_size = ((pdata[0] >> 5) & 0xFF) + 8 * pdata[1] + 2048 * pdata[2]; vp8->decoder.first_part_size = ((pdata[0] >> 5) & 0xFF) + 8 * pdata[1] + 2048 * pdata[2];
} }
// Starting new frame...reset "corrupted" value // Starting new frame...reset "corrupted" value
vp8->decoder.corrupted = tsk_false; vp8->decoder.corrupted = tsk_false;
// Key frame? // Key frame?
vp8->decoder.idr = !(pdata[0] & 0x01); vp8->decoder.idr = !(pdata[0] & 0x01);
// Update timestamp // Update timestamp
vp8->decoder.last_timestamp = rtp_hdr->timestamp; vp8->decoder.last_timestamp = rtp_hdr->timestamp;
} }
if (in_size > xmax_size) { if (in_size > xmax_size) {
vp8->decoder.accumulator_pos = 0; 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); TSK_DEBUG_ERROR("%u too big to contain valid encoded data. xmax_size=%u", (unsigned)in_size, (unsigned)xmax_size);
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
// start-accumulator // start-accumulator
if (!vp8->decoder.accumulator) { if (!vp8->decoder.accumulator) {
if (!(vp8->decoder.accumulator = tsk_calloc(in_size, sizeof(uint8_t)))) { if (!(vp8->decoder.accumulator = tsk_calloc(in_size, sizeof(uint8_t)))) {
TSK_DEBUG_ERROR("Failed to allocated new buffer"); TSK_DEBUG_ERROR("Failed to allocated new buffer");
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
vp8->decoder.accumulator_size = in_size; vp8->decoder.accumulator_size = in_size;
@ -450,7 +458,7 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
if ((vp8->decoder.accumulator_pos + in_size) >= xmax_size) { if ((vp8->decoder.accumulator_pos + in_size) >= xmax_size) {
TSK_DEBUG_ERROR("BufferOverflow"); TSK_DEBUG_ERROR("BufferOverflow");
vp8->decoder.accumulator_pos = 0; vp8->decoder.accumulator_pos = 0;
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
if ((vp8->decoder.accumulator_pos + in_size) > vp8->decoder.accumulator_size) { if ((vp8->decoder.accumulator_pos + in_size) > vp8->decoder.accumulator_size) {
@ -458,7 +466,7 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
TSK_DEBUG_ERROR("Failed to reallocated new buffer"); TSK_DEBUG_ERROR("Failed to reallocated new buffer");
vp8->decoder.accumulator_pos = 0; vp8->decoder.accumulator_pos = 0;
vp8->decoder.accumulator_size = 0; vp8->decoder.accumulator_size = 0;
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
vp8->decoder.accumulator_size = (vp8->decoder.accumulator_pos + in_size); vp8->decoder.accumulator_size = (vp8->decoder.accumulator_pos + in_size);
@ -483,23 +491,23 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
// libvpx will crash very ofen when the frame is corrupted => for now we decided not to decode such frame // 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 // according to the latest release there is a function to check if the frame
// is corrupted or not => To be checked // is corrupted or not => To be checked
if(vp8->decoder.corrupted){ if(vp8->decoder.corrupted) {
vp8->decoder.corrupted = tsk_false; vp8->decoder.corrupted = tsk_false;
goto bail; goto bail;
} }
#endif #endif
if (pay_size < vp8->decoder.first_part_size) { 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); 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 // Not a fatal error
goto bail; goto bail;
} }
vpx_ret = vpx_codec_decode(&vp8->decoder.context, pay_ptr, (int)pay_size, tsk_null, 0); vpx_ret = vpx_codec_decode(&vp8->decoder.context, pay_ptr, (int)pay_size, tsk_null, 0);
if (vpx_ret != VPX_CODEC_OK) { if (vpx_ret != VPX_CODEC_OK) {
TSK_DEBUG_INFO("vpx_codec_decode failed with error =%s", vpx_codec_err_to_string(vpx_ret)); TSK_DEBUG_INFO("vpx_codec_decode failed with error =%s", vpx_codec_err_to_string(vpx_ret));
fatal_error = tsk_true; fatal_error = tsk_true;
goto bail; goto bail;
} }
else if (vp8->decoder.idr) { else if (vp8->decoder.idr) {
@ -532,9 +540,9 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
} }
// layout picture // layout picture
for (plane=0; plane < 3; plane++) { for (plane = 0; plane < 3; plane++) {
unsigned char *buf =img->planes[plane]; unsigned char *buf = img->planes[plane];
for (y=0; y<img->d_h >> (plane ? 1 : 0); y++) { for (y = 0; y < img->d_h >> (plane ? 1 : 0); y++) {
unsigned int w_count = img->d_w >> (plane ? 1 : 0); unsigned int w_count = img->d_w >> (plane ? 1 : 0);
if ((ret + w_count) > *out_max_size) { if ((ret + w_count) > *out_max_size) {
TSK_DEBUG_ERROR("BufferOverflow"); TSK_DEBUG_ERROR("BufferOverflow");
@ -543,32 +551,32 @@ static tsk_size_t tdav_codec_vp8_decode(tmedia_codec_t* self, const void* in_dat
} }
memcpy(((uint8_t*)*out_data) + ret, buf, w_count); memcpy(((uint8_t*)*out_data) + ret, buf, w_count);
ret += w_count; ret += w_count;
buf += img->stride[plane]; buf += img->stride[plane];
} }
} }
} }
} }
bail: bail:
if (fatal_error && TMEDIA_CODEC_VIDEO(self)->in.callback) { 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.type = tmedia_video_decode_result_type_error;
TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr; TMEDIA_CODEC_VIDEO(self)->in.result.proto_hdr = proto_hdr;
TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result); TMEDIA_CODEC_VIDEO(self)->in.callback(&TMEDIA_CODEC_VIDEO(self)->in.result);
} }
fatal_error = tsk_true; fatal_error = tsk_true;
// vp8->decoder.last_PartID = PartID; // vp8->decoder.last_PartID = PartID;
// vp8->decoder.last_S = S; // vp8->decoder.last_S = S;
// vp8->decoder.last_N = N; // vp8->decoder.last_N = N;
return ret; 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) 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 0
if(tsk_striequals(att_name, "fmtp")){ if(tsk_striequals(att_name, "fmtp")) {
unsigned width, height, fps; unsigned width, height, fps;
if(tmedia_parse_video_fmtp(att_value, TMEDIA_CODEC_VIDEO(codec)->pref_size, &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); TSK_DEBUG_ERROR("Failed to match fmtp=%s", att_value);
return tsk_false; return tsk_false;
} }
@ -578,16 +586,16 @@ static tsk_bool_t tdav_codec_vp8_sdp_att_match(const tmedia_codec_t* codec, cons
} }
else else
#endif #endif
if(tsk_striequals(att_name, "imageattr")){ if (tsk_striequals(att_name, "imageattr")) {
unsigned in_width, in_height, out_width, out_height; 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){ 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; 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;
} }
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; return tsk_true;
} }
@ -595,15 +603,15 @@ static tsk_bool_t tdav_codec_vp8_sdp_att_match(const tmedia_codec_t* codec, cons
static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char* att_name) static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char* att_name)
{ {
#if 0 #if 0
if(tsk_striequals(att_name, "fmtp")){ if(tsk_striequals(att_name, "fmtp")) {
return tmedia_get_video_fmtp(TMEDIA_CODEC_VIDEO(codec)->pref_size); return tmedia_get_video_fmtp(TMEDIA_CODEC_VIDEO(codec)->pref_size);
} }
else else
#endif #endif
if(tsk_striequals(att_name, "imageattr")){ if (tsk_striequals(att_name, "imageattr")) {
return tmedia_get_video_imageattr(TMEDIA_CODEC_VIDEO(codec)->pref_size, 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); 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; return tsk_null;
} }
@ -613,7 +621,7 @@ static char* tdav_codec_vp8_sdp_att_get(const tmedia_codec_t* codec, const char*
static tsk_object_t* tdav_codec_vp8_ctor(tsk_object_t * self, va_list * app) static tsk_object_t* tdav_codec_vp8_ctor(tsk_object_t * self, va_list * app)
{ {
tdav_codec_vp8_t *vp8 = self; tdav_codec_vp8_t *vp8 = self;
if(vp8){ if (vp8) {
/* init base: called by tmedia_codec_create() */ /* init base: called by tmedia_codec_create() */
/* init self */ /* init self */
} }
@ -624,26 +632,12 @@ static tsk_object_t* tdav_codec_vp8_dtor(tsk_object_t * self)
{ {
tdav_codec_vp8_t *vp8 = self; tdav_codec_vp8_t *vp8 = self;
TSK_DEBUG_INFO("*** tdav_codec_vp8_dtor destroyed ***"); TSK_DEBUG_INFO("*** tdav_codec_vp8_dtor destroyed ***");
if(vp8){ if (vp8) {
/* deinit base */ /* deinit base */
tmedia_codec_video_deinit(vp8); tmedia_codec_video_deinit(vp8);
/* deinit self */ /* deinit self */
if(vp8->encoder.rtp.ptr){ tdav_codec_vp8_close_encoder(vp8);
TSK_FREE(vp8->encoder.rtp.ptr); tdav_codec_vp8_close_decoder(vp8);
vp8->encoder.rtp.size = 0;
}
if(vp8->encoder.initialized){
vpx_codec_destroy(&vp8->encoder.context);
vp8->encoder.initialized = tsk_false;
}
if(vp8->decoder.initialized){
vpx_codec_destroy(&vp8->decoder.context);
vp8->decoder.initialized = tsk_false;
}
if(vp8->decoder.accumulator){
TSK_FREE(vp8->decoder.accumulator);
vp8->decoder.accumulator_pos = 0;
}
} }
return self; return self;
@ -673,7 +667,7 @@ static const tmedia_codec_plugin_def_t tdav_codec_vp8_plugin_def_s =
{ 0 }, { 0 },
/* video (defaul width,height,fps) */ /* video (defaul width,height,fps) */
{176, 144, 0}, // fps is @deprecated { 176, 144, 0 }, // fps is @deprecated
tdav_codec_vp8_set, tdav_codec_vp8_set,
tdav_codec_vp8_open, tdav_codec_vp8_open,
@ -692,12 +686,12 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
vpx_codec_err_t vpx_ret; vpx_codec_err_t vpx_ret;
vpx_enc_frame_flags_t enc_flags = 0; // VPX_EFLAG_XXX vpx_enc_frame_flags_t enc_flags = 0; // VPX_EFLAG_XXX
if(self->encoder.initialized){ if (self->encoder.initialized) {
TSK_DEBUG_ERROR("VP8 encoder already inialized"); TSK_DEBUG_ERROR("VP8 encoder already inialized");
return -1; return -1;
} }
if((vpx_ret = vpx_codec_enc_config_default(vp8_interface_enc, &self->encoder.cfg, 0)) != VPX_CODEC_OK){ 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)); TSK_DEBUG_ERROR("vpx_codec_enc_config_default failed with error =%s", vpx_codec_err_to_string(vpx_ret));
return -2; return -2;
} }
@ -707,7 +701,7 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
0, 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_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 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_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.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_mode = VPX_KF_AUTO;
@ -721,7 +715,7 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
self->encoder.cfg.g_error_resilient |= VPX_ERROR_RESILIENT_PARTITIONS; self->encoder.cfg.g_error_resilient |= VPX_ERROR_RESILIENT_PARTITIONS;
#endif #endif
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION) #if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
enc_flags |= VPX_CODEC_USE_OUTPUT_PARTITION; enc_flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
#endif #endif
self->encoder.cfg.g_lag_in_frames = 0; self->encoder.cfg.g_lag_in_frames = 0;
#if TDAV_UNDER_WINDOWS #if TDAV_UNDER_WINDOWS
@ -745,12 +739,11 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
self->encoder.cfg.rc_buf_sz = 1000; self->encoder.cfg.rc_buf_sz = 1000;
#endif #endif
if((vpx_ret = vpx_codec_enc_init(&self->encoder.context, vp8_interface_enc, &self->encoder.cfg, enc_flags)) != VPX_CODEC_OK){ 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)); TSK_DEBUG_ERROR("vpx_codec_enc_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
return -3; return -3;
} }
self->encoder.pic_id = /*(rand() ^ rand()) % 0x7FFF*/0/*Use zero: why do you want to make your life harder?*/; self->encoder.pic_id = /*(rand() ^ rand()) % 0x7FFF*/0/*Use zero: why do you want to make your life harder?*/;
self->encoder.initialized = tsk_true;
/* 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 ?) */ #if !TDAV_UNDER_MOBILE /* must not remove: crash on Android for sure and probably on iOS also (all ARM devices ?) */
@ -758,25 +751,33 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
#endif #endif
/* vpx_codec_control(&self->encoder.context, VP8E_SET_CPUUSED, 0); */ /* vpx_codec_control(&self->encoder.context, VP8E_SET_CPUUSED, 0); */
// Set number of partitions // Set number of partitions
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION) #if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
{ {
unsigned _s = TMEDIA_CODEC_VIDEO(self)->out.height * TMEDIA_CODEC_VIDEO(self)->out.width; unsigned _s = TMEDIA_CODEC_VIDEO(self)->out.height * TMEDIA_CODEC_VIDEO(self)->out.width;
if (_s < (352 * 288)) { if (_s < (352 * 288)) {
vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION); vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
} }
else if (_s < (352 * 288) * 2 * 2) { else if (_s < (352 * 288) * 2 * 2) {
vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_TWO_TOKENPARTITION); vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_TWO_TOKENPARTITION);
} }
else if (_s < (352 * 288) * 4 * 4) { else if (_s < (352 * 288) * 4 * 4) {
vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_FOUR_TOKENPARTITION); vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_FOUR_TOKENPARTITION);
} }
else if (_s < (352 * 288) * 8 * 8) { else if (_s < (352 * 288) * 8 * 8) {
vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_EIGHT_TOKENPARTITION); vpx_codec_control(&self->encoder.context, VP8E_SET_TOKEN_PARTITIONS, VP8_EIGHT_TOKENPARTITION);
} }
} }
#endif #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;
}
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);
@ -789,10 +790,10 @@ int tdav_codec_vp8_open_decoder(tdav_codec_vp8_t* self)
vpx_codec_caps_t dec_caps; vpx_codec_caps_t dec_caps;
vpx_codec_flags_t dec_flags = 0; vpx_codec_flags_t dec_flags = 0;
#if !TDAV_UNDER_MOBILE #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 #endif
if(self->decoder.initialized){ if (self->decoder.initialized) {
TSK_DEBUG_ERROR("VP8 decoder already initialized"); TSK_DEBUG_ERROR("VP8 decoder already initialized");
return -1; return -1;
} }
@ -809,23 +810,23 @@ int tdav_codec_vp8_open_decoder(tdav_codec_vp8_t* self)
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 !TDAV_UNDER_MOBILE
if(dec_caps & VPX_CODEC_CAP_POSTPROC){ if (dec_caps & VPX_CODEC_CAP_POSTPROC) {
dec_flags |= VPX_CODEC_USE_POSTPROC; dec_flags |= VPX_CODEC_USE_POSTPROC;
} }
#endif #endif
#if defined(VPX_CODEC_CAP_ERROR_CONCEALMENT) #if defined(VPX_CODEC_CAP_ERROR_CONCEALMENT)
if(dec_caps & VPX_CODEC_CAP_ERROR_CONCEALMENT){ if (dec_caps & VPX_CODEC_CAP_ERROR_CONCEALMENT) {
dec_flags |= VPX_CODEC_USE_ERROR_CONCEALMENT; dec_flags |= VPX_CODEC_USE_ERROR_CONCEALMENT;
} }
#endif #endif
if((vpx_ret = vpx_codec_dec_init(&self->decoder.context, vp8_interface_dec, &self->decoder.cfg, dec_flags)) != VPX_CODEC_OK){ 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)); TSK_DEBUG_ERROR("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
return -4; return -4;
} }
#if !TDAV_UNDER_MOBILE #if !TDAV_UNDER_MOBILE
if((vpx_ret = vpx_codec_control(&self->decoder.context, VP8_SET_POSTPROC, &__pp))){ 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)); TSK_DEBUG_WARN("vpx_codec_dec_init failed with error =%s", vpx_codec_err_to_string(vpx_ret));
} }
#endif #endif
self->decoder.initialized = tsk_true; self->decoder.initialized = tsk_true;
@ -840,7 +841,12 @@ int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self)
vpx_codec_destroy(&self->encoder.context); vpx_codec_destroy(&self->encoder.context);
self->encoder.initialized = tsk_false; self->encoder.initialized = tsk_false;
} }
self->encoder.rotation = 0; // reset rotation 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)"); TSK_DEBUG_INFO("tdav_codec_vp8_close_encoder(end)");
return 0; return 0;
} }
@ -848,10 +854,13 @@ int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self)
int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self) int tdav_codec_vp8_close_decoder(tdav_codec_vp8_t* self)
{ {
TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)"); TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)");
if(self->decoder.initialized){ if (self->decoder.initialized) {
vpx_codec_destroy(&self->decoder.context); vpx_codec_destroy(&self->decoder.context);
self->decoder.initialized = tsk_false; 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)"); TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(end)");
return 0; return 0;
@ -862,50 +871,50 @@ 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) 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; tsk_bool_t non_ref, is_keyframe, part_start;
uint8_t *frame_ptr; uint8_t *frame_ptr;
uint32_t part_size, part_ID, pkt_size, index; uint32_t part_size, part_ID, pkt_size, index;
if (!self || !pkt || !pkt->data.frame.buf || !pkt->data.frame.sz) { if (!self || !pkt || !pkt->data.frame.buf || !pkt->data.frame.sz) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return; return;
} }
index = 0; index = 0;
frame_ptr = pkt->data.frame.buf ; frame_ptr = pkt->data.frame.buf;
pkt_size = (uint32_t)pkt->data.frame.sz; pkt_size = (uint32_t)pkt->data.frame.sz;
non_ref = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE); non_ref = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE);
is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY); is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY);
#if defined(VPX_CODEC_USE_OUTPUT_PARTITION) #if defined(VPX_CODEC_USE_OUTPUT_PARTITION)
part_ID = pkt->data.frame.partition_id; part_ID = pkt->data.frame.partition_id;
part_start = tsk_true; part_start = tsk_true;
part_size = pkt_size; part_size = pkt_size;
while (index < part_size) { while (index < part_size) {
uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index)); uint32_t frag_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (part_size - index));
tdav_codec_vp8_rtp_callback( tdav_codec_vp8_rtp_callback(
self, self,
&frame_ptr[index], &frame_ptr[index],
frag_size, frag_size,
part_ID, part_ID,
part_start, part_start,
non_ref, non_ref,
((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0 && (index + frag_size) == part_size) // RTP marker? ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0 && (index + frag_size) == part_size) // RTP marker?
); );
part_start = tsk_false; part_start = tsk_false;
index += frag_size; index += frag_size;
} }
#else #else
// first partition (contains modes and motion vectors) // 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_ID = 0; // The first VP8 partition(containing modes and motion vectors) MUST be labeled with PartID = 0
part_start = tsk_true; part_start = tsk_true;
part_size = (frame_ptr[2] << 16) | (frame_ptr[1] << 8) | frame_ptr[0]; part_size = (frame_ptr[2] << 16) | (frame_ptr[1] << 8) | frame_ptr[0];
part_size = (part_size >> 5) & 0x7FFFF; part_size = (part_size >> 5) & 0x7FFFF;
if (part_size > pkt_size) { if (part_size > pkt_size) {
TSK_DEBUG_ERROR("part_size is > pkt_size(%u,%u)", part_size, pkt_size); TSK_DEBUG_ERROR("part_size is > pkt_size(%u,%u)", part_size, pkt_size);
return; return;
} }
// first,first,....partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE) // first,first,....partitions (or fragment if part_size > TDAV_VP8_RTP_PAYLOAD_MAX_SIZE)
while (index<part_size) { while (index<part_size) {
@ -923,7 +932,7 @@ static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_
/* PartID SHOULD be incremented for each subsequent partition, /* PartID SHOULD be incremented for each subsequent partition,
but MAY be kept at 0 for all packets. PartID MUST NOT be larger but MAY be kept at 0 for all packets. PartID MUST NOT be larger
than 8. than 8.
*/ */
part_ID++; part_ID++;
} }
part_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (pkt_size - index)); part_size = TSK_MIN(TDAV_VP8_RTP_PAYLOAD_MAX_SIZE, (pkt_size - index));
@ -932,8 +941,8 @@ static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_
index += part_size; index += part_size;
/* /*
If more than one packet in an encoded frame contains the 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 same PartID, the S bit MUST NOT be set for any other packet than
the first packet with that PartID. the first packet with that PartID.
*/ */
part_start = tsk_false; part_start = tsk_false;
} }
@ -946,52 +955,52 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
tsk_bool_t has_hdr; tsk_bool_t has_hdr;
/* draft-ietf-payload-vp8-04 - 4.2. VP8 Payload Descriptor /* draft-ietf-payload-vp8-04 - 4.2. VP8 Payload Descriptor
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|X|R|N|S|PartID | (REQUIRED) |X|R|N|S|PartID | (REQUIRED)
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
X: |I|L|T|K| RSV | (OPTIONAL) X: |I|L|T|K| RSV | (OPTIONAL)
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
I: |M| PictureID | (OPTIONAL) I: |M| PictureID | (OPTIONAL)
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
L: | TL0PICIDX | (OPTIONAL) L: | TL0PICIDX | (OPTIONAL)
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
T/K: |TID|Y| KEYIDX | (OPTIONAL) T/K: |TID|Y| KEYIDX | (OPTIONAL)
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
draft-ietf-payload-vp8-04 - 4.3. VP8 Payload Header draft-ietf-payload-vp8-04 - 4.3. VP8 Payload Header
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
|Size0|H| VER |P| |Size0|H| VER |P|
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Size1 | | Size1 |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Size2 | | Size2 |
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| Bytes 4..N of | | Bytes 4..N of |
| VP8 payload | | VP8 payload |
: : : :
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
| OPTIONAL RTP | | OPTIONAL RTP |
| padding | | padding |
: : : :
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
*/ */
/* /*
Note that the header is present only in packets which have the S bit equal to one and the 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. PartID equal to zero in the payload descriptor.
*/ */
if((has_hdr = (part_start && partID == 0))){ if ((has_hdr = (part_start && partID == 0))) {
has_hdr = tsk_true; has_hdr = tsk_true;
paydesc_and_hdr_size += 0; // encoded data already contains payload header? paydesc_and_hdr_size += 0; // encoded data already contains payload header?
} }
if(!data || !size){ if (!data || !size) {
TSK_DEBUG_ERROR("Invalid parameter"); TSK_DEBUG_ERROR("Invalid parameter");
return; return;
} }
if(self->encoder.rtp.size < (size + paydesc_and_hdr_size)){ 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)))){ if (!(self->encoder.rtp.ptr = tsk_realloc(self->encoder.rtp.ptr, (size + paydesc_and_hdr_size)))) {
TSK_DEBUG_ERROR("Failed to allocate new buffer"); TSK_DEBUG_ERROR("Failed to allocate new buffer");
return; return;
} }
@ -1006,7 +1015,7 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
| ((non_ref << 5) & 0x20) // N | ((non_ref << 5) & 0x20) // N
// R = 0 // R = 0
#if TDAV_VP8_DISABLE_EXTENSION #if TDAV_VP8_DISABLE_EXTENSION
| (0x00) // X=0 | (0x00) // X=0
#else #else
| (0x80) // X=1 | (0x80) // X=1
#endif #endif
@ -1021,15 +1030,15 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
#endif #endif
/* 4.2. VP8 Payload Header */ /* 4.2. VP8 Payload Header */
//if(has_hdr){ //if(has_hdr) {
// already part of the encoded stream // already part of the encoded stream
//} //}
// Send data over the network // Send data over the network
if(TMEDIA_CODEC_VIDEO(self)->out.callback){ 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.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.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.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.result.last_chunck = last;
TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result); TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
} }