OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 // TODO(hlundin): The functionality in this file should be moved into one or | 11 // TODO(hlundin): The functionality in this file should be moved into one or |
12 // several classes. | 12 // several classes. |
13 | 13 |
14 #include <assert.h> | 14 #include <assert.h> |
15 #include <errno.h> | 15 #include <errno.h> |
16 #include <limits.h> // For ULONG_MAX returned by strtoul. | 16 #include <limits.h> // For ULONG_MAX returned by strtoul. |
17 #include <stdio.h> | 17 #include <stdio.h> |
18 #include <stdlib.h> // For strtoul. | 18 #include <stdlib.h> // For strtoul. |
19 | 19 |
20 #include <algorithm> | 20 #include <algorithm> |
21 #include <iostream> | 21 #include <iostream> |
| 22 #include <limits> |
22 #include <string> | 23 #include <string> |
23 | 24 |
24 #include "google/gflags.h" | 25 #include "google/gflags.h" |
25 #include "webrtc/base/checks.h" | 26 #include "webrtc/base/checks.h" |
26 #include "webrtc/base/safe_conversions.h" | 27 #include "webrtc/base/safe_conversions.h" |
27 #include "webrtc/base/scoped_ptr.h" | 28 #include "webrtc/base/scoped_ptr.h" |
28 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" | 29 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" |
29 #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" | 30 #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" |
30 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" | 31 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" |
31 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" | 32 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" |
32 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" | 33 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" |
33 #include "webrtc/modules/audio_coding/neteq/tools/packet.h" | 34 #include "webrtc/modules/audio_coding/neteq/tools/packet.h" |
| 35 #include "webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h" |
34 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" | 36 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" |
35 #include "webrtc/modules/interface/module_common_types.h" | 37 #include "webrtc/modules/interface/module_common_types.h" |
36 #include "webrtc/system_wrappers/interface/trace.h" | 38 #include "webrtc/system_wrappers/interface/trace.h" |
| 39 #include "webrtc/test/rtp_file_reader.h" |
37 #include "webrtc/test/testsupport/fileutils.h" | 40 #include "webrtc/test/testsupport/fileutils.h" |
38 #include "webrtc/typedefs.h" | 41 #include "webrtc/typedefs.h" |
39 | 42 |
40 using webrtc::NetEq; | 43 using webrtc::NetEq; |
41 using webrtc::WebRtcRTPHeader; | 44 using webrtc::WebRtcRTPHeader; |
42 | 45 |
43 namespace { | 46 namespace { |
44 | 47 |
45 // Parses the input string for a valid SSRC (at the start of the string). If a | 48 // Parses the input string for a valid SSRC (at the start of the string). If a |
46 // valid SSRC is found, it is written to the output variable |ssrc|, and true is | 49 // valid SSRC is found, it is written to the output variable |ssrc|, and true is |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 if (FLAGS_codec_map) { | 381 if (FLAGS_codec_map) { |
379 // We have already printed the codec map. Just end the program. | 382 // We have already printed the codec map. Just end the program. |
380 return 0; | 383 return 0; |
381 } | 384 } |
382 // Print usage information. | 385 // Print usage information. |
383 std::cout << google::ProgramUsage(); | 386 std::cout << google::ProgramUsage(); |
384 return 0; | 387 return 0; |
385 } | 388 } |
386 | 389 |
387 printf("Input file: %s\n", argv[1]); | 390 printf("Input file: %s\n", argv[1]); |
388 rtc::scoped_ptr<webrtc::test::RtpFileSource> file_source( | 391 |
389 webrtc::test::RtpFileSource::Create(argv[1])); | 392 // TODO(ivoc): Modify the RtpFileSource::Create and RtcEventLogSource::Create |
| 393 // functions to return a nullptr on failure instead of crashing |
| 394 // the program. |
| 395 |
| 396 // This temporary solution uses a RtpFileReader directly to check if the file |
| 397 // is a valid RtpDump file. |
| 398 bool is_rtp_dump = false; |
| 399 { |
| 400 rtc::scoped_ptr<webrtc::test::RtpFileReader> rtp_reader( |
| 401 webrtc::test::RtpFileReader::Create( |
| 402 webrtc::test::RtpFileReader::kRtpDump, argv[1])); |
| 403 if (rtp_reader) |
| 404 is_rtp_dump = true; |
| 405 } |
| 406 rtc::scoped_ptr<webrtc::test::PacketSource> file_source; |
| 407 webrtc::test::RtcEventLogSource* event_log_source = nullptr; |
| 408 if (is_rtp_dump) { |
| 409 file_source.reset(webrtc::test::RtpFileSource::Create(argv[1])); |
| 410 } else { |
| 411 event_log_source = webrtc::test::RtcEventLogSource::Create(argv[1]); |
| 412 file_source.reset(event_log_source); |
| 413 } |
| 414 |
390 assert(file_source.get()); | 415 assert(file_source.get()); |
391 | 416 |
392 // Check if an SSRC value was provided. | 417 // Check if an SSRC value was provided. |
393 if (!FLAGS_ssrc.empty()) { | 418 if (!FLAGS_ssrc.empty()) { |
394 uint32_t ssrc; | 419 uint32_t ssrc; |
395 CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; | 420 CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; |
396 file_source->SelectSsrc(ssrc); | 421 file_source->SelectSsrc(ssrc); |
397 } | 422 } |
398 | 423 |
399 // Check if a replacement audio file was provided, and if so, open it. | 424 // Check if a replacement audio file was provided, and if so, open it. |
400 bool replace_payload = false; | 425 bool replace_payload = false; |
401 rtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file; | 426 rtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file; |
402 if (!FLAGS_replacement_audio_file.empty()) { | 427 if (!FLAGS_replacement_audio_file.empty()) { |
403 replacement_audio_file.reset( | 428 replacement_audio_file.reset( |
404 new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); | 429 new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); |
405 replace_payload = true; | 430 replace_payload = true; |
406 } | 431 } |
407 | 432 |
408 // Read first packet. | 433 // Read first packet. |
409 rtc::scoped_ptr<webrtc::test::Packet> packet(file_source->NextPacket()); | 434 rtc::scoped_ptr<webrtc::test::Packet> packet(file_source->NextPacket()); |
410 if (!packet) { | 435 if (!packet) { |
411 printf( | 436 printf( |
412 "Warning: input file is empty, or the filters did not match any " | 437 "Warning: input file is empty, or the filters did not match any " |
413 "packets\n"); | 438 "packets\n"); |
414 webrtc::Trace::ReturnTrace(); | 439 webrtc::Trace::ReturnTrace(); |
415 return 0; | 440 return 0; |
416 } | 441 } |
417 bool packet_available = true; | 442 if (packet->payload_length_bytes() == 0 && !replace_payload) { |
| 443 std::cerr << "Warning: input file contains header-only packets, but no " |
| 444 << "replacement file is specified." << std::endl; |
| 445 webrtc::Trace::ReturnTrace(); |
| 446 return -1; |
| 447 } |
418 | 448 |
419 // Check the sample rate. | 449 // Check the sample rate. |
420 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); | 450 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); |
421 if (sample_rate_hz <= 0) { | 451 if (sample_rate_hz <= 0) { |
422 printf("Warning: Invalid sample rate from RTP packet.\n"); | 452 printf("Warning: Invalid sample rate from RTP packet.\n"); |
423 webrtc::Trace::ReturnTrace(); | 453 webrtc::Trace::ReturnTrace(); |
424 return 0; | 454 return 0; |
425 } | 455 } |
426 | 456 |
427 // Open the output file now that we know the sample rate. (Rate is only needed | 457 // Open the output file now that we know the sample rate. (Rate is only needed |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
469 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); | 499 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); |
470 payload_mem_size_bytes = 2 * input_frame_size_timestamps; | 500 payload_mem_size_bytes = 2 * input_frame_size_timestamps; |
471 payload.reset(new uint8_t[payload_mem_size_bytes]); | 501 payload.reset(new uint8_t[payload_mem_size_bytes]); |
472 next_packet.reset(file_source->NextPacket()); | 502 next_packet.reset(file_source->NextPacket()); |
473 assert(next_packet); | 503 assert(next_packet); |
474 next_packet_available = true; | 504 next_packet_available = true; |
475 } | 505 } |
476 | 506 |
477 // This is the main simulation loop. | 507 // This is the main simulation loop. |
478 // Set the simulation clock to start immediately with the first packet. | 508 // Set the simulation clock to start immediately with the first packet. |
479 int start_time_ms = packet->time_ms(); | 509 int64_t start_time_ms = rtc::checked_cast<int64_t>(packet->time_ms()); |
480 int time_now_ms = packet->time_ms(); | 510 int64_t time_now_ms = start_time_ms; |
481 int next_input_time_ms = time_now_ms; | 511 int64_t next_input_time_ms = time_now_ms; |
482 int next_output_time_ms = time_now_ms; | 512 int64_t next_output_time_ms = time_now_ms; |
483 if (time_now_ms % kOutputBlockSizeMs != 0) { | 513 if (time_now_ms % kOutputBlockSizeMs != 0) { |
484 // Make sure that next_output_time_ms is rounded up to the next multiple | 514 // Make sure that next_output_time_ms is rounded up to the next multiple |
485 // of kOutputBlockSizeMs. (Legacy bit-exactness.) | 515 // of kOutputBlockSizeMs. (Legacy bit-exactness.) |
486 next_output_time_ms += | 516 next_output_time_ms += |
487 kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; | 517 kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; |
488 } | 518 } |
489 while (packet_available) { | 519 |
| 520 bool packet_available = true; |
| 521 bool output_event_available = true; |
| 522 if (!is_rtp_dump) { |
| 523 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); |
| 524 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) |
| 525 output_event_available = false; |
| 526 start_time_ms = time_now_ms = |
| 527 std::min(next_input_time_ms, next_output_time_ms); |
| 528 } |
| 529 while (packet_available || output_event_available) { |
| 530 // Advance time to next event. |
| 531 time_now_ms = std::min(next_input_time_ms, next_output_time_ms); |
490 // Check if it is time to insert packet. | 532 // Check if it is time to insert packet. |
491 while (time_now_ms >= next_input_time_ms && packet_available) { | 533 while (time_now_ms >= next_input_time_ms && packet_available) { |
492 assert(packet->virtual_payload_length_bytes() > 0); | 534 assert(packet->virtual_payload_length_bytes() > 0); |
493 // Parse RTP header. | 535 // Parse RTP header. |
494 WebRtcRTPHeader rtp_header; | 536 WebRtcRTPHeader rtp_header; |
495 packet->ConvertHeader(&rtp_header); | 537 packet->ConvertHeader(&rtp_header); |
496 const uint8_t* payload_ptr = packet->payload(); | 538 const uint8_t* payload_ptr = packet->payload(); |
497 size_t payload_len = packet->payload_length_bytes(); | 539 size_t payload_len = packet->payload_length_bytes(); |
498 if (replace_payload) { | 540 if (replace_payload) { |
499 payload_len = ReplacePayload(replacement_audio_file.get(), | 541 payload_len = ReplacePayload(replacement_audio_file.get(), |
500 &replacement_audio, | 542 &replacement_audio, |
501 &payload, | 543 &payload, |
502 &payload_mem_size_bytes, | 544 &payload_mem_size_bytes, |
503 &input_frame_size_timestamps, | 545 &input_frame_size_timestamps, |
504 &rtp_header, | 546 &rtp_header, |
505 next_packet.get()); | 547 next_packet.get()); |
506 payload_ptr = payload.get(); | 548 payload_ptr = payload.get(); |
507 } | 549 } |
508 int error = | 550 int error = neteq->InsertPacket( |
509 neteq->InsertPacket(rtp_header, | 551 rtp_header, payload_ptr, payload_len, |
510 payload_ptr, | 552 static_cast<uint32_t>(packet->time_ms() * sample_rate_hz / 1000)); |
511 payload_len, | |
512 packet->time_ms() * sample_rate_hz / 1000); | |
513 if (error != NetEq::kOK) { | 553 if (error != NetEq::kOK) { |
514 if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { | 554 if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { |
515 std::cerr << "RTP Payload type " | 555 std::cerr << "RTP Payload type " |
516 << static_cast<int>(rtp_header.header.payloadType) | 556 << static_cast<int>(rtp_header.header.payloadType) |
517 << " is unknown." << std::endl; | 557 << " is unknown." << std::endl; |
518 std::cerr << "Use --codec_map to view default mapping." << std::endl; | 558 std::cerr << "Use --codec_map to view default mapping." << std::endl; |
519 std::cerr << "Use --helpshort for information on how to make custom " | 559 std::cerr << "Use --helpshort for information on how to make custom " |
520 "mappings." << std::endl; | 560 "mappings." << std::endl; |
521 } else { | 561 } else { |
522 std::cerr << "InsertPacket returned error code " << neteq->LastError() | 562 std::cerr << "InsertPacket returned error code " << neteq->LastError() |
523 << std::endl; | 563 << std::endl; |
524 std::cerr << "Header data:" << std::endl; | 564 std::cerr << "Header data:" << std::endl; |
525 std::cerr << " PT = " | 565 std::cerr << " PT = " |
526 << static_cast<int>(rtp_header.header.payloadType) | 566 << static_cast<int>(rtp_header.header.payloadType) |
527 << std::endl; | 567 << std::endl; |
528 std::cerr << " SN = " << rtp_header.header.sequenceNumber | 568 std::cerr << " SN = " << rtp_header.header.sequenceNumber |
529 << std::endl; | 569 << std::endl; |
530 std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; | 570 std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; |
531 } | 571 } |
532 } | 572 } |
533 | 573 |
534 // Get next packet from file. | 574 // Get next packet from file. |
535 webrtc::test::Packet* temp_packet = file_source->NextPacket(); | 575 webrtc::test::Packet* temp_packet = file_source->NextPacket(); |
536 if (temp_packet) { | 576 if (temp_packet) { |
537 packet.reset(temp_packet); | 577 packet.reset(temp_packet); |
| 578 if (replace_payload) { |
| 579 // At this point |packet| contains the packet *after* |next_packet|. |
| 580 // Swap Packet objects between |packet| and |next_packet|. |
| 581 packet.swap(next_packet); |
| 582 // Swap the status indicators unless they're already the same. |
| 583 if (packet_available != next_packet_available) { |
| 584 packet_available = !packet_available; |
| 585 next_packet_available = !next_packet_available; |
| 586 } |
| 587 } |
| 588 next_input_time_ms = rtc::checked_cast<int64_t>(packet->time_ms()); |
538 } else { | 589 } else { |
| 590 // Set next input time to the maximum value of int64_t to prevent the |
| 591 // time_now_ms from becoming stuck at the final value. |
| 592 next_input_time_ms = std::numeric_limits<int64_t>::max(); |
539 packet_available = false; | 593 packet_available = false; |
540 } | 594 } |
541 if (replace_payload) { | |
542 // At this point |packet| contains the packet *after* |next_packet|. | |
543 // Swap Packet objects between |packet| and |next_packet|. | |
544 packet.swap(next_packet); | |
545 // Swap the status indicators unless they're already the same. | |
546 if (packet_available != next_packet_available) { | |
547 packet_available = !packet_available; | |
548 next_packet_available = !next_packet_available; | |
549 } | |
550 } | |
551 next_input_time_ms = packet->time_ms(); | |
552 } | 595 } |
553 | 596 |
554 // Check if it is time to get output audio. | 597 // Check if it is time to get output audio. |
555 if (time_now_ms >= next_output_time_ms) { | 598 while (time_now_ms >= next_output_time_ms && output_event_available) { |
556 static const size_t kOutDataLen = | 599 static const size_t kOutDataLen = |
557 kOutputBlockSizeMs * kMaxSamplesPerMs * kMaxChannels; | 600 kOutputBlockSizeMs * kMaxSamplesPerMs * kMaxChannels; |
558 int16_t out_data[kOutDataLen]; | 601 int16_t out_data[kOutDataLen]; |
559 int num_channels; | 602 int num_channels; |
560 size_t samples_per_channel; | 603 size_t samples_per_channel; |
561 int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, | 604 int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, |
562 &num_channels, NULL); | 605 &num_channels, NULL); |
563 if (error != NetEq::kOK) { | 606 if (error != NetEq::kOK) { |
564 std::cerr << "GetAudio returned error code " << | 607 std::cerr << "GetAudio returned error code " << |
565 neteq->LastError() << std::endl; | 608 neteq->LastError() << std::endl; |
566 } else { | 609 } else { |
567 // Calculate sample rate from output size. | 610 // Calculate sample rate from output size. |
568 sample_rate_hz = rtc::checked_cast<int>( | 611 sample_rate_hz = rtc::checked_cast<int>( |
569 1000 * samples_per_channel / kOutputBlockSizeMs); | 612 1000 * samples_per_channel / kOutputBlockSizeMs); |
570 } | 613 } |
571 | 614 |
572 // Write to file. | 615 // Write to file. |
573 // TODO(hlundin): Make writing to file optional. | 616 // TODO(hlundin): Make writing to file optional. |
574 size_t write_len = samples_per_channel * num_channels; | 617 size_t write_len = samples_per_channel * num_channels; |
575 if (!output->WriteArray(out_data, write_len)) { | 618 if (!output->WriteArray(out_data, write_len)) { |
576 std::cerr << "Error while writing to file" << std::endl; | 619 std::cerr << "Error while writing to file" << std::endl; |
577 webrtc::Trace::ReturnTrace(); | 620 webrtc::Trace::ReturnTrace(); |
578 exit(1); | 621 exit(1); |
579 } | 622 } |
580 next_output_time_ms += kOutputBlockSizeMs; | 623 if (is_rtp_dump) { |
| 624 next_output_time_ms += kOutputBlockSizeMs; |
| 625 if (!packet_available) |
| 626 output_event_available = false; |
| 627 } else { |
| 628 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); |
| 629 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) |
| 630 output_event_available = false; |
| 631 } |
581 } | 632 } |
582 // Advance time to next event. | |
583 time_now_ms = std::min(next_input_time_ms, next_output_time_ms); | |
584 } | 633 } |
585 | |
586 printf("Simulation done\n"); | 634 printf("Simulation done\n"); |
587 printf("Produced %i ms of audio\n", time_now_ms - start_time_ms); | 635 printf("Produced %i ms of audio\n", |
| 636 static_cast<int>(time_now_ms - start_time_ms)); |
588 | 637 |
589 delete neteq; | 638 delete neteq; |
590 webrtc::Trace::ReturnTrace(); | 639 webrtc::Trace::ReturnTrace(); |
591 return 0; | 640 return 0; |
592 } | 641 } |
OLD | NEW |