diff --git a/group.go b/group.go index ba1c75a..dedccc7 100644 --- a/group.go +++ b/group.go @@ -209,6 +209,9 @@ func (this *Tox) ConferenceDelete(groupNumber uint32) (int, error) { this.unlock() return 1, toxerrf("delete group chat failed:%d", cerr) } + if _, ok := this.cb_audios[groupNumber]; ok { + delete(this.cb_audios, groupNumber) + } this.unlock() if this.hooks.ConferenceDelete != nil { @@ -383,12 +386,15 @@ func (this *Tox) ConferenceGetNames(groupNumber uint32) []string { return vec } - for idx := uint32(0); idx < peerCount; idx++ { + for idx := uint32(0); idx < math.MaxUint32; idx++ { pname, err := this.ConferencePeerGetName(groupNumber, idx) if err != nil { - return vec[0:0] + break } vec[idx] = pname + if uint32(len(vec)) >= peerCount { + break + } } return vec @@ -400,6 +406,7 @@ func (this *Tox) ConferenceGetPeerPubkeys(groupNumber uint32) []string { for peerNumber := uint32(0); peerNumber < math.MaxUint32; peerNumber++ { pubkey, err := this.ConferencePeerGetPublicKey(groupNumber, peerNumber) if err != nil { + break } else { vec = append(vec, pubkey) } @@ -416,6 +423,7 @@ func (this *Tox) ConferenceGetPeers(groupNumber uint32) map[uint32]string { for peerNumber := uint32(0); peerNumber < math.MaxUint32; peerNumber++ { pubkey, err := this.ConferencePeerGetPublicKey(groupNumber, peerNumber) if err != nil { + break } else { vec[peerNumber] = pubkey } diff --git a/group_intern.c b/group_intern.c index 853dd02..356135e 100644 --- a/group_intern.c +++ b/group_intern.c @@ -59,28 +59,12 @@ typedef struct { void *log_user_data; } Messenger_Options_Fake; -typedef struct { - uint32_t nospam; - void (*handle_friendrequest)(void *, const uint8_t *, const uint8_t *, size_t, void *); - uint8_t handle_friendrequest_isset; - void *handle_friendrequest_object; - - int (*filter_function)(const uint8_t *, void *); - void *filter_function_userdata; - /* NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem. - * TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam.) - */ - #define MAX_RECEIVED_STORED 32 #define CRYPTO_PUBLIC_KEY_SIZE 32 #define CRYPTO_SHARED_KEY_SIZE 32 #define CRYPTO_SYMMETRIC_KEY_SIZE CRYPTO_SHARED_KEY_SIZE - uint8_t received_requests[MAX_RECEIVED_STORED][CRYPTO_PUBLIC_KEY_SIZE]; - uint16_t received_requests_index; -} Friend_Requests_Fake; - typedef struct { uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; IP_Port ip_port; @@ -115,6 +99,20 @@ typedef struct { uint8_t title[TOX_MAX_NAME_LENGTH]; uint8_t title_len; + + uint32_t message_number; + uint16_t lossy_message_number; + uint16_t peer_number; + + uint64_t last_sent_ping; + + int number_joined; /* friendcon_id of person that invited us to the chat. (-1 means none) */ + + void *object; + + void (*peer_on_join)(void *, uint32_t, uint32_t); + void (*peer_on_leave)(void *, uint32_t, uint32_t, void *); + void (*group_on_delete)(void *, uint32_t); } Group_c_Fake; typedef enum { @@ -143,7 +141,7 @@ struct Messenger { /*Friend_Connections*/void *fr_c; /*TCP_Server*/void *tcp_server; - Friend_Requests_Fake fr; + /*Friend_Requests **/void *fr; uint8_t name[TOX_MAX_NAME_LENGTH]; uint16_t name_length; diff --git a/group_intern_test.go b/group_intern_test.go new file mode 100644 index 0000000..1f517c7 --- /dev/null +++ b/group_intern_test.go @@ -0,0 +1,22 @@ +package tox + +import ( + "fmt" + "log" + "testing" +) + +func TestGid0(t *testing.T) { + mt := NewMiniTox() + t0 := mt.t + + go mt.Iterate() + + for i := 0; i < 5; i++ { + gn, err := t0.ConferenceNew() + log.Println("gn:", gn, err) + t0.ConferenceSetTitle(gn, fmt.Sprintf("group###%d", gn)) + id, err := t0.ConferenceGetIdentifier(gn) + log.Println("id:", id, err) + } +} diff --git a/group_test.go b/group_test.go index 1edec40..8b5a589 100644 --- a/group_test.go +++ b/group_test.go @@ -39,7 +39,7 @@ func TestIssue6(t *testing.T) { waitcond(func() bool { return _t2.IsConnected() > 0 }, 100) log.Println("both connected") - gid := _t1.AddAVGroupChat() + gid := _t1.AddAVGroupChat(nil) // ok, err := _t1.DelGroupChat(gid) // log.Println(ok, err) log.Println(gid) diff --git a/tox.go b/tox.go index 954e0ec..343dd9c 100644 --- a/tox.go +++ b/tox.go @@ -105,6 +105,8 @@ type Tox struct { cb_file_recv_chunks map[unsafe.Pointer]interface{} cb_file_chunk_requests map[unsafe.Pointer]interface{} + cb_audios map[uint32]interface{} // groupNumber => cb_audio_ftype + cb_iterate_data interface{} cb_conference_message_setted bool @@ -509,6 +511,8 @@ func NewTox(opt *ToxOptions) *Tox { tox.cb_file_recv_chunks = make(map[unsafe.Pointer]interface{}) tox.cb_file_chunk_requests = make(map[unsafe.Pointer]interface{}) + tox.cb_audios = make(map[uint32]interface{}) + return tox } @@ -729,6 +733,9 @@ func (this *Tox) FriendExists(friendNumber uint32) bool { } func (this *Tox) FriendSendMessage(friendNumber uint32, message string) (uint32, error) { + if len(message) == 0 { + return 0, nil + } this.lock() defer this.unlock() diff --git a/tox_test.go b/tox_test.go index eed85c0..370fa40 100644 --- a/tox_test.go +++ b/tox_test.go @@ -702,7 +702,7 @@ func TestGroup(t *testing.T) { t.Error(err) } case CONFERENCE_TYPE_AV: - _, err := t1.t.JoinAVGroupChat(friendNumber, data) + _, err := t1.t.JoinAVGroupChat(friendNumber, data, nil) if err != nil { t.Error(err) } @@ -784,7 +784,7 @@ func TestGroup(t *testing.T) { case CONFERENCE_TYPE_TEXT: t1.t.JoinGroupChat(friendNumber, data) case CONFERENCE_TYPE_AV: - t1.t.JoinAVGroupChat(friendNumber, data) + t1.t.JoinAVGroupChat(friendNumber, data, nil) } }, nil) diff --git a/toxav.go b/toxav.go index cceaf36..e0a8a84 100644 --- a/toxav.go +++ b/toxav.go @@ -9,55 +9,19 @@ package tox void callbackCallWrapperForC(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); -typedef void (*cb_call_ftype)(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, - bool video_enabled, void *user_data); -static void cb_call_wrapper_for_go(ToxAV *m, cb_call_ftype fn, void *userdata) -{ toxav_callback_call(m, fn, userdata); } - void callbackCallStateWrapperForC(ToxAV *toxAV, uint32_t friendNumber, uint32_t state, void* user_data); -typedef void (*cb_call_state_ftype)(ToxAV *toxAV, uint32_t friendNumber, uint32_t state, void *user_data); -static void cb_call_state_wrapper_for_go(ToxAV *m, cb_call_state_ftype fn, void *userdata) -{ toxav_callback_call_state(m, fn, userdata); } - void callbackAudioBitRateWrapperForC(ToxAV *toxAV, uint32_t friendNumber, uint32_t audioBitRate, void* user_data); -typedef void (*cb_audio_bit_rate_ftype)(ToxAV *toxAV, uint32_t friendNumber, uint32_t audioBitRate, void *user_data); -static void cb_audio_bit_rate_wrapper_for_go(ToxAV *m, cb_audio_bit_rate_ftype fn, void *userdata) -{ toxav_callback_audio_bit_rate(m, fn, userdata); } - void callbackVideoBitRateWrapperForC(ToxAV *toxAV, uint32_t friendNumber, uint32_t videoBitRate, void* user_data); -typedef void (*cb_video_bit_rate_ftype)(ToxAV *toxAV, uint32_t friendNumber, uint32_t videoBitRate, void *user_data); -static void cb_video_bit_rate_wrapper_for_go(ToxAV *m, cb_video_bit_rate_ftype fn, void *userdata) -{ toxav_callback_video_bit_rate(m, fn, userdata); } - void callbackAudioReceiveFrameWrapperForC(ToxAV *toxAV, uint32_t friendNumber, int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void* user_data); -typedef void (*cb_audio_receive_frame_ftype)(ToxAV *toxAV, uint32_t friendNumber, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void *user_data); -static void cb_audio_receive_frame_wrapper_for_go(ToxAV *m, cb_audio_receive_frame_ftype fn, void *userdata) -{ toxav_callback_audio_receive_frame(m, fn, userdata); } - void callbackVideoReceiveFrameWrapperForC(ToxAV *toxAV, uint32_t friendNumber, uint16_t width, uint16_t height, uint8_t *y, uint8_t *u, uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void* user_data); -typedef void (*cb_video_receive_frame_ftype)(ToxAV *toxAV, uint32_t friendNumber, uint16_t width, - uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, - int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); -static void cb_video_receive_frame_wrapper_for_go(ToxAV *m, cb_video_receive_frame_ftype fn, void *userdata) -{ toxav_callback_video_receive_frame(m, fn, userdata); } +void callbackAudioForC(Tox *tox, uint32_t groupnumber, uint32_t peernumber, int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata); extern void i420_to_rgb(int width, int height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int ystride, int ustride, int vstride, unsigned char *out); extern void rgb_to_i420(unsigned char* rgb, vpx_image_t *img); - -// fix nouse compile warning -static inline __attribute__((__unused__)) void fixnousetoxav() { - cb_call_wrapper_for_go(NULL, NULL, NULL); - cb_call_state_wrapper_for_go(NULL, NULL, NULL); - cb_audio_bit_rate_wrapper_for_go(NULL, NULL, NULL); - cb_video_bit_rate_wrapper_for_go(NULL, NULL, NULL); - cb_audio_receive_frame_wrapper_for_go(NULL, NULL, NULL); - cb_video_receive_frame_wrapper_for_go(NULL, NULL, NULL); -} - */ import "C" import ( @@ -72,6 +36,7 @@ type cb_audio_bit_rate_ftype func(this *ToxAV, friendNumber uint32, audioBitRate type cb_video_bit_rate_ftype func(this *ToxAV, friendNumber uint32, videoBitRate uint32, userData interface{}) type cb_audio_receive_frame_ftype func(this *ToxAV, friendNumber uint32, pcm []byte, sampleCount int, channels int, samplingRate int, userData interface{}) type cb_video_receive_frame_ftype func(this *ToxAV, friendNumber uint32, width uint16, height uint16, data []byte, userData interface{}) +type cb_audio_ftype func(this *Tox, groupNumber uint32, peerNumber uint32, pcm []byte, samples uint, channels uint8, sample_rate uint32, userData interface{}) type ToxAV struct { tox *Tox @@ -157,11 +122,8 @@ func (this *ToxAV) CallbackCall(cbfn cb_call_ftype, userData interface{}) { this.cb_call = cbfn this.cb_call_user_data = userData - var _cbfn = (C.cb_call_ftype)(C.callbackCallWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil - - C.cb_call_wrapper_for_go(this.toxav, _cbfn, _userData) + var _cbfn = (*C.toxav_call_cb)(C.callbackCallWrapperForC) + C.toxav_callback_call(this.toxav, _cbfn, nil) } func (this *ToxAV) Answer(friendNumber uint32, audioBitRate uint32, videoBitRate uint32) (bool, error) { @@ -186,11 +148,8 @@ func (this *ToxAV) CallbackCallState(cbfn cb_call_state_ftype, userData interfac this.cb_call_state = cbfn this.cb_call_state_user_data = userData - var _cbfn = (C.cb_call_state_ftype)(C.callbackCallStateWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil - - C.cb_call_state_wrapper_for_go(this.toxav, _cbfn, _userData) + var _cbfn = (*C.toxav_call_state_cb)(C.callbackCallStateWrapperForC) + C.toxav_callback_call_state(this.toxav, _cbfn, nil) } func (this *ToxAV) CallControl(friendNumber uint32, control int) (bool, error) { @@ -232,11 +191,8 @@ func (this *ToxAV) CallbackAudioBitRate(cbfn cb_audio_bit_rate_ftype, userData i this.cb_audio_bit_rate = cbfn this.cb_audio_bit_rate_user_data = userData - var _cbfn = (C.cb_audio_bit_rate_ftype)(C.callbackAudioBitRateWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil - - C.cb_audio_bit_rate_wrapper_for_go(this.toxav, _cbfn, _userData) + var _cbfn = (*C.toxav_audio_bit_rate_cb)(C.callbackAudioBitRateWrapperForC) + C.toxav_callback_audio_bit_rate(this.toxav, _cbfn, nil) } //export callbackVideoBitRateWrapperForC @@ -251,11 +207,8 @@ func (this *ToxAV) CallbackVideoBitRate(cbfn cb_video_bit_rate_ftype, userData i this.cb_video_bit_rate = cbfn this.cb_video_bit_rate_user_data = userData - var _cbfn = (C.cb_video_bit_rate_ftype)(C.callbackVideoBitRateWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil - - C.cb_video_bit_rate_wrapper_for_go(this.toxav, _cbfn, _userData) + var _cbfn = (*C.toxav_video_bit_rate_cb)(C.callbackVideoBitRateWrapperForC) + C.toxav_callback_video_bit_rate(this.toxav, _cbfn, nil) } func (this *ToxAV) AudioSendFrame(friendNumber uint32, pcm []byte, sampleCount int, channels int, samplingRate int) (bool, error) { @@ -309,11 +262,9 @@ func (this *ToxAV) CallbackAudioReceiveFrame(cbfn cb_audio_receive_frame_ftype, this.cb_audio_receive_frame = cbfn this.cb_audio_receive_frame_user_data = userData - var _cbfn = (C.cb_audio_receive_frame_ftype)(C.callbackAudioReceiveFrameWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil + var _cbfn = (*C.toxav_audio_receive_frame_cb)(C.callbackAudioReceiveFrameWrapperForC) - C.cb_audio_receive_frame_wrapper_for_go(this.toxav, _cbfn, _userData) + C.toxav_callback_audio_receive_frame(this.toxav, _cbfn, nil) } //export callbackVideoReceiveFrameWrapperForC @@ -345,41 +296,55 @@ func (this *ToxAV) CallbackVideoReceiveFrame(cbfn cb_video_receive_frame_ftype, this.cb_video_receive_frame = cbfn this.cb_video_receive_frame_user_data = userData - var _cbfn = (C.cb_video_receive_frame_ftype)(C.callbackVideoReceiveFrameWrapperForC) - var _userData = unsafe.Pointer(this) - _userData = nil + var _cbfn = (*C.toxav_video_receive_frame_cb)(C.callbackVideoReceiveFrameWrapperForC) - C.cb_video_receive_frame_wrapper_for_go(this.toxav, _cbfn, _userData) + C.toxav_callback_video_receive_frame(this.toxav, _cbfn, nil) } -// TODO -// toxav_add_av_groupchat -// toxav_join_av_groupchat -// toxav_group_send_audio +//export callbackAudioForC +func callbackAudioForC(m *C.Tox, groupnumber C.uint32_t, peernumber C.uint32_t, pcm *C.int16_t, samples C.uint, channels C.uint8_t, sample_rate C.uint32_t, userdata unsafe.Pointer) { + var this = cbUserDatas.get(m) + + if _cbfnx, ok := this.cb_audios[uint32(groupnumber)]; ok && _cbfnx != nil { + _cbfn := (_cbfnx).(cb_audio_ftype) + this.putcbevts(func() { + blen := C.int(samples) * C.int(channels) * 2 + pcm_ := C.GoBytes(unsafe.Pointer(pcm), blen) // TODO copy memory improve + _cbfn(this, uint32(groupnumber), uint32(peernumber), pcm_, uint(samples), uint8(channels), uint32(sample_rate), nil) + }) + } +} -func (this *Tox) AddAVGroupChat() int { - r := C.toxav_add_av_groupchat(this.toxcore, nil, nil) - return int(r) +func (this *Tox) AddAVGroupChat(cbfn cb_audio_ftype) uint32 { + r := C.toxav_add_av_groupchat(this.toxcore, (*[0]byte)(unsafe.Pointer(C.callbackAudioForC)), nil) + if cbfn != nil { + this.cb_audios[uint32(r)] = cbfn + } + return uint32(r) } -func (this *Tox) JoinAVGroupChat(friendNumber uint32, cookie string) (int, error) { +func (this *Tox) JoinAVGroupChat(friendNumber uint32, cookie string, cbfn cb_audio_ftype) (uint32, error) { data, err := hex.DecodeString(cookie) if err != nil { return 0, errors.New("Invalid cookie:" + cookie) } + var _fn = C.uint32_t(friendNumber) var _data = (*C.uint8_t)((unsafe.Pointer)(&data[0])) var length = len(data) var _length = C.uint16_t(length) - // TODO nil => real - r := C.toxav_join_av_groupchat(this.toxcore, _fn, _data, _length, nil, nil) + r := C.toxav_join_av_groupchat(this.toxcore, _fn, _data, _length, + (*[0]byte)(unsafe.Pointer(C.callbackAudioForC)), nil) if int(r) == -1 { - return int(r), errors.New("Join av group chat failed") + return uint32(r), errors.New("Join av group chat failed") + } + if cbfn != nil { + this.cb_audios[uint32(r)] = cbfn } if this.hooks.ConferenceJoin != nil { this.hooks.ConferenceJoin(friendNumber, uint32(r), cookie) } - return int(r), nil + return uint32(r), nil } diff --git a/utils.go b/utils.go index 32a335e..5497da6 100644 --- a/utils.go +++ b/utils.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "reflect" "unsafe" ) @@ -43,6 +44,8 @@ func FileExist(fname string) bool { return true } +// the go-toxcore-c has data lost problem +// we need first write tmp file, and if ok, then mv to real file func (this *Tox) WriteSavedata(fname string) error { if !FileExist(fname) { err := ioutil.WriteFile(fname, this.GetSavedata(), 0755) @@ -56,10 +59,23 @@ func (this *Tox) WriteSavedata(fname string) error { } liveData := this.GetSavedata() if bytes.Compare(data, liveData) != 0 { - err := ioutil.WriteFile(fname, this.GetSavedata(), 0755) + tfp, err := ioutil.TempFile(filepath.Dir(fname), "gotcb") if err != nil { return err } + if _, err := tfp.Write(liveData); err != nil { + return err + } + tfname := tfp.Name() + if err := tfp.Close(); err != nil { + return err + } + if err := os.Remove(fname); err != nil { + return err + } + if err := os.Rename(filepath.Dir(fname)+"/"+tfname, fname); err != nil { + return err + } } }