Ticket #872: symbian-mda-g711.patch
File symbian-mda-g711.patch, 31.4 KB (added by bennylp, 15 years ago) |
---|
-
pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
38 38 #include <mdaaudiooutputstream.h> 39 39 #include <mdaaudioinputstream.h> 40 40 41 #include "s60_g729_bitstream.h" 41 42 42 43 #define THIS_FILE "symb_mda_dev.c" 43 44 #define BITS_PER_SAMPLE 16 … … 50 51 # define TRACE_(st) 51 52 #endif 52 53 54 /* Forward decl */ 55 class CPjSndUtility; 53 56 54 57 /* MDA factory */ 55 58 struct mda_factory … … 57 60 pjmedia_aud_dev_factory base; 58 61 pj_pool_t *pool; 59 62 pj_pool_factory *pf; 63 CPjSndUtility *sndUtility; 60 64 pjmedia_aud_dev_info dev_info; 61 65 }; 62 66 … … 74 78 pj_pool_t *pool; /**< Memory pool. */ 75 79 76 80 // Common settings. 77 pjmedia_aud_param param;/**< Stream param. */81 pjmedia_aud_param param; /**< Stream param. */ 78 82 79 83 // Audio engine 80 84 CPjAudioInputEngine *in_engine; /**< Record engine. */ 81 85 CPjAudioOutputEngine *out_engine; /**< Playback engine. */ 86 87 // Detected G.711 frame size 88 unsigned mda_g711_frame_len; 82 89 }; 83 90 84 91 … … 176 183 { 177 184 PJ_LOG(1,(THIS_FILE, "%s: error code %d", title, rc)); 178 185 } 179 186 187 static TFourCC format_id_to_fourcc(const pjmedia_format *fmt) 188 { 189 switch (fmt->id) { 190 case PJMEDIA_FORMAT_L16: 191 case PJMEDIA_FORMAT_PCMA: 192 return TFourCC('G', '7', '1', '1'); 193 case PJMEDIA_FORMAT_AMR: 194 return TFourCC(' ', 'A', 'M', 'R'); 195 case PJMEDIA_FORMAT_G729: 196 return TFourCC('G', '7', '2', '9'); 197 case PJMEDIA_FORMAT_ILBC: 198 return TFourCC('i', 'L', 'B', 'C'); 199 } 200 201 pj_assert(!"Unsupported codec"); 202 return TFourCC('G', '7', '1', '1'); 203 } 204 180 205 ////////////////////////////////////////////////////////////////////////////// 181 206 // 182 207 … … 240 265 241 266 // cache variable 242 267 // to avoid calculating frame length repeatedly 243 TInt frameLen_;268 TInt pcmFrameLen_; 244 269 245 270 // sometimes recorded size != requested framesize, so let's 246 271 // provide a buffer to make sure the rec callback returning … … 248 273 TUint8 *frameRecBuf_; 249 274 TInt frameRecBufLen_; 250 275 276 // is the device opened in g711 format? 277 bool fmt_g711_; 278 279 // stuffs to process encoded frame 280 TBitStream bitStream_; 281 251 282 CPjAudioInputEngine(struct mda_stream *parent_strm, 252 283 pjmedia_aud_rec_cb rec_cb, 253 284 void *user_data); 254 285 void ConstructL(); 255 286 TPtr8 & GetFrame(); 256 287 288 void RecCbPcm(const TDesC8 &aBuffer); 289 void RecCbEncoded(const TDesC8 &aBuffer); 290 257 291 public: 258 292 virtual void MaiscOpenComplete(TInt aError); 259 293 virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer); … … 269 303 recCb_(rec_cb), userData_(user_data), 270 304 iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0), 271 305 lastError_(KErrNone), timeStamp_(0), 272 frameLen_(parent_strm->param.samples_per_frame * 273 BYTES_PER_SAMPLE), 274 frameRecBuf_(NULL), frameRecBufLen_(0) 306 pcmFrameLen_(parent_strm->param.samples_per_frame * 307 BYTES_PER_SAMPLE), 308 frameRecBuf_(NULL), frameRecBufLen_(0), 309 fmt_g711_(false) 275 310 { 276 311 } 277 312 … … 289 324 290 325 void CPjAudioInputEngine::ConstructL() 291 326 { 292 iStreamBuffer_ = HBufC8::NewL( frameLen_);327 iStreamBuffer_ = HBufC8::NewL(pcmFrameLen_); 293 328 CleanupStack::PushL(iStreamBuffer_); 294 329 295 frameRecBuf_ = new TUint8[ frameLen_*2];330 frameRecBuf_ = new TUint8[pcmFrameLen_*2]; 296 331 CleanupStack::PushL(frameRecBuf_); 297 332 } 298 333 … … 362 397 iInputStream_->Open(&iStreamSettings); 363 398 364 399 // Success 365 PJ_LOG(4,(THIS_FILE, "Sound capture started."));366 400 return PJ_SUCCESS; 367 401 } 368 402 … … 390 424 391 425 TPtr8 & CPjAudioInputEngine::GetFrame() 392 426 { 393 //iStreamBuffer_->Des().FillZ( frameLen_);394 iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);427 //iStreamBuffer_->Des().FillZ(pcmFrameLen_); 428 iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), pcmFrameLen_, pcmFrameLen_); 395 429 return iFramePtr_; 396 430 } 397 431 … … 403 437 return; 404 438 } 405 439 440 PJ_LOG(4,(THIS_FILE, "Sound capture started.")); 441 406 442 // set stream priority to normal and time sensitive 407 443 iInputStream_->SetPriority(EPriorityNormal, 408 EMdaPriorityPreferenceTime); 409 444 EMdaPriorityPreferenceTime); 445 446 // set the stream format 447 TFourCC aFmt = format_id_to_fourcc(&parentStrm_->param.ext_fmt); 448 TRAPD(fmterr, iInputStream_->SetDataTypeL(aFmt)); 449 if (fmterr) { 450 if (parentStrm_->param.ext_fmt.id != 0) { 451 // This is error since we can't open codec as requested 452 snd_perror("Unable to set capture format", aError); 453 return; 454 } else { 455 // This is fine. The stream is opened in PCM but we couldn't 456 // set G.711 codec, so just revert back to use PCM. 457 fmt_g711_ = false; 458 } 459 } else { 460 if (parentStrm_->param.ext_fmt.id == 0) 461 fmt_g711_ = true; 462 } 463 410 464 // Read the first frame. 411 465 TPtr8 & frm = GetFrame(); 412 466 TRAPD(err2, iInputStream_->ReadL(frm)); … … 415 469 } 416 470 } 417 471 418 void CPjAudioInputEngine::MaiscBufferCopied(TInt aError, 419 472 // Record callback for PCM format. 473 void CPjAudioInputEngine::RecCbPcm(const TDesC8 &aBuffer) 420 474 { 421 lastError_ = aError; 422 if (aError != KErrNone) { 423 snd_perror("Error in MaiscBufferCopied()", aError); 424 return; 425 } 426 427 if (frameRecBufLen_ || aBuffer.Length() < frameLen_) { 428 pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length()); 475 if (fmt_g711_) { 476 pj_assert(aBuffer.Length() >= 2); 477 if (aBuffer[0]==1 && aBuffer[1]==0) { 478 /* Normal voice */ 479 pjmedia_alaw_decode((short*)(frameRecBuf_ + frameRecBufLen_), 480 ((const pj_uint8_t*)aBuffer.Ptr())+2, 481 aBuffer.Length()-2); 482 frameRecBufLen_ += ((aBuffer.Length()-2) << 1); 483 } else { 484 /* Zeroes or SID */ 485 pj_bzero(frameRecBuf_ + frameRecBufLen_, ((aBuffer.Length()-2) << 1)); 486 frameRecBufLen_ += ((aBuffer.Length()-2) << 1); 487 } 488 } else { 489 pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), 490 aBuffer.Length()); 429 491 frameRecBufLen_ += aBuffer.Length(); 430 492 } 431 493 432 494 if (frameRecBufLen_) { 433 while (frameRecBufLen_ >= frameLen_) {495 while (frameRecBufLen_ >= pcmFrameLen_) { 434 496 pjmedia_frame f; 435 497 436 498 f.type = PJMEDIA_FRAME_TYPE_AUDIO; 437 499 f.buf = frameRecBuf_; 438 f.size = frameLen_;500 f.size = pcmFrameLen_; 439 501 f.timestamp.u32.lo = timeStamp_; 440 502 f.bit_info = 0; 441 503 … … 444 506 // Increment timestamp. 445 507 timeStamp_ += parentStrm_->param.samples_per_frame; 446 508 447 frameRecBufLen_ -= frameLen_;448 pj_memmove(frameRecBuf_, frameRecBuf_+ frameLen_, frameRecBufLen_);509 frameRecBufLen_ -= pcmFrameLen_; 510 pj_memmove(frameRecBuf_, frameRecBuf_+pcmFrameLen_, frameRecBufLen_); 449 511 } 450 } else {451 pjmedia_frame f;452 453 f.type = PJMEDIA_FRAME_TYPE_AUDIO;454 f.buf = (void*)aBuffer.Ptr();455 f.size = aBuffer.Length();456 f.timestamp.u32.lo = timeStamp_;457 f.bit_info = 0;458 459 // Call the callback.460 recCb_(userData_, &f);461 462 // Increment timestamp.463 timeStamp_ += parentStrm_->param.samples_per_frame;464 512 } 465 513 466 514 // Record next frame 467 515 TPtr8 & frm = GetFrame(); 468 516 TRAPD(err2, iInputStream_->ReadL(frm)); … … 471 519 } 472 520 } 473 521 522 // Record callback for encoded format. 523 void CPjAudioInputEngine::RecCbEncoded(const TDesC8 &aBuffer) 524 { 525 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) frameRecBuf_; 526 527 switch(parentStrm_->param.ext_fmt.id) { 528 case PJMEDIA_FORMAT_AMR: 529 { 530 const pj_uint8_t *p = (const pj_uint8_t*)aBuffer.Ptr() + 1; 531 unsigned len = aBuffer.Length() - 1; 532 533 pjmedia_frame_ext_append_subframe(frame, p, len << 3, 160); 534 if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 535 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 536 recCb_(userData_, (pjmedia_frame*)frame); 537 frame->samples_cnt = 0; 538 frame->subframe_cnt = 0; 539 } 540 } 541 break; 542 543 case PJMEDIA_FORMAT_G729: 544 { 545 /* Check if we got a normal or SID frame. */ 546 if (aBuffer[0] != 0 || aBuffer[1] != 0) { 547 enum { NORMAL_LEN = 22, SID_LEN = 8 }; 548 unsigned src_len = aBuffer.Length()- 2; 549 550 pj_assert(src_len == NORMAL_LEN || src_len == SID_LEN); 474 551 552 const TDesC8& p = bitStream_.CompressG729Frame( 553 aBuffer.Right(src_len), 554 src_len == SID_LEN); 555 556 pjmedia_frame_ext_append_subframe(frame, p.Ptr(), 557 p.Length() << 3, 80); 558 } else { /* We got null frame. */ 559 pjmedia_frame_ext_append_subframe(frame, NULL, 0, 80); 560 } 561 562 if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 563 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 564 recCb_(userData_, (pjmedia_frame*)frame); 565 frame->samples_cnt = 0; 566 frame->subframe_cnt = 0; 567 } 568 } 569 break; 570 571 case PJMEDIA_FORMAT_ILBC: 572 { 573 unsigned samples_got; 574 575 samples_got = (parentStrm_->param.ext_fmt.bitrate) == 15200? 576 160 : 240; 577 578 /* Check if we got a normal frame. */ 579 if (aBuffer[0] == 1 && aBuffer[1] == 0) { 580 const pj_uint8_t *p = (const pj_uint8_t*)aBuffer.Ptr() + 2; 581 unsigned len = aBuffer.Length() - 2; 582 583 pjmedia_frame_ext_append_subframe(frame, p, len << 3, 584 samples_got); 585 } else { /* We got null frame. */ 586 pjmedia_frame_ext_append_subframe(frame, NULL, 0, samples_got); 587 } 588 589 if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 590 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 591 recCb_(userData_, (pjmedia_frame*)frame); 592 frame->samples_cnt = 0; 593 frame->subframe_cnt = 0; 594 } 595 } 596 break; 597 598 case PJMEDIA_FORMAT_PCMU: 599 case PJMEDIA_FORMAT_PCMA: 600 { 601 unsigned samples_processed = 0; 602 603 /* Make sure it is normal frame. */ 604 pj_assert(aBuffer[0] == 1 && aBuffer[1] == 0); 605 606 /* Detect the recorder G.711 frame size, player frame size will 607 * follow this recorder frame size. 608 */ 609 if (parentStrm_->mda_g711_frame_len == 0) { 610 parentStrm_->mda_g711_frame_len = aBuffer.Length()-2; 611 TRACE_((THIS_FILE, "Detected MDA G.711 frame size = %u samples", 612 parentStrm_->mda_g711_frame_len)); 613 } 614 615 /* Convert APS buffer format into pjmedia_frame_ext. Whenever 616 * samples count in the frame is equal to stream's samples per 617 * frame, call parent stream callback. 618 */ 619 while (samples_processed < parentStrm_->mda_g711_frame_len) { 620 unsigned tmp; 621 const pj_uint8_t *pb = (const pj_uint8_t*)aBuffer.Ptr() + 622 2 + samples_processed; 623 624 tmp = PJ_MIN(parentStrm_->param.samples_per_frame - frame->samples_cnt, 625 parentStrm_->mda_g711_frame_len - samples_processed); 626 627 pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp); 628 samples_processed += tmp; 629 630 if (frame->samples_cnt >= parentStrm_->param.samples_per_frame) { 631 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 632 recCb_(userData_, (pjmedia_frame*)frame); 633 frame->samples_cnt = 0; 634 frame->subframe_cnt = 0; 635 } 636 } 637 } 638 break; 639 640 default: 641 break; 642 } 643 644 } 645 646 void CPjAudioInputEngine::MaiscBufferCopied(TInt aError, 647 const TDesC8 &aBuffer) 648 { 649 lastError_ = aError; 650 if (aError != KErrNone) { 651 snd_perror("Error in MaiscBufferCopied()", aError); 652 return; 653 } 654 655 if (this->parentStrm_->param.ext_fmt.id == 0) 656 RecCbPcm(aBuffer); 657 else 658 RecCbEncoded(aBuffer); 659 } 660 661 475 662 void CPjAudioInputEngine::MaiscRecordComplete(TInt aError) 476 663 { 477 664 lastError_ = aError; … … 540 727 pjmedia_aud_play_cb playCb_; 541 728 void *userData_; 542 729 CMdaAudioOutputStream *iOutputStream_; 543 TUint8 *frameBuf_; 544 unsigned frameBufSize_; 730 TUint8 *frameBuf_; // buffer for playCb_ 731 unsigned frameBufSize_; // left over samples in frameBuf_ 732 unsigned frameBufCapacity_; // frameBuf_ capacity 545 733 TPtrC8 frame_; 546 734 TInt lastError_; 547 735 unsigned timestamp_; 548 736 bool fmt_g711_; 737 pj_uint8_t *tmpEncodedFrame_; 738 549 739 CPjAudioOutputEngine(struct mda_stream *parent_strm, 550 740 pjmedia_aud_play_cb play_cb, 551 741 void *user_data); … … 554 744 virtual void MaoscOpenComplete(TInt aError); 555 745 virtual void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer); 556 746 virtual void MaoscPlayComplete(TInt aError); 747 748 bool GetFramePcm(); 749 bool PlayFramePcm(); 750 bool PlayFrameEncoded(); 557 751 }; 558 752 559 753 … … 561 755 pjmedia_aud_play_cb play_cb, 562 756 void *user_data) 563 757 : state_(STATE_INACTIVE), parentStrm_(parent_strm), playCb_(play_cb), 564 userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL), 565 lastError_(KErrNone), timestamp_(0) 758 userData_(user_data), iOutputStream_(NULL), frameBuf_(NULL), frameBufSize_(0), 759 lastError_(KErrNone), timestamp_(0), fmt_g711_(false), tmpEncodedFrame_(NULL) 566 760 { 567 761 } 568 762 569 763 570 764 void CPjAudioOutputEngine::ConstructL() 571 765 { 572 frameBufSize_ = parentStrm_->param.samples_per_frame * 573 BYTES_PER_SAMPLE; 574 frameBuf_ = new TUint8[frameBufSize_]; 766 frameBufCapacity_ = parentStrm_->param.samples_per_frame * 2 * 767 BYTES_PER_SAMPLE; 768 frameBuf_ = new TUint8[frameBufCapacity_]; 769 tmpEncodedFrame_ = new TUint8[parentStrm_->param.samples_per_frame+2]; 575 770 } 576 771 577 772 CPjAudioOutputEngine::~CPjAudioOutputEngine() 578 773 { 579 774 Stop(); 580 delete [] frameBuf_; 775 delete [] frameBuf_; 776 delete [] tmpEncodedFrame_; 581 777 } 582 778 583 779 CPjAudioOutputEngine * … … 663 859 state_ = STATE_INACTIVE; 664 860 } 665 861 862 bool CPjAudioOutputEngine::GetFramePcm() 863 { 864 pjmedia_frame f; 865 unsigned pcm_size = parentStrm_->param.samples_per_frame * BYTES_PER_SAMPLE; 866 pj_status_t status; 867 868 f.type = PJMEDIA_FRAME_TYPE_AUDIO; 869 f.buf = frameBuf_+frameBufSize_; 870 f.size = pcm_size; 871 f.timestamp.u32.lo = timestamp_; 872 f.bit_info = 0; 873 874 status = playCb_(this->userData_, &f); 875 if (status != PJ_SUCCESS) { 876 this->Stop(); 877 return false; 878 } 879 880 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO || f.size == 0) { 881 pj_bzero(frameBuf_, pcm_size); 882 f.size = pcm_size; 883 } 884 885 frameBufSize_ += pcm_size; 886 887 // Increment timestamp. 888 timestamp_ += (pcm_size / BYTES_PER_SAMPLE); 889 890 return true; 891 } 892 893 bool CPjAudioOutputEngine::PlayFramePcm() 894 { 895 if (fmt_g711_) { 896 // Here we open MDA in G.711 mode although user asked PCM 897 unsigned frame_size = parentStrm_->mda_g711_frame_len; 898 899 if (frame_size==0) 900 frame_size = 320; 901 902 // If we don't have enough samples in the buffer to fill in one frame, 903 // ask user. 904 if (frameBufSize_ < frame_size) { 905 if (!GetFramePcm()) 906 return false; 907 } 908 909 pj_assert(frameBufSize_ >= frame_size); 910 911 pjmedia_alaw_encode((pj_uint8_t*)tmpEncodedFrame_+2, 912 (const pj_int16_t*)frameBuf_, 913 frame_size >> 1); 914 tmpEncodedFrame_[0] = 1; 915 tmpEncodedFrame_[1] = 0; 916 frame_.Set(tmpEncodedFrame_, (frame_size >> 1) + 2); 917 iOutputStream_->WriteL(frame_); 918 919 frameBufSize_ -= frame_size; 920 pj_memmove(frameBuf_, frameBuf_ + frame_size, frameBufSize_); 921 } else { 922 // Here we open MDA in PCM mode, so just return the frame 923 pj_assert(frameBufSize_ == 0); 924 925 if (!GetFramePcm()) 926 return false; 927 928 frame_.Set(frameBuf_, frameBufSize_); 929 iOutputStream_->WriteL(frame_); 930 931 frameBufSize_ = 0; 932 } 933 934 return true; 935 } 936 937 bool CPjAudioOutputEngine::PlayFrameEncoded() 938 { 939 pjmedia_frame_ext *frame = (pjmedia_frame_ext*) frameBuf_; 940 941 switch(parentStrm_->param.ext_fmt.id) { 942 case PJMEDIA_FORMAT_AMR: 943 { 944 if (frame->samples_cnt == 0) { 945 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 946 strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 947 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 948 frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 949 } 950 951 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 952 pjmedia_frame_ext_subframe *sf; 953 unsigned samples_cnt; 954 955 sf = pjmedia_frame_ext_get_subframe(frame, 0); 956 samples_cnt = frame->samples_cnt / frame->subframe_cnt; 957 958 if (sf->data && sf->bitlen) { 959 /* AMR header for APS is one byte, the format (may be!): 960 * 0xxxxy00, where xxxx:frame type, y:not sure. 961 */ 962 unsigned len = (sf->bitlen+7)>>3; 963 enum {SID_FT = 8 }; 964 pj_uint8_t amr_header = 4, ft = SID_FT; 965 966 if (len >= pjmedia_codec_amrnb_framelen[0]) 967 ft = pjmedia_codec_amr_get_mode2(PJ_TRUE, len); 968 969 amr_header |= ft << 3; 970 buf.iBuffer.Append(amr_header); 971 972 buf.iBuffer.Append((TUint8*)sf->data, len); 973 } else { 974 buf.iBuffer.Append(0); 975 } 976 977 pjmedia_frame_ext_pop_subframes(frame, 1); 978 979 } else { /* PJMEDIA_FRAME_TYPE_NONE */ 980 buf.iBuffer.Append(0); 981 982 frame->samples_cnt = 0; 983 frame->subframe_cnt = 0; 984 } 985 } 986 break; 987 988 case PJMEDIA_FORMAT_G729: 989 { 990 if (frame->samples_cnt == 0) { 991 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 992 strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 993 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 994 frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 995 } 996 997 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 998 pjmedia_frame_ext_subframe *sf; 999 unsigned samples_cnt; 1000 1001 sf = pjmedia_frame_ext_get_subframe(frame, 0); 1002 samples_cnt = frame->samples_cnt / frame->subframe_cnt; 1003 1004 if (sf->data && sf->bitlen) { 1005 enum { NORMAL_LEN = 10, SID_LEN = 2 }; 1006 pj_bool_t sid_frame = ((sf->bitlen >> 3) == SID_LEN); 1007 TBitStream *bitstream = (TBitStream*)strm->strm_data; 1008 const TPtrC8 src(sf->data, sf->bitlen>>3); 1009 const TDesC8 &dst = bitstream->ExpandG729Frame(src, 1010 sid_frame); 1011 if (sid_frame) { 1012 buf.iBuffer.Append(0); 1013 buf.iBuffer.Append(1); 1014 } else { 1015 buf.iBuffer.Append(1); 1016 buf.iBuffer.Append(0); 1017 } 1018 buf.iBuffer.Append(dst); 1019 } else { 1020 buf.iBuffer.Append(0); 1021 buf.iBuffer.Append(0); 1022 } 1023 1024 pjmedia_frame_ext_pop_subframes(frame, 1); 1025 1026 } else { /* PJMEDIA_FRAME_TYPE_NONE */ 1027 buf.iBuffer.Append(0); 1028 buf.iBuffer.Append(0); 1029 1030 frame->samples_cnt = 0; 1031 frame->subframe_cnt = 0; 1032 } 1033 } 1034 break; 1035 1036 case PJMEDIA_FORMAT_ILBC: 1037 { 1038 if (frame->samples_cnt == 0) { 1039 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 1040 strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 1041 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 1042 frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 1043 } 1044 1045 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 1046 pjmedia_frame_ext_subframe *sf; 1047 unsigned samples_cnt; 1048 1049 sf = pjmedia_frame_ext_get_subframe(frame, 0); 1050 samples_cnt = frame->samples_cnt / frame->subframe_cnt; 1051 1052 pj_assert((strm->param.ext_fmt.bitrate == 15200 && 1053 samples_cnt == 160) || 1054 (strm->param.ext_fmt.bitrate != 15200 && 1055 samples_cnt == 240)); 1056 1057 if (sf->data && sf->bitlen) { 1058 buf.iBuffer.Append(1); 1059 buf.iBuffer.Append(0); 1060 buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3); 1061 } else { 1062 buf.iBuffer.Append(0); 1063 buf.iBuffer.Append(0); 1064 } 1065 1066 pjmedia_frame_ext_pop_subframes(frame, 1); 1067 1068 } else { /* PJMEDIA_FRAME_TYPE_NONE */ 1069 buf.iBuffer.Append(0); 1070 buf.iBuffer.Append(0); 1071 1072 frame->samples_cnt = 0; 1073 frame->subframe_cnt = 0; 1074 } 1075 } 1076 break; 1077 1078 case PJMEDIA_FORMAT_PCMU: 1079 case PJMEDIA_FORMAT_PCMA: 1080 { 1081 unsigned samples_ready = 0; 1082 unsigned samples_req = aps_g711_frame_len; 1083 1084 /* Assume frame size is 10ms if frame size hasn't been known. */ 1085 if (samples_req == 0) 1086 samples_req = 80; 1087 1088 buf.iBuffer.Append(1); 1089 buf.iBuffer.Append(0); 1090 1091 /* Call parent stream callback to get samples to play. */ 1092 while (samples_ready < samples_req) { 1093 if (frame->samples_cnt == 0) { 1094 frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED; 1095 strm->play_cb(strm->user_data, (pjmedia_frame*)frame); 1096 pj_assert(frame->base.type==PJMEDIA_FRAME_TYPE_EXTENDED || 1097 frame->base.type==PJMEDIA_FRAME_TYPE_NONE); 1098 } 1099 1100 if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 1101 pjmedia_frame_ext_subframe *sf; 1102 unsigned samples_cnt; 1103 1104 sf = pjmedia_frame_ext_get_subframe(frame, 0); 1105 samples_cnt = frame->samples_cnt / frame->subframe_cnt; 1106 if (sf->data && sf->bitlen) { 1107 buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3); 1108 } else { 1109 pj_uint8_t silc; 1110 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)? 1111 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0); 1112 buf.iBuffer.AppendFill(silc, samples_cnt); 1113 } 1114 samples_ready += samples_cnt; 1115 1116 pjmedia_frame_ext_pop_subframes(frame, 1); 1117 1118 } else { /* PJMEDIA_FRAME_TYPE_NONE */ 1119 pj_uint8_t silc; 1120 1121 silc = (strm->param.ext_fmt.id==PJMEDIA_FORMAT_PCMU)? 1122 pjmedia_linear2ulaw(0) : pjmedia_linear2alaw(0); 1123 buf.iBuffer.AppendFill(silc, samples_req - samples_ready); 1124 1125 samples_ready = samples_req; 1126 frame->samples_cnt = 0; 1127 frame->subframe_cnt = 0; 1128 } 1129 } 1130 } 1131 break; 1132 1133 default: 1134 break; 1135 } 1136 } 1137 666 1138 void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError) 667 1139 { 668 1140 lastError_ = aError; … … 688 1160 iOutputStream_->SetPriority(EPriorityNormal, 689 1161 EMdaPriorityPreferenceTime); 690 1162 691 // Call callback to retrieve frame from upstream. 692 pjmedia_frame f; 693 pj_status_t status; 694 695 f.type = PJMEDIA_FRAME_TYPE_AUDIO; 696 f.buf = frameBuf_; 697 f.size = frameBufSize_; 698 f.timestamp.u32.lo = timestamp_; 699 f.bit_info = 0; 700 701 status = playCb_(this->userData_, &f); 702 if (status != PJ_SUCCESS) { 703 this->Stop(); 704 return; 1163 // set format 1164 TFourCC aFmt = format_id_to_fourcc(&parentStrm_->param.ext_fmt); 1165 TRAPD(ferr, iOutputStream_->SetDataTypeL(aFmt)); 1166 if (ferr) { 1167 if (parentStrm_->param.ext_fmt.id != 0) { 1168 // This is error since we can't open codec as requested 1169 snd_perror("Unable to set playback format", aError); 1170 return; 1171 } else { 1172 // This is fine. The stream is opened in PCM but we couldn't 1173 // set G.711 codec, so just revert back to use PCM. 1174 fmt_g711_ = false; 1175 } 1176 } else { 1177 if (parentStrm_->param.ext_fmt.id == 0) 1178 fmt_g711_ = true; 705 1179 } 706 1180 707 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO) 708 pj_bzero(frameBuf_, frameBufSize_); 1181 // Kick start the first Write 1182 TPtrC8 dummy; 1183 MaoscBufferCopied(KErrNone, dummy); 709 1184 710 // Increment timestamp.711 timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);712 713 // issue WriteL() to write the first audio data block,714 // subsequent calls to WriteL() will be issued in715 // MMdaAudioOutputStreamCallback::MaoscBufferCopied()716 // until whole data buffer is written.717 frame_.Set(frameBuf_, frameBufSize_);718 iOutputStream_->WriteL(frame_);719 1185 } else { 720 1186 snd_perror("Error in MaoscOpenComplete()", aError); 721 1187 } … … 728 1194 729 1195 if (aError==KErrNone) { 730 1196 // Buffer successfully written, feed another one. 1197 if (parentStrm_->param.ext_fmt.id == 0) 1198 PlayFramePcm(); 1199 else 1200 PlayFrameEncoded(); 731 1201 732 // Call callback to retrieve frame from upstream.733 pjmedia_frame f;734 pj_status_t status;735 736 f.type = PJMEDIA_FRAME_TYPE_AUDIO;737 f.buf = frameBuf_;738 f.size = frameBufSize_;739 f.timestamp.u32.lo = timestamp_;740 f.bit_info = 0;741 742 status = playCb_(this->userData_, &f);743 if (status != PJ_SUCCESS) {744 this->Stop();745 return;746 }747 748 if (f.type != PJMEDIA_FRAME_TYPE_AUDIO)749 pj_bzero(frameBuf_, frameBufSize_);750 751 // Increment timestamp.752 timestamp_ += (frameBufSize_ / BYTES_PER_SAMPLE);753 754 // Write to playback stream.755 frame_.Set(frameBuf_, frameBufSize_);756 iOutputStream_->WriteL(frame_);757 758 1202 } else if (aError==KErrAbort) { 759 1203 // playing was aborted, due to call to CMdaAudioOutputStream::Stop() 760 1204 state_ = STATE_INACTIVE; … … 776 1220 } 777 1221 778 1222 /**************************************************************************** 1223 * MDA format enumerator 1224 */ 1225 class CPjSndUtility : public CBase, MMdaAudioInputStreamCallback 1226 { 1227 public: 1228 CPjSndUtility(); 1229 ~CPjSndUtility(); 1230 1231 bool Busy() const; 1232 pj_status_t EnumFormats(pjmedia_format fmt[], unsigned *count); 1233 1234 private: 1235 CMdaAudioInputStream *iInputStream_; 1236 bool done_; 1237 bool stopped_; 1238 pjmedia_format *fmt_; 1239 unsigned max_fmt_; 1240 unsigned *pcount_; 1241 TInt lastError_; 1242 1243 virtual void MaiscOpenComplete(TInt aError); 1244 virtual void MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer); 1245 virtual void MaiscRecordComplete(TInt aError); 1246 }; 1247 1248 CPjSndUtility::CPjSndUtility() 1249 : iInputStream_(NULL) 1250 { 1251 1252 } 1253 1254 CPjSndUtility::~CPjSndUtility() 1255 { 1256 delete iInputStream_; 1257 } 1258 1259 bool CPjSndUtility::Busy() const 1260 { 1261 return iInputStream_ != NULL; 1262 } 1263 1264 pj_status_t CPjSndUtility::EnumFormats(pjmedia_format fmt[], unsigned *count) 1265 { 1266 unsigned i; 1267 1268 if (Busy()) { 1269 // Already running 1270 return PJ_EINVALIDOP; 1271 } 1272 1273 fmt_ = fmt; 1274 pcount_ = count; 1275 max_fmt_ = *count; 1276 done_ = false; 1277 1278 // Create the stream. 1279 TRAPD(err, iInputStream_ = CMdaAudioInputStream::NewL(*this)); 1280 if (err != KErrNone) 1281 return PJ_RETURN_OS_ERROR(err); 1282 1283 // Initialize settings. 1284 TMdaAudioDataSettings iStreamSettings; 1285 iStreamSettings.iChannels = get_channel_cap(1); 1286 iStreamSettings.iSampleRate = get_clock_rate_cap(8000); 1287 1288 pj_assert(iStreamSettings.iChannels != 0 && 1289 iStreamSettings.iSampleRate != 0); 1290 1291 // Open stream. 1292 lastError_ = KRequestPending; 1293 iInputStream_->Open(&iStreamSettings); 1294 1295 for (i=0; i<100 && !done_; ++i) 1296 pj_symbianos_poll(-1, 50); 1297 1298 stopped_ = false; 1299 iInputStream_->Stop(); 1300 for (i=0; i<10 && !stopped_; ++i) 1301 pj_symbianos_poll(-1, 50); 1302 1303 if (stopped_) { 1304 delete iInputStream_; 1305 iInputStream_ = NULL; 1306 } 1307 1308 if (!done_) 1309 return PJ_RETURN_OS_ERROR(KRequestPending); 1310 1311 return PJ_RETURN_OS_ERROR(lastError_); 1312 } 1313 1314 void CPjSndUtility::MaiscOpenComplete(TInt aError) 1315 { 1316 struct fmt_info 1317 { 1318 TFourCC fourcc; 1319 const char *name; 1320 pjmedia_format fmt; 1321 } fmt[8]; 1322 unsigned i, cnt = 0; 1323 1324 lastError_ = aError; 1325 if (aError != KErrNone) { 1326 snd_perror("Error in MaiscOpenComplete()", aError); 1327 done_ = true; 1328 return; 1329 } 1330 1331 pj_bzero(fmt, sizeof(fmt)); 1332 1333 // G.711 Alaw 1334 fmt[cnt].fourcc = TFourCC('G', '7', '1', '1'); 1335 fmt[cnt].fmt.id = PJMEDIA_FORMAT_ALAW; 1336 fmt[cnt].fmt.bitrate = 64000; 1337 fmt[cnt].fmt.vad = PJ_TRUE; 1338 cnt++; 1339 1340 // AMR-NB 1341 fmt[cnt].fourcc = TFourCC(' ', 'A', 'M', 'R'); 1342 fmt[cnt].fmt.id = PJMEDIA_FORMAT_AMR; 1343 fmt[cnt].fmt.bitrate = 7400; 1344 fmt[cnt].fmt.vad = PJ_TRUE; 1345 cnt++; 1346 1347 // G.729 1348 fmt[cnt].fourcc = TFourCC('G', '7', '2', '9'); 1349 fmt[cnt].fmt.id = PJMEDIA_FORMAT_G729; 1350 fmt[cnt].fmt.bitrate = 8000; 1351 fmt[cnt].fmt.vad = PJ_TRUE; 1352 cnt++; 1353 1354 // iLBC 1355 fmt[cnt].fourcc = TFourCC('i', 'L', 'B', 'C'); 1356 fmt[cnt].fmt.id = PJMEDIA_FORMAT_ILBC; 1357 fmt[cnt].fmt.bitrate = 13333; 1358 fmt[cnt].fmt.vad = PJ_TRUE; 1359 cnt++; 1360 1361 // set stream priority to normal and time sensitive 1362 iInputStream_->SetPriority(EPriorityNormal, 1363 EMdaPriorityPreferenceTime); 1364 1365 // Test each formats 1366 for (i=0; i<cnt; ++i) { 1367 TRAPD(fmterr, iInputStream_->SetDataTypeL(fmt[i].fourcc)); 1368 if (!fmterr && *pcount_ < max_fmt_) { 1369 fmt_[*pcount_] = fmt[i].fmt; 1370 (*pcount_)++; 1371 } 1372 PJ_LOG(4,(THIS_FILE, "Format %s is %s", 1373 fmt[i].name, (fmterr? "not supported" : "supported"))); 1374 } 1375 1376 done_ = true; 1377 } 1378 1379 void CPjSndUtility::MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer) 1380 { 1381 // Should not happen 1382 pj_assert(!"Should not be called"); 1383 } 1384 1385 void CPjSndUtility::MaiscRecordComplete(TInt aError) 1386 { 1387 stopped_ = true; 1388 } 1389 1390 /**************************************************************************** 779 1391 * Factory operations 780 1392 */ 781 1393 … … 807 1419 static pj_status_t factory_init(pjmedia_aud_dev_factory *f) 808 1420 { 809 1421 struct mda_factory *af = (struct mda_factory*)f; 810 1422 unsigned i; 1423 pj_status_t status; 1424 1425 PJ_LOG(4, (THIS_FILE, "Initializing Symbian MDA sound subsystem..")); 1426 811 1427 pj_ansi_strcpy(af->dev_info.name, "Symbian Audio"); 812 1428 af->dev_info.default_samples_per_sec = 8000; 813 1429 af->dev_info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING | … … 815 1431 af->dev_info.input_count = 1; 816 1432 af->dev_info.output_count = 1; 817 1433 818 PJ_LOG(4, (THIS_FILE, "Symb Mda initialized")); 819 1434 // Enum supported formats 1435 af->sndUtility = new CPjSndUtility; 1436 af->dev_info.ext_fmt_cnt = PJ_ARRAY_SIZE(af->dev_info.ext_fmt); 1437 status = af->sndUtility->EnumFormats(af->dev_info.ext_fmt, 1438 &af->dev_info.ext_fmt_cnt); 1439 if (status != PJ_SUCCESS) { 1440 af->dev_info.ext_fmt_cnt = 0; 1441 } else { 1442 af->dev_info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT; 1443 } 1444 1445 if (!af->sndUtility->Busy()) { 1446 delete af->sndUtility; 1447 af->sndUtility = NULL; 1448 } 1449 1450 // If G.711 is supported, that should indicate that EC and VAD is being 1451 // used. It cannot be turned off by this API though. 1452 for (i=0; i<af->dev_info.ext_fmt_cnt; ++i) { 1453 if (af->dev_info.ext_fmt[i].id == PJMEDIA_FORMAT_PCMA) 1454 break; 1455 } 1456 if (i != af->dev_info.ext_fmt_cnt) { 1457 af->dev_info.caps |= (PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_VAD); 1458 } 1459 1460 PJ_LOG(4, (THIS_FILE, "Symbian MDA sound initialized")); 820 1461 return PJ_SUCCESS; 821 1462 } 822 1463 … … 826 1467 struct mda_factory *af = (struct mda_factory*)f; 827 1468 pj_pool_t *pool = af->pool; 828 1469 1470 delete af->sndUtility; 1471 af->sndUtility = NULL; 1472 829 1473 af->pool = NULL; 830 1474 pj_pool_release(pool); 831 1475 … … 874 1518 param->bits_per_sample = BITS_PER_SAMPLE; 875 1519 param->flags = af->dev_info.caps; 876 1520 1521 /* Clear volume settings */ 1522 param->flags &= ~(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | 1523 PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING); 1524 877 1525 return PJ_SUCCESS; 878 1526 } 879 1527 … … 900 1548 PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL); 901 1549 902 1550 /* Create and Initialize stream descriptor */ 903 pool = pj_pool_create(mf->pf, "symb_aud_dev", 1000, 1000, NULL);1551 pool = pj_pool_create(mf->pf, "symb_aud_dev", 512, 512, NULL); 904 1552 PJ_ASSERT_RETURN(pool, PJ_ENOMEM); 905 1553 906 1554 strm = PJ_POOL_ZALLOC_T(pool, struct mda_stream); … … 934 1582 strm->base.op = &stream_op; 935 1583 *p_aud_strm = &strm->base; 936 1584 1585 /* Apply output volume setting if specified */ 1586 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) { 1587 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 1588 ¶m->output_vol); 1589 } 1590 1591 /* Apply input volume setting if specified */ 1592 if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) { 1593 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, 1594 ¶m->input_vol); 1595 } 1596 937 1597 return PJ_SUCCESS; 938 1598 } 939 1599 … … 947 1607 948 1608 pj_memcpy(pi, &strm->param, sizeof(*pi)); 949 1609 1610 /* Update the output volume setting */ 1611 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, 1612 &pi->output_vol) == PJ_SUCCESS) 1613 { 1614 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; 1615 } else { 1616 pi->flags &= (~PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING); 1617 } 1618 1619 /* Update the input volume setting */ 1620 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, 1621 &pi->input_vol) == PJ_SUCCESS) 1622 { 1623 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING; 1624 } else { 1625 pi->flags &= (~PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING); 1626 } 1627 950 1628 return PJ_SUCCESS; 951 1629 } 952 1630