Example #1
0
tnet_ice_candidate_t* tnet_ice_candidate_create(tnet_ice_cand_type_t type_e, tnet_socket_t* socket, tsk_bool_t is_ice_jingle, tsk_bool_t is_rtp, tsk_bool_t is_video, const char* ufrag, const char* pwd, const char *foundation)
{
	tnet_ice_candidate_t* candidate;

	if(!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){
		TSK_DEBUG_ERROR("Failed to create candidate");
		return tsk_null;
	}
	
	candidate->type_e = type_e;
	candidate->socket = tsk_object_ref(socket);
	candidate->local_pref = 0xFFFF;
	candidate->is_ice_jingle = is_ice_jingle;
	candidate->is_rtp = is_rtp;
	candidate->is_video = is_video;
	candidate->comp_id = is_rtp ? TNET_ICE_CANDIDATE_COMPID_RTP : TNET_ICE_CANDIDATE_COMPID_RTCP;
	if(foundation){
		memcpy(candidate->foundation, foundation, TSK_MIN(tsk_strlen(foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF));
	}
	else{
		tnet_ice_utils_compute_foundation((char*)candidate->foundation, TSK_MIN(sizeof(candidate->foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF));
	}
	candidate->priority = tnet_ice_utils_get_priority(candidate->type_e, candidate->local_pref, candidate->is_rtp);
	if(candidate->socket){
		memcpy(candidate->connection_addr, candidate->socket->ip, sizeof(candidate->socket->ip));
		candidate->port = candidate->socket->port;
		candidate->transport_e = socket->type;
	}
	tnet_ice_candidate_set_credential(candidate, ufrag, pwd);
	
	return candidate;
}
Example #2
0
int tdav_codec_set_priority(tdav_codec_id_t codec_id, int priority)
{
	static int count = sizeof(__codecs)/sizeof(tdav_codec_decl_t);
	int i;
	
	if(priority < 0){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	for(i = 0; i<count; ++i){
		if(__codecs[i].id == codec_id){
			tdav_codec_decl_t codec_decl_1, codec_decl_2;
			priority = TSK_MIN(priority, count-1);
			codec_decl_1 = __codecs[priority];
			codec_decl_2 = __codecs[i];
			
			__codecs[i] = codec_decl_1;
			__codecs[priority] = codec_decl_2;

			// change priority if already registered and supported
			if(_tdav_codec_is_supported(codec_decl_2.id, *codec_decl_2.plugin) && tmedia_codec_plugin_is_registered(*codec_decl_2.plugin)){
				return tmedia_codec_plugin_register_2(*codec_decl_2.plugin, priority);
			}
			return 0;
		}
	}
	
	TSK_DEBUG_ERROR("cannot find codec with id=%d", codec_id);
	return -2;
}
Example #3
0
int tdav_codec_set_priority(tdav_codec_id_t codec_id, int priority)
{
    tsk_size_t i;

    if(priority < 0) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    for(i = 0; i < __codec_plugins_all_count && __codec_plugins_all[i]; ++i) {
        if(__codec_plugins_all[i]->codec_id == codec_id) {
            const struct tmedia_codec_plugin_def_s *codec_decl_1, *codec_decl_2;
            priority = TSK_MIN(priority, (int)__codec_plugins_all_count-1);
            codec_decl_1 = __codec_plugins_all[priority];
            codec_decl_2 = __codec_plugins_all[i];

            __codec_plugins_all[i] = codec_decl_1;
            __codec_plugins_all[priority] = codec_decl_2;

            // change priority if already registered and supported
            if(_tdav_codec_is_supported((tdav_codec_id_t)codec_decl_2->codec_id, codec_decl_2) && tmedia_codec_plugin_is_registered(codec_decl_2)) {
                return tmedia_codec_plugin_register_2(codec_decl_2, priority);
            }
            return 0;
        }
    }

    TSK_DEBUG_INFO("Cannot find codec with id=%d for priority setting", codec_id);
    return 0;
}
/** Initialize audio consumer */
int tdav_consumer_audio_init(tdav_consumer_audio_t* self)
{
	int ret;

	TSK_DEBUG_INFO("tdav_consumer_audio_init()");

	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	/* base */
	if((ret = tmedia_consumer_init(TMEDIA_CONSUMER(self)))){
		return ret;
	}

	/* self (should be update by prepare() by using the codec's info)*/
	TMEDIA_CONSUMER(self)->audio.bits_per_sample = TDAV_BITS_PER_SAMPLE_DEFAULT;
	TMEDIA_CONSUMER(self)->audio.ptime = TDAV_PTIME_DEFAULT;
	TMEDIA_CONSUMER(self)->audio.in.channels = TDAV_CHANNELS_DEFAULT;
	TMEDIA_CONSUMER(self)->audio.in.rate = TDAV_RATE_DEFAULT;
	TMEDIA_CONSUMER(self)->audio.gain = TSK_MIN(tmedia_defaults_get_audio_consumer_gain(), TDAV_AUDIO_GAIN_MAX);

	tsk_safeobj_init(self);

	return 0;
}
Example #5
0
static tsk_bool_t tdav_codec_opus_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value)
{
    tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec;

    if(!opus) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return tsk_false;
    }

    TSK_DEBUG_INFO("[OPUS] Trying to match [%s:%s]", att_name, att_value);

    if(tsk_striequals(att_name, "fmtp")) {
        int val_int;
        tsk_params_L_t* params;
        /* e.g. FIXME */
        if((params = tsk_params_fromstring(att_value, ";", tsk_true))) {
            tsk_bool_t ret = tsk_false;
            /* === maxplaybackrate ===*/
            if((val_int = tsk_params_get_param_value_as_int(params, "maxplaybackrate")) != -1) {
                if(!_tdav_codec_opus_rate_is_valid(val_int)) {
                    TSK_DEBUG_ERROR("[OPUS] %d not valid as maxplaybackrate value", val_int);
                    goto done;
                }
                TMEDIA_CODEC(opus)->out.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->out.rate, val_int);
                TMEDIA_CODEC_AUDIO(opus)->out.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->out.rate);
            }
            /* === sprop-maxcapturerate ===*/
            if((val_int = tsk_params_get_param_value_as_int(params, "sprop-maxcapturerate")) != -1) {
                if(!_tdav_codec_opus_rate_is_valid(val_int)) {
                    TSK_DEBUG_ERROR("[OPUS] %d not valid as sprop-maxcapturerate value", val_int);
                    goto done;
                }
                TMEDIA_CODEC(opus)->in.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->in.rate, val_int);
                TMEDIA_CODEC_AUDIO(opus)->in.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->in.rate);
            }
            ret = tsk_true;
done:
            TSK_OBJECT_SAFE_FREE(params);
            return ret;
        }
    }

    return tsk_true;
}
Example #6
0
int tnet_ice_candidate_set_rflx_addr(tnet_ice_candidate_t* self, const char* addr, tnet_port_t port)
{
	if(!self || !addr || !port){
		TSK_DEBUG_ERROR("Invalid argument");
		return -1;
	}
	memset(self->connection_addr, 0, sizeof(self->connection_addr));
	memcpy(self->connection_addr, addr, TSK_MIN(tsk_strlen(addr), sizeof(self->connection_addr)));
	self->port = port;
	return 0;
}
Example #7
0
int trtp_srtp_match_line(const char* crypto_line, int32_t* tag, int32_t* crypto_type, char* key, tsk_size_t key_size)
{
	char* v = strtok((char*)crypto_line, " :|;");
	int32_t k = 0;
	while(v){
		switch(k){
			case 0:
				{
					if(tag){
						*tag = atoi(v);
					}
					break;
				}
			case 1:
				{
					if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_80)){
						if(crypto_type){
							*crypto_type = HMAC_SHA1_80;
						}
					}
					else if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_32)){
						if(crypto_type){
							*crypto_type = HMAC_SHA1_32;
						}
					}
					else {
						return -0xFF; 
					}
					break;
				}
			case 2:
				{
					if(!tsk_striequals(v, "inline")){
						return -0xFF;
					}
					break;
				}
			case 3:
				{
					if(key && key_size){
						memset(key, 0, key_size);
						memcpy(key, v, TSK_MIN(key_size, tsk_strlen(v)));
					}
					return 0;
				}
		}
		++k;
		v = strtok(tsk_null, " :|;");
	}

	return -0xF0;
}
Example #8
0
int tnet_transport_get_public_ip_n_port(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_ip_t *ip, tnet_port_t *port)
{
	tsk_bool_t stun_ok = tsk_false;
	struct tnet_nat_ctx_s* natt_ctx;
	const tnet_transport_t *transport = handle;
	if(!transport){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	if (TNET_SOCKET_TYPE_IS_DGRAM(transport->type) && (natt_ctx = tsk_object_ref(transport->natt_ctx))) {
		tnet_stun_binding_id_t bind_id = kStunBindingInvalidId;
		// if the socket is already monitored by the transport we should pause because both the transport and
		// NAT binder will try to read from it
		
		// Pause the soket
		tnet_transport_pause_socket(transport, fd, tsk_true);
		// Performs STUN binding
		bind_id = tnet_nat_stun_bind(transport->natt_ctx, fd);
		// Resume the socket
		tnet_transport_pause_socket(transport, fd, tsk_false);
		
		if (bind_id != kStunBindingInvalidId) {
			char* public_ip = tsk_null;
			if(tnet_nat_stun_get_reflexive_address(transport->natt_ctx, bind_id, &public_ip, port) == 0){
				if(ip && public_ip){
					tsk_size_t ip_len = tsk_strlen(public_ip);
					memcpy(ip, public_ip, ip_len> sizeof(*ip)?sizeof(*ip):ip_len);
				}
				stun_ok = tsk_true;
			}
			TSK_FREE(public_ip);
			tnet_nat_stun_unbind(transport->natt_ctx, bind_id);
		}
		tsk_object_unref(natt_ctx);
	}

	if(!stun_ok){
		if(fd == TNET_INVALID_FD && transport->local_ip){
			memcpy(*ip, transport->local_ip, TSK_MIN(sizeof(tnet_ip_t), tsk_strlen(transport->local_ip)));
			*port = transport->bind_local_port;
			return 0;
		}
		else{
			return tnet_transport_get_ip_n_port(handle, fd, ip, port);
		}
	}
	
	return 0;
}
static void __handle_output_buffer(void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer) {
    OSStatus ret;
	void *data;
	tsk_size_t out_size = 0;
    tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)userdata;

    if (!consumer->started) {
        return;
    }
    
	if((data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer), &out_size))){
        // If we can get audio to play, then copy in the buffer
		memcpy(buffer->mAudioData, data, TSK_MIN(consumer->buffer_size, out_size));
		TSK_FREE(data);
	} else{
        // Put silence if there is no audio to play
        memset(buffer->mAudioData, 0, consumer->buffer_size);
	}
    
    // Re-enqueue the buffer
    ret = AudioQueueEnqueueBuffer(consumer->queue, buffer, 0, NULL);
}
Example #10
0
// @param str e.g. "1 1 udp 1 192.168.196.1 57806 typ host name video_rtcp network_name {0C0137CC-DB78-46B6-9B6C-7E097FFA79FE} username StFEVThMK2DHThkv password qkhKUDr4WqKRwZTo generation 0"
tnet_ice_candidate_t* tnet_ice_candidate_parse(const char* str)
{
	char *v, *copy;
	int32_t k;
	tnet_ice_candidate_t* candidate;

	if(tsk_strnullORempty(str)){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}	

	if(!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){
		TSK_DEBUG_ERROR("Failed to create candidate");
		return tsk_null;
	}

	k = 0;
	copy = tsk_strdup(str);
	v = strtok(copy, " ");

	while(v){
		switch(k){
			case 0:
				{
					memcpy(candidate->foundation, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->foundation)));
					break;
				}
			case 1:
				{
					candidate->comp_id = atoi(v);
					break;
				}
			case 2:
				{
					candidate->transport_str = tsk_strdup(v);
					break;
				}
			case 3:
				{
					candidate->priority = atoi(v);
					break;
				}
			case 4:
				{
					memcpy(candidate->connection_addr, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->connection_addr)));
					break;
				}
			case 5:
				{
					tnet_family_t family;
					candidate->port = atoi(v);
					family = tnet_get_family(candidate->connection_addr, candidate->port);
					candidate->transport_e = _tnet_ice_candidate_get_transport_type((family == AF_INET6), candidate->transport_str);
					break;
				}
			case 6:
				{
					v = strtok(tsk_null, " ");
					tsk_strupdate(&candidate->cand_type_str, v);
					candidate->type_e = _tnet_ice_candtype_get_transport_type(v);
					break;
				}
			default:
				{
					const char* name = v;
					const char* value = (v = strtok(tsk_null, " "));
					tsk_param_t* param = tsk_param_create(name, value);
					if(param){
						tsk_list_push_back_data(candidate->extension_att_list, (void**)&param);
					}
					break;
				}
		}

		++k;
		v = strtok(tsk_null, " ");
	}

	if(k < 6){
		TSK_DEBUG_ERROR("Failed to parse: %s", str);
		TSK_OBJECT_SAFE_FREE(candidate);
	}
	TSK_FREE(copy);

	return candidate;
}
Example #11
0
static int tdav_codec_h264_set(tmedia_codec_t* self, const tmedia_param_t* param)
{
	tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self;
	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:
					{
						h264->encoder.force_idr = tsk_true;
						break;
					}
				case tmedia_codec_action_bw_down:
					{
						h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality + 1), 31);
#if HAVE_FFMPEG
						if (h264->encoder.context) {
							h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
						}
#endif
						break;
					}
				case tmedia_codec_action_bw_up:
					{
						h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality - 1), 31);
#if HAVE_FFMPEG
						if (h264->encoder.context) {
							h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality;
						}
#endif
						break;
					}
			}
			return 0;
		}
		else if(tsk_striequals(param->key, "bw_kbps")){
			int32_t max_bw_userdefine = self->bandwidth_max_upload;
			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
				h264->encoder.max_bw_kpbs = TSK_MIN(max_bw_new, max_bw_userdefine);
			}
			else {
				h264->encoder.max_bw_kpbs = max_bw_new;
			}
			return 0;
		}
		else if(tsk_striequals(param->key, "bypass-encoding")){
			h264->encoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false;
			TSK_DEBUG_INFO("[H.264] bypass-encoding = %d", h264->encoder.passthrough);
			return 0;
		}
		else if(tsk_striequals(param->key, "bypass-decoding")){
			h264->decoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false;
			TSK_DEBUG_INFO("[H.264] bypass-decoding = %d", h264->decoder.passthrough);
			return 0;
		}
		else if(tsk_striequals(param->key, "rotation")){
			int32_t rotation = *((int32_t*)param->value);
			if(h264->encoder.rotation != rotation){
                h264->encoder.rotation = rotation;
				if (self->opened) {
					int ret;
                    if ((ret = tdav_codec_h264_close_encoder(h264, kResetRotationFalse))) {
                        return ret;
                    }
					if ((ret = tdav_codec_h264_open_encoder(h264))) {
						return ret;
					}
#if 0 // Not working
					if((ret = avcodec_close(h264->encoder.context))){
						TSK_DEBUG_ERROR("Failed to close [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
						return ret;
					}
					h264->encoder.context->width = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.height : TMEDIA_CODEC_VIDEO(h264)->out.width;
					h264->encoder.context->height = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.width : TMEDIA_CODEC_VIDEO(h264)->out.height;
					if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){
						TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc);
						return ret;
					}
					h264->encoder.force_idr = tsk_true;
#endif
				}
			}
			return 0;
		}
	}
	return -1;
}
void tdav_codec_h264_rtp_callback(struct tdav_codec_h264_common_s *self, const void *data, tsk_size_t size, tsk_bool_t marker)
{
	uint8_t* pdata = (uint8_t*)data;

	//TSK_DEBUG_INFO("%x %x %x %x -- %u", pdata[0], pdata[1], pdata[2], pdata[3], size);

	if(size>4 && pdata[0] == H264_START_CODE_PREFIX[0] && pdata[1] == H264_START_CODE_PREFIX[1]){
		if(pdata[2] == H264_START_CODE_PREFIX[3]){
			pdata += 3, size -= 3;
		}
		else if(pdata[2] == H264_START_CODE_PREFIX[2] && pdata[3] == H264_START_CODE_PREFIX[3]){
			pdata += 4, size -= 4;
		}
	}
	
	//TSK_DEBUG_INFO("==> SCP %2x %2x %2x %2x", pdata[0], pdata[1], pdata[2], pdata[3]);

	if(size < H264_RTP_PAYLOAD_SIZE){
		/* Can be packet in a Single Nal Unit */
		// Send data over the network
		if(TMEDIA_CODEC_VIDEO(self)->out.callback){
			TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = pdata;
			TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = size;
			TMEDIA_CODEC_VIDEO(self)->out.result.duration = (3003* (30/TMEDIA_CODEC_VIDEO(self)->out.fps));
			TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = marker;
			TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
		}
	}
	else if(size > H264_NAL_UNIT_TYPE_HEADER_SIZE){
		/* Should be Fragmented as FUA */
		uint8_t fua_hdr[H264_FUA_HEADER_SIZE]; /* "FU indicator" and "FU header" - 2bytes */
		fua_hdr[0] = pdata[0] & 0x60/* F=0 */, fua_hdr[0] |= fu_a;
		fua_hdr[1] = 0x80/* S=1,E=0,R=0 */, fua_hdr[1] |= pdata[0] & 0x1f; /* type */
		// discard header
		pdata += H264_NAL_UNIT_TYPE_HEADER_SIZE;
		size -= H264_NAL_UNIT_TYPE_HEADER_SIZE;

		while(size){
			tsk_size_t packet_size = TSK_MIN(H264_RTP_PAYLOAD_SIZE, size);

			if(self->rtp.size < (packet_size + H264_FUA_HEADER_SIZE)){
				if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (packet_size + H264_FUA_HEADER_SIZE)))){
					TSK_DEBUG_ERROR("Failed to allocate new buffer");
					return;
				}
				self->rtp.size = (packet_size + H264_FUA_HEADER_SIZE);
			}
			// set E bit
			if((size - packet_size) == 0){
				// Last packet
				fua_hdr[1] |= 0x40;
			}
			// copy FUA header
			memcpy(self->rtp.ptr, fua_hdr, H264_FUA_HEADER_SIZE);
			// reset "S" bit
			fua_hdr[1] &= 0x7F;
			// copy data
			memcpy((self->rtp.ptr + H264_FUA_HEADER_SIZE), pdata, packet_size);
			pdata += packet_size;
			size -= packet_size;

			// send data
			if(TMEDIA_CODEC_VIDEO(self)->out.callback){
				TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->rtp.ptr;
				TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (packet_size + H264_FUA_HEADER_SIZE);
				TMEDIA_CODEC_VIDEO(self)->out.result.duration = (3003* (30/TMEDIA_CODEC_VIDEO(self)->out.fps));
				TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = (size == 0);
				TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result);
			}
		}
	}
}
static void *__playback_thread(void *param)
{
	tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)param; 

	HRESULT hr;
	LPVOID lpvAudio1, lpvAudio2;
	DWORD dwBytesAudio1, dwBytesAudio2;

	void* data;
	int index;

	TSK_DEBUG_INFO("__playback_thread -- START");

	SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS);

	for(;;){
		DWORD dwEvent = WaitForMultipleObjects(sizeof(dsound->notifEvents)/sizeof(HANDLE), dsound->notifEvents, FALSE, INFINITE);

		if(!dsound->started){
			break;
		}
		else {
			tsk_size_t out_size = 0;
			data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(dsound), &out_size);
			index = (dwEvent == (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT-1)) ? 0 : (dwEvent + 1);
			
			// lock
			if((hr = IDirectSoundBuffer_Lock(dsound->secondaryBuffer, (index * dsound->bytes_per_notif), dsound->bytes_per_notif, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0)) != DS_OK){
				tdav_win32_print_error("IDirectSoundBuffer_Lock", hr);
				goto next;
			}

			if(data){
				// copy data to dsound buffers
				memcpy(lpvAudio1, data, TSK_MIN(dwBytesAudio1, out_size));
				if(lpvAudio2){
					memcpy(lpvAudio2, ((LPBYTE*)data) + dwBytesAudio1, dwBytesAudio2);
				}
			}
			else{
				// Put silence
				memset(lpvAudio1, 0, dwBytesAudio1);
				if(lpvAudio2){
					memset(lpvAudio2, 0, dwBytesAudio2);
				}
			}

			// unlock
			if((hr = IDirectSoundBuffer_Unlock(dsound->secondaryBuffer, lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2)) != DS_OK){
				tdav_win32_print_error("IDirectSoundBuffer_UnLock", hr);
				goto next;
			}
next:
			TSK_FREE(data);
		}
	}

	TSK_DEBUG_INFO("__playback_thread -- STOP");
	

	return tsk_null;
}