Line data Source code
1 : /*
2 : * Copyright (c) 2016 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/video_coding/utility/ivf_file_writer.h"
12 :
13 : #include <string>
14 : #include <utility>
15 :
16 : #include "webrtc/base/checks.h"
17 : #include "webrtc/base/logging.h"
18 : #include "webrtc/modules/rtp_rtcp/source/byte_io.h"
19 :
20 : // TODO(palmkvist): make logging more informative in the absence of a file name
21 : // (or get one)
22 :
23 : namespace webrtc {
24 :
25 : const size_t kIvfHeaderSize = 32;
26 :
27 0 : IvfFileWriter::IvfFileWriter(rtc::File file, size_t byte_limit)
28 : : codec_type_(kVideoCodecUnknown),
29 : bytes_written_(0),
30 : byte_limit_(byte_limit),
31 : num_frames_(0),
32 : width_(0),
33 : height_(0),
34 : last_timestamp_(-1),
35 : using_capture_timestamps_(false),
36 0 : file_(std::move(file)) {
37 0 : RTC_DCHECK(byte_limit == 0 || kIvfHeaderSize <= byte_limit)
38 0 : << "The byte_limit is too low, not even the header will fit.";
39 0 : }
40 :
41 0 : IvfFileWriter::~IvfFileWriter() {
42 0 : Close();
43 0 : }
44 :
45 0 : std::unique_ptr<IvfFileWriter> IvfFileWriter::Wrap(rtc::File file,
46 : size_t byte_limit) {
47 : return std::unique_ptr<IvfFileWriter>(
48 0 : new IvfFileWriter(std::move(file), byte_limit));
49 : }
50 :
51 0 : bool IvfFileWriter::WriteHeader() {
52 0 : if (!file_.Seek(0)) {
53 0 : LOG(LS_WARNING) << "Unable to rewind ivf output file.";
54 0 : return false;
55 : }
56 :
57 0 : uint8_t ivf_header[kIvfHeaderSize] = {0};
58 0 : ivf_header[0] = 'D';
59 0 : ivf_header[1] = 'K';
60 0 : ivf_header[2] = 'I';
61 0 : ivf_header[3] = 'F';
62 0 : ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0); // Version.
63 0 : ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32); // Header size.
64 :
65 0 : switch (codec_type_) {
66 : case kVideoCodecVP8:
67 0 : ivf_header[8] = 'V';
68 0 : ivf_header[9] = 'P';
69 0 : ivf_header[10] = '8';
70 0 : ivf_header[11] = '0';
71 0 : break;
72 : case kVideoCodecVP9:
73 0 : ivf_header[8] = 'V';
74 0 : ivf_header[9] = 'P';
75 0 : ivf_header[10] = '9';
76 0 : ivf_header[11] = '0';
77 0 : break;
78 : case kVideoCodecH264:
79 0 : ivf_header[8] = 'H';
80 0 : ivf_header[9] = '2';
81 0 : ivf_header[10] = '6';
82 0 : ivf_header[11] = '4';
83 0 : break;
84 : default:
85 0 : LOG(LS_ERROR) << "Unknown CODEC type: " << codec_type_;
86 0 : return false;
87 : }
88 :
89 0 : ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[12], width_);
90 0 : ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_);
91 : // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a
92 : // 90kHz clock.
93 0 : ByteWriter<uint32_t>::WriteLittleEndian(
94 0 : &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000);
95 0 : ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1);
96 0 : ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24],
97 0 : static_cast<uint32_t>(num_frames_));
98 0 : ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0); // Reserved.
99 :
100 0 : if (file_.Write(ivf_header, kIvfHeaderSize) < kIvfHeaderSize) {
101 0 : LOG(LS_ERROR) << "Unable to write IVF header for ivf output file.";
102 0 : return false;
103 : }
104 :
105 0 : if (bytes_written_ < kIvfHeaderSize) {
106 0 : bytes_written_ = kIvfHeaderSize;
107 : }
108 :
109 0 : return true;
110 : }
111 :
112 0 : bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image,
113 : VideoCodecType codec_type) {
114 0 : width_ = encoded_image._encodedWidth;
115 0 : height_ = encoded_image._encodedHeight;
116 0 : RTC_CHECK_GT(width_, 0);
117 0 : RTC_CHECK_GT(height_, 0);
118 0 : using_capture_timestamps_ = encoded_image._timeStamp == 0;
119 :
120 0 : codec_type_ = codec_type;
121 :
122 0 : if (!WriteHeader())
123 0 : return false;
124 :
125 : const char* codec_name =
126 0 : CodecTypeToPayloadName(codec_type_).value_or("Unknown");
127 0 : LOG(LS_WARNING) << "Created IVF file for codec data of type " << codec_name
128 0 : << " at resolution " << width_ << " x " << height_
129 0 : << ", using " << (using_capture_timestamps_ ? "1" : "90")
130 0 : << "kHz clock resolution.";
131 0 : return true;
132 : }
133 :
134 0 : bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image,
135 : VideoCodecType codec_type) {
136 0 : if (!file_.IsOpen())
137 0 : return false;
138 :
139 0 : if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image, codec_type))
140 0 : return false;
141 0 : RTC_DCHECK_EQ(codec_type_, codec_type);
142 :
143 0 : if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) &&
144 0 : (encoded_image._encodedHeight != height_ ||
145 0 : encoded_image._encodedWidth != width_)) {
146 0 : LOG(LS_WARNING)
147 0 : << "Incomig frame has diffferent resolution then previous: (" << width_
148 0 : << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x"
149 0 : << encoded_image._encodedHeight << ")";
150 : }
151 :
152 0 : int64_t timestamp = using_capture_timestamps_
153 0 : ? encoded_image.capture_time_ms_
154 0 : : wrap_handler_.Unwrap(encoded_image._timeStamp);
155 0 : if (last_timestamp_ != -1 && timestamp <= last_timestamp_) {
156 0 : LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ << " -> "
157 0 : << timestamp;
158 : }
159 0 : last_timestamp_ = timestamp;
160 :
161 0 : const size_t kFrameHeaderSize = 12;
162 0 : if (byte_limit_ != 0 &&
163 0 : bytes_written_ + kFrameHeaderSize + encoded_image._length > byte_limit_) {
164 0 : LOG(LS_WARNING) << "Closing IVF file due to reaching size limit: "
165 0 : << byte_limit_ << " bytes.";
166 0 : Close();
167 0 : return false;
168 : }
169 0 : uint8_t frame_header[kFrameHeaderSize] = {};
170 0 : ByteWriter<uint32_t>::WriteLittleEndian(
171 0 : &frame_header[0], static_cast<uint32_t>(encoded_image._length));
172 0 : ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp);
173 0 : if (file_.Write(frame_header, kFrameHeaderSize) < kFrameHeaderSize ||
174 0 : file_.Write(encoded_image._buffer, encoded_image._length) <
175 0 : encoded_image._length) {
176 0 : LOG(LS_ERROR) << "Unable to write frame to file.";
177 0 : return false;
178 : }
179 :
180 0 : bytes_written_ += kFrameHeaderSize + encoded_image._length;
181 :
182 0 : ++num_frames_;
183 0 : return true;
184 : }
185 :
186 0 : bool IvfFileWriter::Close() {
187 0 : if (!file_.IsOpen())
188 0 : return false;
189 :
190 0 : if (num_frames_ == 0) {
191 0 : file_.Close();
192 0 : return true;
193 : }
194 :
195 0 : bool ret = WriteHeader();
196 0 : file_.Close();
197 0 : return ret;
198 : }
199 :
200 : } // namespace webrtc
|