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