- Timestamp:
- Jan 28, 2009 6:03:12 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/symbian_sound_aps.cpp
r2418 r2434 18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 19 */ 20 #include <pjmedia/symbian_sound_aps.h> 20 21 #include <pjmedia/sound.h> 21 22 #include <pjmedia/alaw_ulaw.h> … … 26 27 #include <pj/os.h> 27 28 29 #if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND 30 28 31 #include <e32msgqueue.h> 29 32 #include <sounddevice.h> … … 46 49 #endif 47 50 48 static pjmedia_snd_dev_info symbian_snd_dev_info = 51 static pjmedia_snd_dev_info symbian_snd_dev_info = 49 52 { 50 53 "Symbian Sound Device (APS)", … … 57 60 extern TPtrC APP_UID; 58 61 59 /* Default setting for loudspeaker */ 60 static pj_bool_t act_loudspeaker = PJ_FALSE; 62 /* Default setting */ 63 static pjmedia_snd_aps_setting def_setting; 64 65 /* APS G.711 frame length */ 66 static pj_uint8_t aps_g711_frame_len; 67 68 /* Pool factory */ 69 static pj_pool_factory *snd_pool_factory; 61 70 62 71 /* Forward declaration of CPjAudioEngine */ 63 72 class CPjAudioEngine; 64 73 65 /* 66 * PJMEDIA Sound Stream instance 74 /* 75 * PJMEDIA Sound Stream instance 67 76 */ 68 77 struct pjmedia_snd_stream 69 78 { 70 79 // Pool 71 pj_pool_t 80 pj_pool_t *pool; 72 81 73 82 // Common settings. 74 pjmedia_dir dir; 75 unsigned clock_rate; 76 unsigned channel_count; 77 unsigned samples_per_frame; 83 pjmedia_dir dir; 84 unsigned clock_rate; 85 unsigned channel_count; 86 unsigned samples_per_frame; 87 pjmedia_snd_rec_cb rec_cb; 88 pjmedia_snd_play_cb play_cb; 89 void *user_data; 78 90 79 91 // Audio engine 80 CPjAudioEngine *engine; 92 CPjAudioEngine *engine; 93 94 pj_timestamp ts_play; 95 pj_timestamp ts_rec; 96 97 pj_int16_t *play_buf; 98 pj_uint16_t play_buf_len; 99 pj_uint16_t play_buf_start; 100 pj_int16_t *rec_buf; 101 pj_uint16_t rec_buf_len; 81 102 }; 82 83 static pj_pool_factory *snd_pool_factory;84 103 85 104 … … 87 106 * Utility: print sound device error 88 107 */ 89 static void snd_perror(const char *title, TInt rc) 108 static void snd_perror(const char *title, TInt rc) 90 109 { 91 110 PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc)); 92 111 } 93 112 94 113 ////////////////////////////////////////////////////////////////////////////// 95 114 // 96 115 116 typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data); 117 97 118 /** 98 119 * Abstract class for handler of callbacks from APS client. … … 101 122 { 102 123 public: 124 MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_, 125 void *UserData_) 126 : RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_) 127 {} 128 103 129 virtual void InputStreamInitialized(const TInt aStatus) = 0; 104 130 virtual void OutputStreamInitialized(const TInt aStatus) = 0; 105 131 virtual void NotifyError(const TInt aError) = 0; 106 132 107 virtual void RecCb(TAPSCommBuffer &buffer) = 0; 108 virtual void PlayCb(TAPSCommBuffer &buffer) = 0; 133 public: 134 PjAudioCallback RecCb; 135 PjAudioCallback PlayCb; 136 void *UserData; 109 137 }; 110 138 … … 133 161 }; 134 162 135 static CQueueHandler* NewL(MQueueHandlerObserver* aObserver, 136 RMsgQueue<TAPSCommBuffer>* aQ, 163 static CQueueHandler* NewL(MQueueHandlerObserver* aObserver, 164 RMsgQueue<TAPSCommBuffer>* aQ, 165 RMsgQueue<TAPSCommBuffer>* aWriteQ, 137 166 TQueueHandlerType aType) 138 167 { 139 CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aType); 168 CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ, 169 aType); 140 170 CleanupStack::PushL(self); 141 171 self->ConstructL(); … … 155 185 private: 156 186 // Constructor 157 CQueueHandler(MQueueHandlerObserver* aObserver, 158 RMsgQueue<TAPSCommBuffer>* aQ, 159 TQueueHandlerType aType) 187 CQueueHandler(MQueueHandlerObserver* aObserver, 188 RMsgQueue<TAPSCommBuffer>* aQ, 189 RMsgQueue<TAPSCommBuffer>* aWriteQ, 190 TQueueHandlerType aType) 160 191 : CActive(CActive::EPriorityHigh), 161 iQ(aQ), i Observer(aObserver), iType(aType)192 iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType) 162 193 { 163 194 CActiveScheduler::Add(this); … … 178 209 iObserver->NotifyError(iStatus.Int()); 179 210 return; 180 } 211 } 181 212 182 213 TAPSCommBuffer buffer; … … 191 222 case ERecordQueue: 192 223 if (buffer.iCommand == EAPSRecordData) { 193 iObserver->RecCb(buffer); 224 iObserver->RecCb(buffer, iObserver->UserData); 225 } else { 226 iObserver->NotifyError(buffer.iStatus); 194 227 } 195 228 break; … … 200 233 case EAPSPlayData: 201 234 if (buffer.iStatus == KErrUnderflow) { 202 iObserver->PlayCb(buffer); 235 iObserver->PlayCb(buffer, iObserver->UserData); 236 iWriteQ->Send(buffer); 203 237 } 204 238 break; … … 225 259 // sent from the APS main thread through EPlayCommQueue 226 260 case EAPSRecorderInitialize: 227 if (buffer.iStatus == KErrNone) {228 iObserver->InputStreamInitialized(buffer.iStatus);229 break;230 }231 261 case EAPSRecordData: 262 default: 232 263 iObserver->NotifyError(buffer.iStatus); 233 break;234 default:235 264 break; 236 265 } … … 246 275 } 247 276 277 TInt RunError(TInt) { 278 return 0; 279 } 280 248 281 // Data 249 282 RMsgQueue<TAPSCommBuffer> *iQ; // (not owned) 283 RMsgQueue<TAPSCommBuffer> *iWriteQ; // (not owned) 250 284 MQueueHandlerObserver *iObserver; // (not owned) 251 285 TQueueHandlerType iType; 252 286 }; 253 287 288 /* 289 * Audio setting for CPjAudioEngine. 290 */ 291 class CPjAudioSetting 292 { 293 public: 294 TFourCC fourcc; 295 TAPSCodecMode mode; 296 TBool plc; 297 TBool vad; 298 TBool cng; 299 TBool loudspk; 300 }; 254 301 255 302 /* … … 269 316 270 317 static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm, 271 pjmedia_snd_rec_cb rec_cb, 272 pjmedia_snd_play_cb play_cb, 273 void *user_data); 318 PjAudioCallback rec_cb, 319 PjAudioCallback play_cb, 320 void *user_data, 321 const CPjAudioSetting &setting); 274 322 275 323 TInt StartL(); … … 280 328 private: 281 329 CPjAudioEngine(pjmedia_snd_stream *parent_strm, 282 pjmedia_snd_rec_cb rec_cb, 283 pjmedia_snd_play_cb play_cb, 284 void *user_data); 330 PjAudioCallback rec_cb, 331 PjAudioCallback play_cb, 332 void *user_data, 333 const CPjAudioSetting &setting); 285 334 void ConstructL(); 286 335 287 336 TInt InitPlayL(); 288 337 TInt InitRecL(); 289 338 TInt StartStreamL(); 290 339 291 340 // Inherited from MQueueHandlerObserver 292 341 virtual void InputStreamInitialized(const TInt aStatus); … … 294 343 virtual void NotifyError(const TInt aError); 295 344 296 virtual void RecCb(TAPSCommBuffer &buffer);297 virtual void PlayCb(TAPSCommBuffer &buffer);298 299 345 State state_; 300 346 pjmedia_snd_stream *parentStrm_; 301 pjmedia_snd_rec_cb recCb_; 302 pjmedia_snd_play_cb playCb_; 303 void *userData_; 304 pj_uint32_t TsPlay_; 305 pj_uint32_t TsRec_; 347 CPjAudioSetting setting_; 306 348 307 349 RAPSSession iSession; 308 TAPSInitSettings iSettings; 350 TAPSInitSettings iPlaySettings; 351 TAPSInitSettings iRecSettings; 352 309 353 RMsgQueue<TAPSCommBuffer> iReadQ; 310 354 RMsgQueue<TAPSCommBuffer> iReadCommQ; … … 315 359 CQueueHandler *iRecCommHandler; 316 360 CQueueHandler *iRecHandler; 317 318 static pj_uint8_t aps_samples_per_frame;319 320 pj_int16_t *play_buf;321 pj_uint16_t play_buf_len;322 pj_uint16_t play_buf_start;323 pj_int16_t *rec_buf;324 pj_uint16_t rec_buf_len;325 361 }; 326 362 327 363 328 pj_uint8_t CPjAudioEngine::aps_samples_per_frame = 0;329 330 331 364 CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm, 332 pjmedia_snd_rec_cb rec_cb, 333 pjmedia_snd_play_cb play_cb, 334 void *user_data) 365 PjAudioCallback rec_cb, 366 PjAudioCallback play_cb, 367 void *user_data, 368 const CPjAudioSetting &setting) 335 369 { 336 370 CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm, 337 371 rec_cb, play_cb, 338 user_data); 372 user_data, 373 setting); 339 374 CleanupStack::PushL(self); 340 375 self->ConstructL(); … … 344 379 345 380 CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm, 346 pjmedia_snd_rec_cb rec_cb, 347 pjmedia_snd_play_cb play_cb, 348 void *user_data) 349 : state_(STATE_NULL), 381 PjAudioCallback rec_cb, 382 PjAudioCallback play_cb, 383 void *user_data, 384 const CPjAudioSetting &setting) 385 : MQueueHandlerObserver(rec_cb, play_cb, user_data), 386 state_(STATE_NULL), 350 387 parentStrm_(parent_strm), 351 recCb_(rec_cb), 352 playCb_(play_cb), 353 userData_(user_data), 388 setting_(setting), 354 389 iPlayCommHandler(0), 355 390 iRecCommHandler(0), … … 362 397 Stop(); 363 398 399 delete iRecHandler; 364 400 delete iPlayCommHandler; 365 iPlayCommHandler = NULL;366 401 delete iRecCommHandler; 367 iRecCommHandler = NULL; 402 403 // On some devices, immediate closing after stopping may cause APS server 404 // panic KERN-EXEC 0, so let's wait for sometime before really closing 405 // the client session. 406 TTime start, now; 407 enum { APS_CLOSE_WAIT_TIME = 200 }; 408 start.UniversalTime(); 409 now.UniversalTime(); 410 while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000) { 411 pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME); 412 now.UniversalTime(); 413 } 368 414 369 415 iSession.Close(); … … 384 430 return 0; 385 431 386 TInt err = iSession.InitializePlayer(i Settings);432 TInt err = iSession.InitializePlayer(iPlaySettings); 387 433 if (err != KErrNone) { 388 434 snd_perror("Failed to initialize player", err); … … 391 437 392 438 // Open message queues for the output stream 393 TBuf<128> buf2 = i Settings.iGlobal;439 TBuf<128> buf2 = iPlaySettings.iGlobal; 394 440 buf2.Append(_L("PlayQueue")); 395 TBuf<128> buf3 = i Settings.iGlobal;441 TBuf<128> buf3 = iPlaySettings.iGlobal; 396 442 buf3.Append(_L("PlayCommQueue")); 397 443 … … 402 448 403 449 // Construct message queue handler 404 iPlayCommHandler = CQueueHandler::NewL(this, 405 &iWriteCommQ, 450 iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ, 406 451 CQueueHandler::EPlayCommQueue); 407 452 … … 418 463 419 464 // Initialize input stream device 420 TInt err = iSession.InitializeRecorder(i Settings);421 if (err != KErrNone ) {465 TInt err = iSession.InitializeRecorder(iRecSettings); 466 if (err != KErrNone && err != KErrAlreadyExists) { 422 467 snd_perror("Failed to initialize recorder", err); 423 468 return err; 424 469 } 425 470 426 TBuf<128> buf1 = i Settings.iGlobal;471 TBuf<128> buf1 = iRecSettings.iGlobal; 427 472 buf1.Append(_L("RecordQueue")); 428 TBuf<128> buf4 = i Settings.iGlobal;473 TBuf<128> buf4 = iRecSettings.iGlobal; 429 474 buf4.Append(_L("RecordCommQueue")); 430 475 … … 437 482 438 483 // Construct message queue handlers 439 iRecCommHandler = CQueueHandler::NewL(this, 440 &iReadCommQ, 484 iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL, 485 CQueueHandler::ERecordQueue); 486 iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL, 441 487 CQueueHandler::ERecordCommQueue); 442 488 443 489 // Start observing APS callbacks from on input stream message queue 490 iRecHandler->Start(); 444 491 iRecCommHandler->Start(); 445 492 446 493 return 0; 447 494 } … … 449 496 TInt CPjAudioEngine::StartL() 450 497 { 451 TInt err = iSession.Connect();452 if (err != KErrNone && err != KErrAlreadyExists)453 return err;454 455 498 if (state_ == STATE_READY) 456 499 return StartStreamL(); 457 500 458 // Even if only capturer are opened, playback thread of APS Server need 501 // Even if only capturer are opened, playback thread of APS Server need 459 502 // to be run(?). Since some messages will be delivered via play comm queue. 460 503 return InitPlayL(); … … 463 506 void CPjAudioEngine::Stop() 464 507 { 465 iSession.Stop(); 466 467 delete iRecHandler; 468 iRecHandler = NULL; 469 470 state_ = STATE_READY; 508 if (state_ == STATE_STREAMING) { 509 iSession.Stop(); 510 state_ = STATE_READY; 511 TRACE_((THIS_FILE, "Streaming stopped")); 512 } 471 513 } 472 514 473 515 void CPjAudioEngine::ConstructL() 474 516 { 475 iSettings.iFourCC = TFourCC(KMCPFourCCIdG711);476 i Settings.iGlobal = APP_UID;477 i Settings.iPriority = TMdaPriority(100);478 i Settings.iPreference = TMdaPriorityPreference(0x05210001);479 i Settings.iSettings.iChannels = EMMFMono;480 i Settings.iSettings.iSampleRate = EMMFSampleRate8000Hz;481 i Settings.iSettings.iVolume = 0;482 483 / * play_buf size is samples per frame of parent stream. */484 play_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,485 parentStrm_->samples_per_frame << 1);486 play_buf_len = 0;487 play_buf_start = 0;488 489 /* rec_buf size is samples per frame of parent stream. */490 rec_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool,491 parentStrm_->samples_per_frame << 1); 492 rec_buf_len = 0;517 // Recorder settings 518 iRecSettings.iFourCC = setting_.fourcc; 519 iRecSettings.iGlobal = APP_UID; 520 iRecSettings.iPriority = TMdaPriority(100); 521 iRecSettings.iPreference = TMdaPriorityPreference(0x05210001); 522 iRecSettings.iSettings.iChannels = EMMFMono; 523 iRecSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz; 524 525 // Player settings 526 iPlaySettings.iFourCC = setting_.fourcc; 527 iPlaySettings.iGlobal = APP_UID; 528 iPlaySettings.iPriority = TMdaPriority(100); 529 iPlaySettings.iPreference = TMdaPriorityPreference(0x05220001); 530 iPlaySettings.iSettings.iChannels = EMMFMono; 531 iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz; 532 iPlaySettings.iSettings.iVolume = 0; 533 534 User::LeaveIfError(iSession.Connect()); 493 535 } 494 536 … … 498 540 return 0; 499 541 500 iSession.SetCng(EFalse); 501 iSession.SetVadMode(EFalse); 502 iSession.SetPlc(EFalse); 503 iSession.SetEncoderMode(EULawOr30ms); 504 iSession.SetDecoderMode(EULawOr30ms); 505 iSession.ActivateLoudspeaker(act_loudspeaker); 506 507 // Not only playback 508 if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) { 509 iRecHandler = CQueueHandler::NewL(this, &iReadQ, 510 CQueueHandler::ERecordQueue); 511 iRecHandler->Start(); 512 iSession.Read(); 513 TRACE_((THIS_FILE, "APS recorder started")); 514 } 542 iSession.SetCng(setting_.cng); 543 iSession.SetVadMode(setting_.vad); 544 iSession.SetPlc(setting_.plc); 545 iSession.SetEncoderMode(setting_.mode); 546 iSession.SetDecoderMode(setting_.mode); 547 iSession.ActivateLoudspeaker(setting_.loudspk); 515 548 516 549 // Not only capture 517 550 if (parentStrm_->dir != PJMEDIA_DIR_CAPTURE) { 518 551 iSession.Write(); 519 TRACE_((THIS_FILE, "APS player started")); 552 TRACE_((THIS_FILE, "Player streaming started")); 553 } 554 555 // Not only playback 556 if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) { 557 iSession.Read(); 558 TRACE_((THIS_FILE, "Recorder streaming started")); 520 559 } 521 560 … … 557 596 } 558 597 559 void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer)560 {561 pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0);562 563 /* Detect the recorder G.711 frame size, player frame size will follow564 * this recorder frame size.565 */566 if (CPjAudioEngine::aps_samples_per_frame == 0) {567 CPjAudioEngine::aps_samples_per_frame = buffer.iBuffer.Length() < 160?568 80 : 160;569 TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",570 CPjAudioEngine::aps_samples_per_frame));571 }572 573 /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.574 * Whenever rec_buf is full, call parent stream callback.575 */576 unsigned dec_len = 0;577 578 while (dec_len < CPjAudioEngine::aps_samples_per_frame) {579 unsigned tmp;580 581 tmp = PJ_MIN(parentStrm_->samples_per_frame - rec_buf_len,582 CPjAudioEngine::aps_samples_per_frame - dec_len);583 pjmedia_ulaw_decode(&rec_buf[rec_buf_len],584 buffer.iBuffer.Ptr() + 2 + dec_len,585 tmp);586 rec_buf_len += tmp;587 dec_len += tmp;588 589 pj_assert(rec_buf_len <= parentStrm_->samples_per_frame);590 591 if (rec_buf_len == parentStrm_->samples_per_frame) {592 recCb_(userData_, 0, rec_buf, rec_buf_len << 1);593 rec_buf_len = 0;594 }595 }596 }597 598 void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer)599 {600 buffer.iCommand = CQueueHandler::EAPSPlayData;601 buffer.iStatus = 0;602 buffer.iBuffer.Zero();603 buffer.iBuffer.Append(1);604 buffer.iBuffer.Append(0);605 606 /* Send 10ms silence frame if frame size hasn't been known. */607 if (CPjAudioEngine::aps_samples_per_frame == 0) {608 pjmedia_zero_samples(play_buf, 80);609 pjmedia_ulaw_encode((pj_uint8_t*)play_buf, play_buf, 80);610 buffer.iBuffer.Append((TUint8*)play_buf, 80);611 iWriteQ.Send(buffer);612 return;613 }614 615 unsigned enc_len = 0;616 617 /* Call parent stream callback to get PCM samples to play,618 * encode the PCM samples into G.711 and put it into APS buffer.619 */620 while (enc_len < CPjAudioEngine::aps_samples_per_frame) {621 if (play_buf_len == 0) {622 playCb_(userData_, 0, play_buf, parentStrm_->samples_per_frame<<1);623 play_buf_len = parentStrm_->samples_per_frame;624 play_buf_start = 0;625 }626 627 unsigned tmp;628 629 tmp = PJ_MIN(play_buf_len,630 CPjAudioEngine::aps_samples_per_frame - enc_len);631 pjmedia_ulaw_encode((pj_uint8_t*)&play_buf[play_buf_start],632 &play_buf[play_buf_start],633 tmp);634 buffer.iBuffer.Append((TUint8*)&play_buf[play_buf_start], tmp);635 enc_len += tmp;636 play_buf_len -= tmp;637 play_buf_start += tmp;638 }639 640 iWriteQ.Send(buffer);641 }642 643 598 // 644 599 // End of inherited from MQueueHandlerObserver … … 650 605 if (state_ == STATE_READY || state_ == STATE_STREAMING) { 651 606 iSession.ActivateLoudspeaker(active); 607 TRACE_((THIS_FILE, "Loudspeaker on/off: %d", active)); 652 608 return KErrNone; 653 609 } … … 658 614 659 615 616 static void RecCbPcm(TAPSCommBuffer &buf, void *user_data) 617 { 618 pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data; 619 620 pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0); 621 622 /* Detect the recorder G.711 frame size, player frame size will follow 623 * this recorder frame size. 624 */ 625 if (aps_g711_frame_len == 0) { 626 aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160; 627 TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples", 628 aps_g711_frame_len)); 629 } 630 631 /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf. 632 * Whenever rec_buf is full, call parent stream callback. 633 */ 634 unsigned dec_len = 0; 635 636 while (dec_len < aps_g711_frame_len) { 637 unsigned tmp; 638 639 tmp = PJ_MIN(strm->samples_per_frame - strm->rec_buf_len, 640 aps_g711_frame_len - dec_len); 641 pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len], 642 buf.iBuffer.Ptr() + 2 + dec_len, 643 tmp); 644 strm->rec_buf_len += tmp; 645 dec_len += tmp; 646 647 pj_assert(strm->rec_buf_len <= strm->samples_per_frame); 648 649 if (strm->rec_buf_len == strm->samples_per_frame) { 650 strm->rec_cb(strm->user_data, 0, strm->rec_buf, 651 strm->rec_buf_len << 1); 652 strm->rec_buf_len = 0; 653 } 654 } 655 } 656 657 static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data) 658 { 659 pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data; 660 unsigned g711_frame_len = aps_g711_frame_len; 661 662 buf.iCommand = CQueueHandler::EAPSPlayData; 663 buf.iStatus = 0; 664 buf.iBuffer.Zero(); 665 buf.iBuffer.Append(1); 666 buf.iBuffer.Append(0); 667 668 /* Assume frame size is 10ms if frame size hasn't been known. */ 669 if (g711_frame_len == 0) 670 g711_frame_len = 80; 671 672 /* Call parent stream callback to get PCM samples to play, 673 * encode the PCM samples into G.711 and put it into APS buffer. 674 */ 675 unsigned enc_len = 0; 676 while (enc_len < g711_frame_len) { 677 if (strm->play_buf_len == 0) { 678 strm->play_cb(strm->user_data, 0, strm->play_buf, 679 strm->samples_per_frame<<1); 680 strm->play_buf_len = strm->samples_per_frame; 681 strm->play_buf_start = 0; 682 } 683 684 unsigned tmp; 685 686 tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - enc_len); 687 pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start], 688 &strm->play_buf[strm->play_buf_start], 689 tmp); 690 buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp); 691 enc_len += tmp; 692 strm->play_buf_len -= tmp; 693 strm->play_buf_start += tmp; 694 } 695 } 696 697 static void RecCb(TAPSCommBuffer &buf, void *user_data) 698 { 699 } 700 701 static void PlayCb(TAPSCommBuffer &buf, void *user_data) 702 { 703 } 704 660 705 /* 661 706 * Initialize sound subsystem. … … 664 709 { 665 710 snd_pool_factory = factory; 711 712 def_setting.format.u32 = PJMEDIA_FOURCC_L16; 713 def_setting.mode = 0; 714 def_setting.bitrate = 128000; 715 def_setting.plc = PJ_FALSE; 716 def_setting.vad = PJ_FALSE; 717 def_setting.cng = PJ_FALSE; 718 def_setting.loudspk = PJ_FALSE; 719 666 720 return PJ_SUCCESS; 667 721 } … … 701 755 pj_pool_t *pool; 702 756 pjmedia_snd_stream *strm; 757 CPjAudioSetting setting; 758 PjAudioCallback aps_rec_cb; 759 PjAudioCallback aps_play_cb; 703 760 704 761 PJ_ASSERT_RETURN(p_snd_strm, PJ_EINVAL); 705 PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 && 762 PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 && 706 763 bits_per_sample == 16, PJ_ENOTSUP); 707 764 PJ_ASSERT_RETURN((dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && rec_cb && play_cb) … … 710 767 PJ_EINVAL); 711 768 712 pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC, 769 pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC, 713 770 NULL); 714 771 if (!pool) 715 772 return PJ_ENOMEM; 716 773 717 strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 774 strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 718 775 sizeof(pjmedia_snd_stream)); 719 776 strm->dir = dir; … … 723 780 strm->samples_per_frame = samples_per_frame; 724 781 782 /* Set audio engine settings. */ 783 if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U || 784 def_setting.format.u32 == PJMEDIA_FOURCC_L16) 785 { 786 setting.fourcc = TFourCC(KMCPFourCCIdG711); 787 } else { 788 setting.fourcc = TFourCC(def_setting.format.u32); 789 } 790 791 if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR) 792 { 793 setting.mode = (TAPSCodecMode)def_setting.bitrate; 794 } else if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U || 795 def_setting.format.u32 == PJMEDIA_FOURCC_L16 || 796 (def_setting.format.u32 == PJMEDIA_FOURCC_ILBC && 797 def_setting.mode == 30)) 798 { 799 setting.mode = EULawOr30ms; 800 } else { 801 setting.mode = EALawOr20ms; 802 } 803 804 setting.vad = def_setting.vad; 805 setting.plc = def_setting.plc; 806 setting.cng = def_setting.cng; 807 setting.loudspk = def_setting.loudspk; 808 809 if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR || 810 def_setting.format.u32 == PJMEDIA_FOURCC_G711A || 811 def_setting.format.u32 == PJMEDIA_FOURCC_G711U || 812 def_setting.format.u32 == PJMEDIA_FOURCC_G729 || 813 def_setting.format.u32 == PJMEDIA_FOURCC_ILBC) 814 { 815 aps_play_cb = &PlayCb; 816 aps_rec_cb = &RecCb; 817 } else { 818 aps_play_cb = &PlayCbPcm; 819 aps_rec_cb = &RecCbPcm; 820 } 821 725 822 // Create the audio engine. 726 TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, rec_cb, play_cb, 727 user_data)); 823 TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, 824 aps_rec_cb, aps_play_cb, 825 strm, setting)); 728 826 if (err != KErrNone) { 729 pj_pool_release(pool); 827 pj_pool_release(pool); 730 828 return PJ_RETURN_OS_ERROR(err); 731 829 } 830 831 strm->rec_cb = rec_cb; 832 strm->play_cb = play_cb; 833 strm->user_data = user_data; 834 835 /* play_buf size is samples per frame. */ 836 strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1); 837 strm->play_buf_len = 0; 838 strm->play_buf_start = 0; 839 840 /* rec_buf size is samples per frame. */ 841 strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1); 842 strm->rec_buf_len = 0; 732 843 733 844 // Done. … … 753 864 PJ_ASSERT_RETURN(index == 0, PJ_EINVAL); 754 865 755 return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count, 866 return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count, 756 867 samples_per_frame, bits_per_sample, rec_cb, NULL, 757 868 user_data, p_snd_strm); … … 770 881 PJ_ASSERT_RETURN(index == 0, PJ_EINVAL); 771 882 772 return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count, 883 return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count, 773 884 samples_per_frame, bits_per_sample, NULL, play_cb, 774 885 user_data, p_snd_strm); … … 790 901 PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL); 791 902 792 return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count, 903 return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count, 793 904 samples_per_frame, bits_per_sample, rec_cb, play_cb, 794 905 user_data, p_snd_strm); … … 822 933 { 823 934 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 824 935 825 936 if (stream->engine) { 826 937 TInt err = stream->engine->StartL(); … … 828 939 return PJ_RETURN_OS_ERROR(err); 829 940 } 830 941 831 942 return PJ_SUCCESS; 832 943 } … … 836 947 { 837 948 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 838 949 839 950 if (stream->engine) { 840 951 stream->engine->Stop(); 841 952 } 842 953 843 954 return PJ_SUCCESS; 844 955 } … … 848 959 { 849 960 pj_pool_t *pool; 850 961 851 962 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 852 853 if (stream->engine) { 854 delete stream->engine; 855 stream->engine = NULL; 856 } 963 964 delete stream->engine; 965 stream->engine = NULL; 857 966 858 967 pool = stream->pool; 859 if (pool) { 968 if (pool) { 860 969 stream->pool = NULL; 861 970 pj_pool_release(pool); 862 971 } 863 972 864 973 return PJ_SUCCESS; 865 974 } … … 876 985 * Set sound latency. 877 986 */ 878 PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, 987 PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, 879 988 unsigned output_latency) 880 989 { … … 890 999 */ 891 1000 PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker( 892 pjmedia_snd_stream *stream, 1001 pjmedia_snd_stream *stream, 893 1002 pj_bool_t active) 894 1003 { 895 1004 if (stream == NULL) { 896 act_loudspeaker= active;1005 def_setting.loudspk = active; 897 1006 } else { 898 1007 if (stream->engine == NULL) … … 906 1015 return PJ_SUCCESS; 907 1016 } 1017 1018 /** 1019 * Set a codec and its settings to be used on the next sound device session. 1020 */ 1021 PJ_DEF(pj_status_t) pjmedia_snd_aps_modify_setting( 1022 const pjmedia_snd_aps_setting *setting) 1023 { 1024 PJ_ASSERT_RETURN(setting, PJ_EINVAL); 1025 1026 def_setting = *setting; 1027 1028 return PJ_SUCCESS; 1029 } 1030 1031 #endif // PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
Note: See TracChangeset
for help on using the changeset viewer.