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:
parent
f7ca47efc6
commit
231fdb89ac
|
@ -1,7 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Doubango Telecom <http://www.doubango.org>
|
||||
*
|
||||
* Contact: Mamadou Diop <diopmamadou(at)doubango(dot)org>
|
||||
* Copyright (C) 2011-2015 Doubango Telecom <http://www.doubango.org>
|
||||
*
|
||||
* This file is part of Open Source Doubango Framework.
|
||||
*
|
||||
|
@ -77,35 +75,37 @@ typedef struct tdav_codec_vp8_s
|
|||
TMEDIA_DECLARE_CODEC_VIDEO;
|
||||
|
||||
// Encoder
|
||||
struct{
|
||||
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;
|
||||
unsigned pic_id : 15;
|
||||
uint64_t frame_count;
|
||||
tsk_bool_t force_idr;
|
||||
int rotation;
|
||||
|
||||
struct{
|
||||
struct {
|
||||
uint8_t* ptr;
|
||||
tsk_size_t size;
|
||||
} rtp;
|
||||
|
||||
tsk_mutex_handle_t* mutex;
|
||||
} encoder;
|
||||
|
||||
// decoder
|
||||
struct{
|
||||
struct {
|
||||
vpx_codec_dec_cfg_t cfg;
|
||||
unsigned initialized:1;
|
||||
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;
|
||||
tsk_size_t first_part_size;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
tsk_bool_t idr;
|
||||
tsk_bool_t corrupted;
|
||||
tsk_bool_t corrupted;
|
||||
} decoder;
|
||||
}
|
||||
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")) {
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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) {
|
||||
|
@ -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);
|
||||
}
|
||||
else {
|
||||
TMEDIA_CODEC(vp8)->bandwidth_max_upload= max_bw_new;
|
||||
TMEDIA_CODEC(vp8)->bandwidth_max_upload = max_bw_new;
|
||||
}
|
||||
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 (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;
|
||||
}
|
||||
|
@ -201,7 +205,7 @@ static int tdav_codec_vp8_open(tmedia_codec_t* self)
|
|||
tdav_codec_vp8_t* vp8 = (tdav_codec_vp8_t*)self;
|
||||
int ret;
|
||||
|
||||
if(!vp8){
|
||||
if (!vp8) {
|
||||
TSK_DEBUG_ERROR("Invalid parameter");
|
||||
return -1;
|
||||
}
|
||||
|
@ -210,12 +214,12 @@ static int tdav_codec_vp8_open(tmedia_codec_t* self)
|
|||
|
||||
|
||||
// Encoder
|
||||
if((ret = tdav_codec_vp8_open_encoder(vp8))){
|
||||
if ((ret = tdav_codec_vp8_open_encoder(vp8))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Decoder
|
||||
if((ret = tdav_codec_vp8_open_decoder(vp8))){
|
||||
if ((ret = tdav_codec_vp8_open_decoder(vp8))) {
|
||||
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;
|
||||
|
||||
if(!vp8){
|
||||
if (!vp8) {
|
||||
TSK_DEBUG_ERROR("Invalid parameter");
|
||||
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;
|
||||
const vpx_codec_cx_pkt_t *pkt;
|
||||
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");
|
||||
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");
|
||||
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)){
|
||||
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){
|
||||
if (vp8->encoder.force_idr) {
|
||||
flags |= VPX_EFLAG_FORCE_KF;
|
||||
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));
|
||||
vpx_img_free(&image);
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -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_end = (pdata + in_size);
|
||||
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)
|
||||
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");
|
||||
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 */
|
||||
uint8_t X, R, N, I, L, T, K;//FIXME: store
|
||||
|
||||
X = (*pdata & 0x80)>>7;
|
||||
R = (*pdata & 0x40)>>6;
|
||||
X = (*pdata & 0x80) >> 7;
|
||||
R = (*pdata & 0x40) >> 6;
|
||||
if (R) {
|
||||
TSK_DEBUG_ERROR("R<>0");
|
||||
return 0;
|
||||
}
|
||||
N = (*pdata & 0x20)>>5;
|
||||
S = (*pdata & 0x10)>>4;
|
||||
N = (*pdata & 0x20) >> 5;
|
||||
S = (*pdata & 0x10) >> 4;
|
||||
PartID = (*pdata & 0x0F);
|
||||
// skip "REQUIRED" header
|
||||
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);
|
||||
|
||||
// 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;
|
||||
// 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){
|
||||
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]
|
||||
*/
|
||||
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;
|
||||
// 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");
|
||||
fatal_error = tsk_true;
|
||||
goto bail;
|
||||
}
|
||||
// Make sure the header is present
|
||||
if (S != 1 || PartID != 0 || in_size < 3) {
|
||||
TSK_DEBUG_WARN("VP8 payload header is missing");
|
||||
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];
|
||||
}
|
||||
{
|
||||
/* 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;
|
||||
// Starting new frame...reset "corrupted" value
|
||||
vp8->decoder.corrupted = tsk_false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
fatal_error = tsk_true;
|
||||
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;
|
||||
fatal_error = tsk_true;
|
||||
goto bail;
|
||||
}
|
||||
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) {
|
||||
TSK_DEBUG_ERROR("BufferOverflow");
|
||||
vp8->decoder.accumulator_pos = 0;
|
||||
fatal_error = tsk_true;
|
||||
fatal_error = tsk_true;
|
||||
goto bail;
|
||||
}
|
||||
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");
|
||||
vp8->decoder.accumulator_pos = 0;
|
||||
vp8->decoder.accumulator_size = 0;
|
||||
fatal_error = tsk_true;
|
||||
fatal_error = tsk_true;
|
||||
goto bail;
|
||||
}
|
||||
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
|
||||
// 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){
|
||||
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;
|
||||
}
|
||||
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;
|
||||
fatal_error = tsk_true;
|
||||
goto bail;
|
||||
}
|
||||
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
|
||||
for (plane=0; plane < 3; plane++) {
|
||||
unsigned char *buf =img->planes[plane];
|
||||
for (y=0; y<img->d_h >> (plane ? 1 : 0); y++) {
|
||||
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");
|
||||
|
@ -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);
|
||||
ret += w_count;
|
||||
buf += img->stride[plane];
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
fatal_error = tsk_true;
|
||||
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);
|
||||
}
|
||||
fatal_error = tsk_true;
|
||||
|
||||
// vp8->decoder.last_PartID = PartID;
|
||||
// vp8->decoder.last_S = S;
|
||||
// vp8->decoder.last_N = N;
|
||||
// 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")){
|
||||
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)){
|
||||
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;
|
||||
}
|
||||
|
@ -578,16 +586,16 @@ static tsk_bool_t tdav_codec_vp8_sdp_att_match(const tmedia_codec_t* codec, cons
|
|||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
#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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
tdav_codec_vp8_t *vp8 = self;
|
||||
if(vp8){
|
||||
if (vp8) {
|
||||
/* init base: called by tmedia_codec_create() */
|
||||
/* init self */
|
||||
}
|
||||
|
@ -624,26 +632,12 @@ 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){
|
||||
if (vp8) {
|
||||
/* deinit base */
|
||||
tmedia_codec_video_deinit(vp8);
|
||||
/* deinit self */
|
||||
if(vp8->encoder.rtp.ptr){
|
||||
TSK_FREE(vp8->encoder.rtp.ptr);
|
||||
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;
|
||||
}
|
||||
tdav_codec_vp8_close_encoder(vp8);
|
||||
tdav_codec_vp8_close_decoder(vp8);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -673,7 +667,7 @@ static const tmedia_codec_plugin_def_t tdav_codec_vp8_plugin_def_s =
|
|||
{ 0 },
|
||||
|
||||
/* video (defaul width,height,fps) */
|
||||
{176, 144, 0}, // fps is @deprecated
|
||||
{ 176, 144, 0 }, // fps is @deprecated
|
||||
|
||||
tdav_codec_vp8_set,
|
||||
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_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");
|
||||
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));
|
||||
return -2;
|
||||
}
|
||||
|
@ -707,7 +701,7 @@ int tdav_codec_vp8_open_encoder(tdav_codec_vp8_t* self)
|
|||
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;
|
||||
|
@ -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;
|
||||
#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;
|
||||
#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;
|
||||
#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));
|
||||
return -3;
|
||||
}
|
||||
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); */
|
||||
#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
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
self->encoder.initialized = tsk_true;
|
||||
|
||||
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_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){
|
||||
if (self->decoder.initialized) {
|
||||
TSK_DEBUG_ERROR("VP8 decoder already initialized");
|
||||
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);
|
||||
#if !TDAV_UNDER_MOBILE
|
||||
if(dec_caps & VPX_CODEC_CAP_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){
|
||||
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){
|
||||
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;
|
||||
|
@ -840,7 +841,12 @@ int tdav_codec_vp8_close_encoder(tdav_codec_vp8_t* self)
|
|||
vpx_codec_destroy(&self->encoder.context);
|
||||
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)");
|
||||
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)
|
||||
{
|
||||
TSK_DEBUG_INFO("tdav_codec_vp8_close_decoder(begin)");
|
||||
if(self->decoder.initialized){
|
||||
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;
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
|
@ -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,
|
||||
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));
|
||||
|
@ -932,8 +941,8 @@ static void tdav_codec_vp8_encap(tdav_codec_vp8_t* self, const vpx_codec_cx_pkt_
|
|||
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.
|
||||
same PartID, the S bit MUST NOT be set for any other packet than
|
||||
the first packet with that PartID.
|
||||
*/
|
||||
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;
|
||||
/* 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)
|
||||
+-+-+-+-+-+-+-+-+
|
||||
+-+-+-+-+-+-+-+-+
|
||||
|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 |
|
||||
: :
|
||||
+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
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))){
|
||||
*/
|
||||
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){
|
||||
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)))){
|
||||
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;
|
||||
}
|
||||
|
@ -1006,7 +1015,7 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
|
|||
| ((non_ref << 5) & 0x20) // N
|
||||
// R = 0
|
||||
#if TDAV_VP8_DISABLE_EXTENSION
|
||||
| (0x00) // X=0
|
||||
| (0x00) // X=0
|
||||
#else
|
||||
| (0x80) // X=1
|
||||
#endif
|
||||
|
@ -1021,15 +1030,15 @@ static void tdav_codec_vp8_rtp_callback(tdav_codec_vp8_t *self, const void *data
|
|||
#endif
|
||||
|
||||
/* 4.2. VP8 Payload Header */
|
||||
//if(has_hdr){
|
||||
// already part of the encoded stream
|
||||
//if(has_hdr) {
|
||||
// already part of the encoded stream
|
||||
//}
|
||||
|
||||
// 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.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.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue