• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2019 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 "test/pc/e2e/analyzer/video/single_process_encoded_image_data_injector.h"
12 
13 #include <algorithm>
14 #include <cstddef>
15 
16 #include "absl/memory/memory.h"
17 #include "api/video/encoded_image.h"
18 #include "rtc_base/checks.h"
19 
20 namespace webrtc {
21 namespace webrtc_pc_e2e {
22 
23 SingleProcessEncodedImageDataInjector::SingleProcessEncodedImageDataInjector() =
24     default;
25 SingleProcessEncodedImageDataInjector::
26     ~SingleProcessEncodedImageDataInjector() = default;
27 
InjectData(uint16_t id,bool discard,const EncodedImage & source)28 EncodedImage SingleProcessEncodedImageDataInjector::InjectData(
29     uint16_t id,
30     bool discard,
31     const EncodedImage& source) {
32   RTC_CHECK(source.size() >= ExtractionInfo::kUsedBufferSize);
33 
34   ExtractionInfo info;
35   info.discard = discard;
36   size_t insertion_pos = source.size() - ExtractionInfo::kUsedBufferSize;
37   memcpy(info.origin_data, &source.data()[insertion_pos],
38          ExtractionInfo::kUsedBufferSize);
39   {
40     MutexLock lock(&lock_);
41     // Will create new one if missed.
42     ExtractionInfoVector& ev = extraction_cache_[id];
43     info.sub_id = ev.next_sub_id++;
44     ev.infos[info.sub_id] = info;
45   }
46 
47   auto buffer = EncodedImageBuffer::Create(source.data(), source.size());
48   buffer->data()[insertion_pos] = id & 0x00ff;
49   buffer->data()[insertion_pos + 1] = (id & 0xff00) >> 8;
50   buffer->data()[insertion_pos + 2] = info.sub_id;
51 
52   EncodedImage out = source;
53   out.SetEncodedData(buffer);
54   return out;
55 }
56 
AddParticipantInCall()57 void SingleProcessEncodedImageDataInjector::AddParticipantInCall() {
58   MutexLock crit(&lock_);
59   expected_receivers_count_++;
60 }
61 
RemoveParticipantInCall()62 void SingleProcessEncodedImageDataInjector::RemoveParticipantInCall() {
63   MutexLock crit(&lock_);
64   expected_receivers_count_--;
65   // Now we need go over `extraction_cache_` and removed frames which have been
66   // received by `expected_receivers_count_`.
67   for (auto& [frame_id, extraction_infos] : extraction_cache_) {
68     for (auto it = extraction_infos.infos.begin();
69          it != extraction_infos.infos.end();) {
70       // Frame is received if `received_count` equals to
71       // `expected_receivers_count_`.
72       if (it->second.received_count == expected_receivers_count_) {
73         it = extraction_infos.infos.erase(it);
74       } else {
75         ++it;
76       }
77     }
78   }
79 }
80 
ExtractData(const EncodedImage & source)81 EncodedImageExtractionResult SingleProcessEncodedImageDataInjector::ExtractData(
82     const EncodedImage& source) {
83   size_t size = source.size();
84   auto buffer = EncodedImageBuffer::Create(source.data(), source.size());
85   EncodedImage out = source;
86   out.SetEncodedData(buffer);
87 
88   std::vector<size_t> frame_sizes;
89   std::vector<size_t> frame_sl_index;
90   size_t max_spatial_index = out.SpatialIndex().value_or(0);
91   for (size_t i = 0; i <= max_spatial_index; ++i) {
92     auto frame_size = source.SpatialLayerFrameSize(i);
93     if (frame_size.value_or(0)) {
94       frame_sl_index.push_back(i);
95       frame_sizes.push_back(frame_size.value());
96     }
97   }
98   if (frame_sizes.empty()) {
99     frame_sizes.push_back(size);
100   }
101 
102   size_t prev_frames_size = 0;
103   absl::optional<uint16_t> id = absl::nullopt;
104   bool discard = true;
105   std::vector<ExtractionInfo> extraction_infos;
106   for (size_t frame_size : frame_sizes) {
107     size_t insertion_pos =
108         prev_frames_size + frame_size - ExtractionInfo::kUsedBufferSize;
109     // Extract frame id from first 2 bytes starting from insertion pos.
110     uint16_t next_id = buffer->data()[insertion_pos] +
111                        (buffer->data()[insertion_pos + 1] << 8);
112     // Extract frame sub id from second 3 byte starting from insertion pos.
113     uint8_t sub_id = buffer->data()[insertion_pos + 2];
114     RTC_CHECK(!id || *id == next_id)
115         << "Different frames encoded into single encoded image: " << *id
116         << " vs " << next_id;
117     id = next_id;
118     ExtractionInfo info;
119     {
120       MutexLock lock(&lock_);
121       auto ext_vector_it = extraction_cache_.find(next_id);
122       RTC_CHECK(ext_vector_it != extraction_cache_.end())
123           << "Unknown frame_id=" << next_id;
124 
125       auto info_it = ext_vector_it->second.infos.find(sub_id);
126       RTC_CHECK(info_it != ext_vector_it->second.infos.end())
127           << "Unknown sub_id=" << sub_id << " for frame_id=" << next_id;
128       info_it->second.received_count++;
129       info = info_it->second;
130       if (info.received_count == expected_receivers_count_) {
131         ext_vector_it->second.infos.erase(info_it);
132       }
133     }
134     // We need to discard encoded image only if all concatenated encoded images
135     // have to be discarded.
136     discard = discard && info.discard;
137 
138     extraction_infos.push_back(info);
139     prev_frames_size += frame_size;
140   }
141   RTC_CHECK(id);
142 
143   if (discard) {
144     out.set_size(0);
145     for (size_t i = 0; i <= max_spatial_index; ++i) {
146       out.SetSpatialLayerFrameSize(i, 0);
147     }
148     return EncodedImageExtractionResult{*id, out, true};
149   }
150 
151   // Make a pass from begin to end to restore origin payload and erase discarded
152   // encoded images.
153   size_t pos = 0;
154   for (size_t frame_index = 0; frame_index < frame_sizes.size();
155        ++frame_index) {
156     RTC_CHECK(pos < size);
157     const size_t frame_size = frame_sizes[frame_index];
158     const ExtractionInfo& info = extraction_infos[frame_index];
159     if (info.discard) {
160       // If this encoded image is marked to be discarded - erase it's payload
161       // from the buffer.
162       memmove(&buffer->data()[pos], &buffer->data()[pos + frame_size],
163               size - pos - frame_size);
164       RTC_CHECK_LT(frame_index, frame_sl_index.size())
165           << "codec doesn't support discard option or the image, that was "
166              "supposed to be discarded, is lost";
167       out.SetSpatialLayerFrameSize(frame_sl_index[frame_index], 0);
168       size -= frame_size;
169     } else {
170       memcpy(
171           &buffer->data()[pos + frame_size - ExtractionInfo::kUsedBufferSize],
172           info.origin_data, ExtractionInfo::kUsedBufferSize);
173       pos += frame_size;
174     }
175   }
176   out.set_size(pos);
177 
178   return EncodedImageExtractionResult{*id, out, discard};
179 }
180 
181 SingleProcessEncodedImageDataInjector::ExtractionInfoVector::
182     ExtractionInfoVector() = default;
183 SingleProcessEncodedImageDataInjector::ExtractionInfoVector::
184     ~ExtractionInfoVector() = default;
185 
186 }  // namespace webrtc_pc_e2e
187 }  // namespace webrtc
188