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/scoped_ptr.h" | 27 #include "webrtc/base/scoped_ptr.h" |
27 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" | 28 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" |
28 #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" | 29 #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" |
29 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" | 30 #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" |
30 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" | 31 #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" |
31 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" | 32 #include "webrtc/modules/audio_coding/neteq/tools/output_wav_file.h" |
32 #include "webrtc/modules/audio_coding/neteq/tools/packet.h" | 33 #include "webrtc/modules/audio_coding/neteq/tools/packet.h" |
34 #include "webrtc/modules/audio_coding/neteq/tools/rtc_event_log_source.h" | |
33 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" | 35 #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" |
34 #include "webrtc/modules/interface/module_common_types.h" | 36 #include "webrtc/modules/interface/module_common_types.h" |
35 #include "webrtc/system_wrappers/interface/trace.h" | 37 #include "webrtc/system_wrappers/interface/trace.h" |
36 #include "webrtc/test/testsupport/fileutils.h" | 38 #include "webrtc/test/testsupport/fileutils.h" |
37 #include "webrtc/typedefs.h" | 39 #include "webrtc/typedefs.h" |
38 | 40 |
39 using webrtc::NetEq; | 41 using webrtc::NetEq; |
40 using webrtc::WebRtcRTPHeader; | 42 using webrtc::WebRtcRTPHeader; |
41 | 43 |
42 namespace { | 44 namespace { |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
377 if (FLAGS_codec_map) { | 379 if (FLAGS_codec_map) { |
378 // We have already printed the codec map. Just end the program. | 380 // We have already printed the codec map. Just end the program. |
379 return 0; | 381 return 0; |
380 } | 382 } |
381 // Print usage information. | 383 // Print usage information. |
382 std::cout << google::ProgramUsage(); | 384 std::cout << google::ProgramUsage(); |
383 return 0; | 385 return 0; |
384 } | 386 } |
385 | 387 |
386 printf("Input file: %s\n", argv[1]); | 388 printf("Input file: %s\n", argv[1]); |
387 rtc::scoped_ptr<webrtc::test::RtpFileSource> file_source( | 389 |
388 webrtc::test::RtpFileSource::Create(argv[1])); | 390 // Check if the magic string "#!rtpplay1.0" appears at the beginning of the |
minyue-webrtc
2015/09/01 11:48:35
nice!
hlundin-webrtc
2015/09/02 08:48:51
Yes, but I suggest you also add RTPencode as a mag
ivoc
2015/09/03 08:01:31
Ah, I did not know that. Thanks.
| |
391 // file. If it does, the input file is an RTPdump file, otherwise we will | |
392 // assume it is an RtcEventLog file. | |
393 bool is_rtp_dump = false; | |
hlundin-webrtc
2015/09/02 08:48:51
This is a lot of extra code to check what is essen
ivoc
2015/09/03 08:01:31
You're right, that does seem like a nicer approach
| |
394 { | |
395 const char magic_string[] = "#!rtpplay1.0"; | |
396 const size_t magic_string_size = sizeof(magic_string) - 1; | |
397 char first_bytes[magic_string_size]; | |
398 FILE* input_file = fopen(argv[1], "r"); | |
399 if (input_file) { | |
400 size_t bytes_read = fread(first_bytes, 1, magic_string_size, input_file); | |
401 if (bytes_read == magic_string_size) | |
402 if (strncmp(magic_string, first_bytes, magic_string_size) == 0) | |
403 is_rtp_dump = true; | |
404 fclose(input_file); | |
405 } else { | |
406 std::cerr << "Unable to read input file." << std::endl; | |
407 return -1; | |
408 } | |
409 } | |
410 rtc::scoped_ptr<webrtc::test::PacketSource> file_source; | |
411 webrtc::test::RtcEventLogSource* event_log_source = nullptr; | |
412 if (is_rtp_dump) { | |
413 file_source.reset(webrtc::test::RtpFileSource::Create(argv[1])); | |
414 } else { | |
415 event_log_source = webrtc::test::RtcEventLogSource::Create(argv[1]); | |
416 file_source.reset(event_log_source); | |
417 } | |
418 | |
389 assert(file_source.get()); | 419 assert(file_source.get()); |
390 | 420 |
391 // Check if an SSRC value was provided. | 421 // Check if an SSRC value was provided. |
392 if (!FLAGS_ssrc.empty()) { | 422 if (!FLAGS_ssrc.empty()) { |
393 uint32_t ssrc; | 423 uint32_t ssrc; |
394 CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; | 424 CHECK(ParseSsrc(FLAGS_ssrc, &ssrc)) << "Flag verification has failed."; |
395 file_source->SelectSsrc(ssrc); | 425 file_source->SelectSsrc(ssrc); |
396 } | 426 } |
397 | 427 |
398 // Check if a replacement audio file was provided, and if so, open it. | 428 // Check if a replacement audio file was provided, and if so, open it. |
399 bool replace_payload = false; | 429 bool replace_payload = false; |
400 rtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file; | 430 rtc::scoped_ptr<webrtc::test::InputAudioFile> replacement_audio_file; |
401 if (!FLAGS_replacement_audio_file.empty()) { | 431 if (!FLAGS_replacement_audio_file.empty()) { |
402 replacement_audio_file.reset( | 432 replacement_audio_file.reset( |
403 new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); | 433 new webrtc::test::InputAudioFile(FLAGS_replacement_audio_file)); |
404 replace_payload = true; | 434 replace_payload = true; |
405 } | 435 } |
406 | 436 |
407 // Read first packet. | 437 // Read first packet. |
408 rtc::scoped_ptr<webrtc::test::Packet> packet(file_source->NextPacket()); | 438 rtc::scoped_ptr<webrtc::test::Packet> packet(file_source->NextPacket()); |
409 if (!packet) { | 439 if (!packet) { |
410 printf( | 440 printf( |
411 "Warning: input file is empty, or the filters did not match any " | 441 "Warning: input file is empty, or the filters did not match any " |
412 "packets\n"); | 442 "packets\n"); |
413 webrtc::Trace::ReturnTrace(); | 443 webrtc::Trace::ReturnTrace(); |
414 return 0; | 444 return 0; |
415 } | 445 } |
416 bool packet_available = true; | |
417 | 446 |
418 // Check the sample rate. | 447 // Check the sample rate. |
419 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); | 448 int sample_rate_hz = CodecSampleRate(packet->header().payloadType); |
420 if (sample_rate_hz <= 0) { | 449 if (sample_rate_hz <= 0) { |
421 printf("Warning: Invalid sample rate from RTP packet.\n"); | 450 printf("Warning: Invalid sample rate from RTP packet.\n"); |
422 webrtc::Trace::ReturnTrace(); | 451 webrtc::Trace::ReturnTrace(); |
423 return 0; | 452 return 0; |
424 } | 453 } |
425 | 454 |
426 // Open the output file now that we know the sample rate. (Rate is only needed | 455 // 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... | |
468 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); | 497 replacement_audio.reset(new int16_t[input_frame_size_timestamps]); |
469 payload_mem_size_bytes = 2 * input_frame_size_timestamps; | 498 payload_mem_size_bytes = 2 * input_frame_size_timestamps; |
470 payload.reset(new uint8_t[payload_mem_size_bytes]); | 499 payload.reset(new uint8_t[payload_mem_size_bytes]); |
471 next_packet.reset(file_source->NextPacket()); | 500 next_packet.reset(file_source->NextPacket()); |
472 assert(next_packet); | 501 assert(next_packet); |
473 next_packet_available = true; | 502 next_packet_available = true; |
474 } | 503 } |
475 | 504 |
476 // This is the main simulation loop. | 505 // This is the main simulation loop. |
477 // Set the simulation clock to start immediately with the first packet. | 506 // Set the simulation clock to start immediately with the first packet. |
478 int start_time_ms = packet->time_ms(); | 507 int64_t start_time_ms = packet->time_ms(); |
hlundin-webrtc
2015/09/02 08:48:51
packet->time_ms() actually returns a double. Sugge
ivoc
2015/09/03 08:01:31
Done.
| |
479 int time_now_ms = packet->time_ms(); | 508 int64_t time_now_ms = packet->time_ms(); |
480 int next_input_time_ms = time_now_ms; | 509 int64_t next_input_time_ms = time_now_ms; |
481 int next_output_time_ms = time_now_ms; | 510 int64_t next_output_time_ms = time_now_ms; |
482 if (time_now_ms % kOutputBlockSizeMs != 0) { | 511 if (time_now_ms % kOutputBlockSizeMs != 0) { |
483 // Make sure that next_output_time_ms is rounded up to the next multiple | 512 // Make sure that next_output_time_ms is rounded up to the next multiple |
484 // of kOutputBlockSizeMs. (Legacy bit-exactness.) | 513 // of kOutputBlockSizeMs. (Legacy bit-exactness.) |
485 next_output_time_ms += | 514 next_output_time_ms += |
486 kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; | 515 kOutputBlockSizeMs - time_now_ms % kOutputBlockSizeMs; |
487 } | 516 } |
488 while (packet_available) { | 517 |
518 bool packet_available = true; | |
519 bool output_event_available = true; | |
520 if (!is_rtp_dump) { | |
521 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); | |
522 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) | |
523 output_event_available = false; | |
524 start_time_ms = time_now_ms = | |
525 std::min(next_input_time_ms, next_output_time_ms); | |
526 } | |
527 while (packet_available || output_event_available) { | |
528 // Advance time to next event. | |
529 time_now_ms = std::min(next_input_time_ms, next_output_time_ms); | |
489 // Check if it is time to insert packet. | 530 // Check if it is time to insert packet. |
490 while (time_now_ms >= next_input_time_ms && packet_available) { | 531 while (time_now_ms >= next_input_time_ms && packet_available) { |
491 assert(packet->virtual_payload_length_bytes() > 0); | 532 assert(packet->virtual_payload_length_bytes() > 0); |
492 // Parse RTP header. | 533 // Parse RTP header. |
493 WebRtcRTPHeader rtp_header; | 534 WebRtcRTPHeader rtp_header; |
494 packet->ConvertHeader(&rtp_header); | 535 packet->ConvertHeader(&rtp_header); |
495 const uint8_t* payload_ptr = packet->payload(); | 536 const uint8_t* payload_ptr = packet->payload(); |
minyue-webrtc
2015/09/01 11:48:35
Not very related to this CL, just curious.
If pac
hlundin-webrtc
2015/09/02 08:48:51
There will be some errors from NetEq, but it is pr
ivoc
2015/09/03 08:01:31
I added a check for this higher up in the code, on
| |
496 size_t payload_len = packet->payload_length_bytes(); | 537 size_t payload_len = packet->payload_length_bytes(); |
497 if (replace_payload) { | 538 if (replace_payload) { |
498 payload_len = ReplacePayload(replacement_audio_file.get(), | 539 payload_len = ReplacePayload(replacement_audio_file.get(), |
499 &replacement_audio, | 540 &replacement_audio, |
500 &payload, | 541 &payload, |
501 &payload_mem_size_bytes, | 542 &payload_mem_size_bytes, |
502 &input_frame_size_timestamps, | 543 &input_frame_size_timestamps, |
503 &rtp_header, | 544 &rtp_header, |
504 next_packet.get()); | 545 next_packet.get()); |
505 payload_ptr = payload.get(); | 546 payload_ptr = payload.get(); |
(...skipping 21 matching lines...) Expand all Loading... | |
527 std::cerr << " SN = " << rtp_header.header.sequenceNumber | 568 std::cerr << " SN = " << rtp_header.header.sequenceNumber |
528 << std::endl; | 569 << std::endl; |
529 std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; | 570 std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; |
530 } | 571 } |
531 } | 572 } |
532 | 573 |
533 // Get next packet from file. | 574 // Get next packet from file. |
534 webrtc::test::Packet* temp_packet = file_source->NextPacket(); | 575 webrtc::test::Packet* temp_packet = file_source->NextPacket(); |
535 if (temp_packet) { | 576 if (temp_packet) { |
536 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 = packet->time_ms(); | |
hlundin-webrtc
2015/09/02 08:48:51
checked_cast here too
ivoc
2015/09/03 08:01:31
Added. I also added one in the call to neteq->Inse
| |
537 } 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(); | |
538 packet_available = false; | 593 packet_available = false; |
539 } | 594 } |
540 if (replace_payload) { | |
541 // At this point |packet| contains the packet *after* |next_packet|. | |
542 // Swap Packet objects between |packet| and |next_packet|. | |
543 packet.swap(next_packet); | |
544 // Swap the status indicators unless they're already the same. | |
545 if (packet_available != next_packet_available) { | |
546 packet_available = !packet_available; | |
547 next_packet_available = !next_packet_available; | |
548 } | |
549 } | |
550 next_input_time_ms = packet->time_ms(); | |
551 } | 595 } |
552 | 596 |
553 // Check if it is time to get output audio. | 597 // Check if it is time to get output audio. |
554 if (time_now_ms >= next_output_time_ms) { | 598 while (time_now_ms >= next_output_time_ms && output_event_available) { |
555 static const int kOutDataLen = | 599 static const int kOutDataLen = |
556 kOutputBlockSizeMs * kMaxSamplesPerMs * kMaxChannels; | 600 kOutputBlockSizeMs * kMaxSamplesPerMs * kMaxChannels; |
557 int16_t out_data[kOutDataLen]; | 601 int16_t out_data[kOutDataLen]; |
558 int num_channels; | 602 int num_channels; |
559 int samples_per_channel; | 603 int samples_per_channel; |
560 int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, | 604 int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, |
561 &num_channels, NULL); | 605 &num_channels, NULL); |
562 if (error != NetEq::kOK) { | 606 if (error != NetEq::kOK) { |
563 std::cerr << "GetAudio returned error code " << | 607 std::cerr << "GetAudio returned error code " << |
564 neteq->LastError() << std::endl; | 608 neteq->LastError() << std::endl; |
565 } else { | 609 } else { |
566 // Calculate sample rate from output size. | 610 // Calculate sample rate from output size. |
567 sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs; | 611 sample_rate_hz = 1000 * samples_per_channel / kOutputBlockSizeMs; |
568 } | 612 } |
569 | 613 |
570 // Write to file. | 614 // Write to file. |
571 // TODO(hlundin): Make writing to file optional. | 615 // TODO(hlundin): Make writing to file optional. |
572 size_t write_len = samples_per_channel * num_channels; | 616 size_t write_len = samples_per_channel * num_channels; |
573 if (!output->WriteArray(out_data, write_len)) { | 617 if (!output->WriteArray(out_data, write_len)) { |
574 std::cerr << "Error while writing to file" << std::endl; | 618 std::cerr << "Error while writing to file" << std::endl; |
575 webrtc::Trace::ReturnTrace(); | 619 webrtc::Trace::ReturnTrace(); |
576 exit(1); | 620 exit(1); |
577 } | 621 } |
578 next_output_time_ms += kOutputBlockSizeMs; | 622 if (is_rtp_dump) { |
623 next_output_time_ms += kOutputBlockSizeMs; | |
624 if (!packet_available) | |
625 output_event_available = false; | |
626 } else { | |
627 next_output_time_ms = event_log_source->NextAudioOutputEventMs(); | |
628 if (next_output_time_ms == std::numeric_limits<int64_t>::max()) | |
629 output_event_available = false; | |
630 } | |
579 } | 631 } |
580 // Advance time to next event. | |
581 time_now_ms = std::min(next_input_time_ms, next_output_time_ms); | |
582 } | 632 } |
583 | |
584 printf("Simulation done\n"); | 633 printf("Simulation done\n"); |
585 printf("Produced %i ms of audio\n", time_now_ms - start_time_ms); | 634 printf("Produced %i ms of audio\n", |
635 static_cast<int>(time_now_ms - start_time_ms)); | |
586 | 636 |
587 delete neteq; | 637 delete neteq; |
588 webrtc::Trace::ReturnTrace(); | 638 webrtc::Trace::ReturnTrace(); |
589 return 0; | 639 return 0; |
590 } | 640 } |
OLD | NEW |