1/*
2 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
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
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_baselinefile.h"
12
13#include <stdio.h>
14
15#include <algorithm>
16#include <vector>
17
18#include "webrtc/base/constructormagic.h"
19#include "webrtc/base/scoped_ptr.h"
20#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_fileutils.h"
21#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
22#include "webrtc/test/testsupport/fileutils.h"
23
24namespace webrtc {
25namespace testing {
26namespace bwe {
27
28// The format of BWE test baseline files is extremely simple:
29//   1. All read/written entities are 32-bit unsigned integers in network byte
30//      order (Big Endian).
31//   2. Files beging with a 2 word header containing a magic marker and file
32//      format version indicator. The Magic marker reads "BWE!" in a hex dump.
33//   3. Each estimate is logged as a pair of words: time in milliseconds and
34//      estimated bit rate, in bits per second.
35const uint32_t kMagicMarker = 0x42574521;
36const uint32_t kFileVersion1 = 0x00000001;
37const char kResourceSubDir[] = "remote_bitrate_estimator";
38
39class BaseLineFileVerify : public BaseLineFileInterface {
40 public:
41  // If |allow_missing_file| is set, VerifyOrWrite() will return true even if
42  // the baseline file is missing. This is the default when verifying files, but
43  // not when updating (i.e. we always write it out if missing).
44  BaseLineFileVerify(const std::string& filepath, bool allow_missing_file)
45      : reader_(),
46        fail_to_read_response_(false) {
47    rtc::scoped_ptr<ResourceFileReader> reader;
48    reader.reset(ResourceFileReader::Create(filepath, "bin"));
49    if (!reader.get()) {
50      printf("WARNING: Missing baseline file for BWE test: %s.bin\n",
51             filepath.c_str());
52      fail_to_read_response_ = allow_missing_file;
53    } else {
54      uint32_t magic_marker = 0;
55      uint32_t file_version = 0;
56      if (reader->Read(&magic_marker) && magic_marker == kMagicMarker &&
57          reader->Read(&file_version) && file_version == kFileVersion1) {
58        reader_.swap(reader);
59      } else {
60        printf("WARNING: Bad baseline file header for BWE test: %s.bin\n",
61               filepath.c_str());
62      }
63    }
64  }
65  virtual ~BaseLineFileVerify() {}
66
67  virtual void Estimate(int64_t time_ms, uint32_t estimate_bps) {
68    if (reader_.get()) {
69      uint32_t read_ms = 0;
70      uint32_t read_bps = 0;
71      if (reader_->Read(&read_ms) && read_ms == time_ms &&
72          reader_->Read(&read_bps) && read_bps == estimate_bps) {
73      } else {
74        printf("ERROR: Baseline differs starting at: %d ms (%d vs %d)!\n",
75            static_cast<uint32_t>(time_ms), estimate_bps, read_bps);
76        reader_.reset(NULL);
77      }
78    }
79  }
80
81  virtual bool VerifyOrWrite() {
82    if (reader_.get()) {
83      if (reader_->IsAtEnd()) {
84        return true;
85      } else {
86        printf("ERROR: Baseline file contains more data!\n");
87        return false;
88      }
89    }
90    return fail_to_read_response_;
91  }
92
93 private:
94  rtc::scoped_ptr<ResourceFileReader> reader_;
95  bool fail_to_read_response_;
96
97  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseLineFileVerify);
98};
99
100class BaseLineFileUpdate : public BaseLineFileInterface {
101 public:
102  BaseLineFileUpdate(const std::string& filepath,
103                     BaseLineFileInterface* verifier)
104      : verifier_(verifier),
105        output_content_(),
106        filepath_(filepath) {
107    output_content_.push_back(kMagicMarker);
108    output_content_.push_back(kFileVersion1);
109  }
110  virtual ~BaseLineFileUpdate() {}
111
112  virtual void Estimate(int64_t time_ms, uint32_t estimate_bps) {
113    verifier_->Estimate(time_ms, estimate_bps);
114    output_content_.push_back(static_cast<uint32_t>(time_ms));
115    output_content_.push_back(estimate_bps);
116  }
117
118  virtual bool VerifyOrWrite() {
119    if (!verifier_->VerifyOrWrite()) {
120      std::string dir_path = webrtc::test::OutputPath() + kResourceSubDir;
121      if (!webrtc::test::CreateDir(dir_path)) {
122        printf("WARNING: Cannot create output dir: %s\n", dir_path.c_str());
123        return false;
124      }
125      rtc::scoped_ptr<OutputFileWriter> writer;
126      writer.reset(OutputFileWriter::Create(filepath_, "bin"));
127      if (!writer.get()) {
128        printf("WARNING: Cannot create output file: %s.bin\n",
129            filepath_.c_str());
130        return false;
131      }
132      printf("NOTE: Writing baseline file for BWE test: %s.bin\n",
133             filepath_.c_str());
134      for (std::vector<uint32_t>::iterator it = output_content_.begin();
135          it != output_content_.end(); ++it) {
136        writer->Write(*it);
137      }
138      return true;
139    }
140    printf("NOTE: No change, not writing: %s\n", filepath_.c_str());
141    return true;
142  }
143
144 private:
145  rtc::scoped_ptr<BaseLineFileInterface> verifier_;
146  std::vector<uint32_t> output_content_;
147  std::string filepath_;
148
149  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseLineFileUpdate);
150};
151
152BaseLineFileInterface* BaseLineFileInterface::Create(
153    const std::string& filename, bool write_output_file) {
154  std::string filepath = filename;
155  std::replace(filepath.begin(), filepath.end(), '/', '_');
156  filepath = std::string(kResourceSubDir) + "/" + filepath;
157
158  rtc::scoped_ptr<BaseLineFileInterface> result;
159  result.reset(new BaseLineFileVerify(filepath, !write_output_file));
160  if (write_output_file) {
161    // Takes ownership of the |verifier| instance.
162    result.reset(new BaseLineFileUpdate(filepath, result.release()));
163  }
164  return result.release();
165}
166}  // namespace bwe
167}  // namespace testing
168}  // namespace webrtc
169