| 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 |