OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 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 #include "webrtc/modules/video_coding/utility/ivf_file_writer.h" | 11 #include "webrtc/modules/video_coding/utility/ivf_file_writer.h" |
12 | 12 |
| 13 #include <string> |
| 14 #include <utility> |
| 15 |
13 #include "webrtc/base/checks.h" | 16 #include "webrtc/base/checks.h" |
14 #include "webrtc/base/logging.h" | 17 #include "webrtc/base/logging.h" |
15 #include "webrtc/modules/rtp_rtcp/source/byte_io.h" | 18 #include "webrtc/modules/rtp_rtcp/source/byte_io.h" |
16 | 19 |
| 20 // TODO(palmkvist): make logging more informative in the absence of a file name |
| 21 // (or get one) |
| 22 |
17 namespace webrtc { | 23 namespace webrtc { |
18 | 24 |
19 IvfFileWriter::IvfFileWriter(const std::string& file_name, | 25 const size_t kIvfHeaderSize = 32; |
20 std::unique_ptr<FileWrapper> file, | 26 |
21 VideoCodecType codec_type) | 27 IvfFileWriter::IvfFileWriter(rtc::File file, size_t byte_limit) |
22 : codec_type_(codec_type), | 28 : codec_type_(kVideoCodecUnknown), |
| 29 bytes_written_(0), |
| 30 byte_limit_(byte_limit), |
23 num_frames_(0), | 31 num_frames_(0), |
24 width_(0), | 32 width_(0), |
25 height_(0), | 33 height_(0), |
26 last_timestamp_(-1), | 34 last_timestamp_(-1), |
27 using_capture_timestamps_(false), | 35 using_capture_timestamps_(false), |
28 file_name_(file_name), | 36 file_(std::move(file)) { |
29 file_(std::move(file)) {} | 37 RTC_DCHECK(byte_limit == 0 || kIvfHeaderSize <= byte_limit) |
| 38 << "The byte_limit is too low, not even the header will fit."; |
| 39 } |
30 | 40 |
31 IvfFileWriter::~IvfFileWriter() { | 41 IvfFileWriter::~IvfFileWriter() { |
32 Close(); | 42 Close(); |
33 } | 43 } |
34 | 44 |
35 const size_t kIvfHeaderSize = 32; | 45 std::unique_ptr<IvfFileWriter> IvfFileWriter::Wrap(rtc::File file, |
36 | 46 size_t byte_limit) { |
37 std::unique_ptr<IvfFileWriter> IvfFileWriter::Open(const std::string& file_name, | 47 return std::unique_ptr<IvfFileWriter>( |
38 VideoCodecType codec_type) { | 48 new IvfFileWriter(std::move(file), byte_limit)); |
39 std::unique_ptr<IvfFileWriter> file_writer; | |
40 std::unique_ptr<FileWrapper> file(FileWrapper::Create()); | |
41 if (!file->OpenFile(file_name.c_str(), false)) | |
42 return file_writer; | |
43 | |
44 file_writer.reset(new IvfFileWriter( | |
45 file_name, std::unique_ptr<FileWrapper>(std::move(file)), codec_type)); | |
46 if (!file_writer->WriteHeader()) | |
47 file_writer.reset(); | |
48 | |
49 return file_writer; | |
50 } | 49 } |
51 | 50 |
52 bool IvfFileWriter::WriteHeader() { | 51 bool IvfFileWriter::WriteHeader() { |
53 if (file_->Rewind() != 0) { | 52 if (!file_.Seek(0)) { |
54 LOG(LS_WARNING) << "Unable to rewind output file " << file_name_; | 53 LOG(LS_WARNING) << "Unable to rewind ivf output file."; |
55 return false; | 54 return false; |
56 } | 55 } |
57 | 56 |
58 uint8_t ivf_header[kIvfHeaderSize] = {0}; | 57 uint8_t ivf_header[kIvfHeaderSize] = {0}; |
59 ivf_header[0] = 'D'; | 58 ivf_header[0] = 'D'; |
60 ivf_header[1] = 'K'; | 59 ivf_header[1] = 'K'; |
61 ivf_header[2] = 'I'; | 60 ivf_header[2] = 'I'; |
62 ivf_header[3] = 'F'; | 61 ivf_header[3] = 'F'; |
63 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0); // Version. | 62 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0); // Version. |
64 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32); // Header size. | 63 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32); // Header size. |
(...skipping 26 matching lines...) Expand all Loading... |
91 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_); | 90 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_); |
92 // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a | 91 // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a |
93 // 90kHz clock. | 92 // 90kHz clock. |
94 ByteWriter<uint32_t>::WriteLittleEndian( | 93 ByteWriter<uint32_t>::WriteLittleEndian( |
95 &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000); | 94 &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000); |
96 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1); | 95 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1); |
97 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24], | 96 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24], |
98 static_cast<uint32_t>(num_frames_)); | 97 static_cast<uint32_t>(num_frames_)); |
99 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0); // Reserved. | 98 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0); // Reserved. |
100 | 99 |
101 if (!file_->Write(ivf_header, kIvfHeaderSize)) { | 100 if (file_.Write(ivf_header, kIvfHeaderSize) < kIvfHeaderSize) { |
102 LOG(LS_ERROR) << "Unable to write IVF header for file " << file_name_; | 101 LOG(LS_ERROR) << "Unable to write IVF header for ivf output file."; |
103 return false; | 102 return false; |
104 } | 103 } |
105 | 104 |
| 105 if (bytes_written_ < kIvfHeaderSize) { |
| 106 bytes_written_ = kIvfHeaderSize; |
| 107 } |
| 108 |
106 return true; | 109 return true; |
107 } | 110 } |
108 | 111 |
109 bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image) { | 112 bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image, |
| 113 VideoCodecType codec_type) { |
110 width_ = encoded_image._encodedWidth; | 114 width_ = encoded_image._encodedWidth; |
111 height_ = encoded_image._encodedHeight; | 115 height_ = encoded_image._encodedHeight; |
112 RTC_CHECK_GT(width_, 0); | 116 RTC_CHECK_GT(width_, 0); |
113 RTC_CHECK_GT(height_, 0); | 117 RTC_CHECK_GT(height_, 0); |
114 using_capture_timestamps_ = encoded_image._timeStamp == 0; | 118 using_capture_timestamps_ = encoded_image._timeStamp == 0; |
115 | 119 |
| 120 codec_type_ = codec_type; |
| 121 |
116 if (!WriteHeader()) | 122 if (!WriteHeader()) |
117 return false; | 123 return false; |
118 | 124 |
119 std::string codec_name; | 125 std::string codec_name; |
120 switch (codec_type_) { | 126 switch (codec_type_) { |
121 case kVideoCodecVP8: | 127 case kVideoCodecVP8: |
122 codec_name = "VP8"; | 128 codec_name = "VP8"; |
123 break; | 129 break; |
124 case kVideoCodecVP9: | 130 case kVideoCodecVP9: |
125 codec_name = "VP9"; | 131 codec_name = "VP9"; |
126 break; | 132 break; |
127 case kVideoCodecH264: | 133 case kVideoCodecH264: |
128 codec_name = "H264"; | 134 codec_name = "H264"; |
129 break; | 135 break; |
130 default: | 136 default: |
131 codec_name = "Unknown"; | 137 codec_name = "Unknown"; |
132 } | 138 } |
133 LOG(LS_WARNING) << "Created IVF file " << file_name_ | 139 LOG(LS_WARNING) << "Created IVF file for codec data of type " << codec_name |
134 << " for codec data of type " << codec_name | |
135 << " at resolution " << width_ << " x " << height_ | 140 << " at resolution " << width_ << " x " << height_ |
136 << ", using " << (using_capture_timestamps_ ? "1" : "90") | 141 << ", using " << (using_capture_timestamps_ ? "1" : "90") |
137 << "kHz clock resolution."; | 142 << "kHz clock resolution."; |
138 return true; | 143 return true; |
139 } | 144 } |
140 | 145 |
141 bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image) { | 146 bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image, |
142 RTC_DCHECK(file_->is_open()); | 147 VideoCodecType codec_type) { |
| 148 if (!file_.IsOpen()) |
| 149 return false; |
143 | 150 |
144 if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image)) | 151 if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image, codec_type)) |
145 return false; | 152 return false; |
| 153 RTC_DCHECK_EQ(codec_type_, codec_type); |
146 | 154 |
147 if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) && | 155 if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) && |
148 (encoded_image._encodedHeight != height_ || | 156 (encoded_image._encodedHeight != height_ || |
149 encoded_image._encodedWidth != width_)) { | 157 encoded_image._encodedWidth != width_)) { |
150 LOG(LS_WARNING) | 158 LOG(LS_WARNING) |
151 << "Incomig frame has diffferent resolution then previous: (" << width_ | 159 << "Incomig frame has diffferent resolution then previous: (" << width_ |
152 << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x" | 160 << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x" |
153 << encoded_image._encodedHeight << ")"; | 161 << encoded_image._encodedHeight << ")"; |
154 } | 162 } |
155 | 163 |
156 int64_t timestamp = using_capture_timestamps_ | 164 int64_t timestamp = using_capture_timestamps_ |
157 ? encoded_image.capture_time_ms_ | 165 ? encoded_image.capture_time_ms_ |
158 : wrap_handler_.Unwrap(encoded_image._timeStamp); | 166 : wrap_handler_.Unwrap(encoded_image._timeStamp); |
159 if (last_timestamp_ != -1 && timestamp <= last_timestamp_) { | 167 if (last_timestamp_ != -1 && timestamp <= last_timestamp_) { |
160 LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ << " -> " | 168 LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ << " -> " |
161 << timestamp; | 169 << timestamp; |
162 } | 170 } |
163 last_timestamp_ = timestamp; | 171 last_timestamp_ = timestamp; |
164 | 172 |
165 const size_t kFrameHeaderSize = 12; | 173 const size_t kFrameHeaderSize = 12; |
| 174 if (byte_limit_ != 0 && |
| 175 bytes_written_ + kFrameHeaderSize + encoded_image._length > byte_limit_) { |
| 176 Close(); |
| 177 return false; |
| 178 } |
166 uint8_t frame_header[kFrameHeaderSize] = {}; | 179 uint8_t frame_header[kFrameHeaderSize] = {}; |
167 ByteWriter<uint32_t>::WriteLittleEndian( | 180 ByteWriter<uint32_t>::WriteLittleEndian( |
168 &frame_header[0], static_cast<uint32_t>(encoded_image._length)); | 181 &frame_header[0], static_cast<uint32_t>(encoded_image._length)); |
169 ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp); | 182 ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp); |
170 if (!file_->Write(frame_header, kFrameHeaderSize) || | 183 if (file_.Write(frame_header, kFrameHeaderSize) < kFrameHeaderSize || |
171 !file_->Write(encoded_image._buffer, encoded_image._length)) { | 184 file_.Write(encoded_image._buffer, encoded_image._length) < |
172 LOG(LS_ERROR) << "Unable to write frame to file " << file_name_; | 185 encoded_image._length) { |
| 186 LOG(LS_ERROR) << "Unable to write frame to file."; |
173 return false; | 187 return false; |
174 } | 188 } |
175 | 189 |
| 190 bytes_written_ += kFrameHeaderSize + encoded_image._length; |
| 191 |
176 ++num_frames_; | 192 ++num_frames_; |
177 return true; | 193 return true; |
178 } | 194 } |
179 | 195 |
180 bool IvfFileWriter::Close() { | 196 bool IvfFileWriter::Close() { |
181 if (!file_->is_open()) | 197 if (!file_.IsOpen()) |
182 return false; | 198 return false; |
183 | 199 |
184 if (num_frames_ == 0) { | 200 if (num_frames_ == 0) { |
185 // No frame written to file, close and remove it entirely if possible. | 201 file_.Close(); |
186 file_->CloseFile(); | |
187 if (remove(file_name_.c_str()) != 0) | |
188 LOG(LS_WARNING) << "Failed to remove empty IVF file " << file_name_; | |
189 | |
190 return true; | 202 return true; |
191 } | 203 } |
192 | 204 |
193 bool ret = WriteHeader(); | 205 bool ret = WriteHeader(); |
194 file_->CloseFile(); | 206 file_.Close(); |
195 return ret; | 207 return ret; |
196 } | 208 } |
197 | 209 |
198 } // namespace webrtc | 210 } // namespace webrtc |
OLD | NEW |