blob: d2de6640f8d57b39f0856119d04fedd3d214b97d [file] [log] [blame]
sprang3911c262016-04-15 01:24:14 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/utility/ivf_file_writer.h"
sprang3911c262016-04-15 01:24:14 -070012
palmkviste75f2042016-09-28 06:19:48 -070013#include <string>
14#include <utility>
15
Mirko Bonadei04255172018-09-10 16:48:02 +020016#include "api/video_codecs/video_codec.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "modules/rtp_rtcp/source/byte_io.h"
18#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
sprang3911c262016-04-15 01:24:14 -070020
palmkviste75f2042016-09-28 06:19:48 -070021// TODO(palmkvist): make logging more informative in the absence of a file name
22// (or get one)
23
sprang3911c262016-04-15 01:24:14 -070024namespace webrtc {
25
palmkviste75f2042016-09-28 06:19:48 -070026const size_t kIvfHeaderSize = 32;
27
28IvfFileWriter::IvfFileWriter(rtc::File file, size_t byte_limit)
Kári Tristan Helgason84ccb2d2018-08-16 14:35:26 +020029 : codec_type_(kVideoCodecGeneric),
palmkviste75f2042016-09-28 06:19:48 -070030 bytes_written_(0),
31 byte_limit_(byte_limit),
sprang3911c262016-04-15 01:24:14 -070032 num_frames_(0),
33 width_(0),
34 height_(0),
35 last_timestamp_(-1),
36 using_capture_timestamps_(false),
palmkviste75f2042016-09-28 06:19:48 -070037 file_(std::move(file)) {
38 RTC_DCHECK(byte_limit == 0 || kIvfHeaderSize <= byte_limit)
39 << "The byte_limit is too low, not even the header will fit.";
40}
sprang3911c262016-04-15 01:24:14 -070041
42IvfFileWriter::~IvfFileWriter() {
43 Close();
44}
45
palmkviste75f2042016-09-28 06:19:48 -070046std::unique_ptr<IvfFileWriter> IvfFileWriter::Wrap(rtc::File file,
47 size_t byte_limit) {
48 return std::unique_ptr<IvfFileWriter>(
49 new IvfFileWriter(std::move(file), byte_limit));
sprang3911c262016-04-15 01:24:14 -070050}
51
52bool IvfFileWriter::WriteHeader() {
palmkviste75f2042016-09-28 06:19:48 -070053 if (!file_.Seek(0)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010054 RTC_LOG(LS_WARNING) << "Unable to rewind ivf output file.";
sprang3911c262016-04-15 01:24:14 -070055 return false;
56 }
57
58 uint8_t ivf_header[kIvfHeaderSize] = {0};
59 ivf_header[0] = 'D';
60 ivf_header[1] = 'K';
61 ivf_header[2] = 'I';
62 ivf_header[3] = 'F';
63 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[4], 0); // Version.
64 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[6], 32); // Header size.
65
66 switch (codec_type_) {
kjellander02b3d272016-04-20 05:05:54 -070067 case kVideoCodecVP8:
sprang3911c262016-04-15 01:24:14 -070068 ivf_header[8] = 'V';
69 ivf_header[9] = 'P';
70 ivf_header[10] = '8';
71 ivf_header[11] = '0';
72 break;
kjellander02b3d272016-04-20 05:05:54 -070073 case kVideoCodecVP9:
sprang3911c262016-04-15 01:24:14 -070074 ivf_header[8] = 'V';
75 ivf_header[9] = 'P';
76 ivf_header[10] = '9';
77 ivf_header[11] = '0';
78 break;
kjellander02b3d272016-04-20 05:05:54 -070079 case kVideoCodecH264:
sprang3911c262016-04-15 01:24:14 -070080 ivf_header[8] = 'H';
81 ivf_header[9] = '2';
82 ivf_header[10] = '6';
83 ivf_header[11] = '4';
84 break;
85 default:
Mirko Bonadei675513b2017-11-09 11:09:25 +010086 RTC_LOG(LS_ERROR) << "Unknown CODEC type: " << codec_type_;
sprang3911c262016-04-15 01:24:14 -070087 return false;
88 }
89
90 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[12], width_);
91 ByteWriter<uint16_t>::WriteLittleEndian(&ivf_header[14], height_);
92 // Render timestamps are in ms (1/1000 scale), while RTP timestamps use a
93 // 90kHz clock.
94 ByteWriter<uint32_t>::WriteLittleEndian(
95 &ivf_header[16], using_capture_timestamps_ ? 1000 : 90000);
96 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[20], 1);
97 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[24],
98 static_cast<uint32_t>(num_frames_));
99 ByteWriter<uint32_t>::WriteLittleEndian(&ivf_header[28], 0); // Reserved.
100
palmkviste75f2042016-09-28 06:19:48 -0700101 if (file_.Write(ivf_header, kIvfHeaderSize) < kIvfHeaderSize) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100102 RTC_LOG(LS_ERROR) << "Unable to write IVF header for ivf output file.";
sprang3911c262016-04-15 01:24:14 -0700103 return false;
104 }
105
palmkviste75f2042016-09-28 06:19:48 -0700106 if (bytes_written_ < kIvfHeaderSize) {
107 bytes_written_ = kIvfHeaderSize;
108 }
109
sprang3911c262016-04-15 01:24:14 -0700110 return true;
111}
112
palmkviste75f2042016-09-28 06:19:48 -0700113bool IvfFileWriter::InitFromFirstFrame(const EncodedImage& encoded_image,
114 VideoCodecType codec_type) {
sprang3911c262016-04-15 01:24:14 -0700115 width_ = encoded_image._encodedWidth;
116 height_ = encoded_image._encodedHeight;
117 RTC_CHECK_GT(width_, 0);
118 RTC_CHECK_GT(height_, 0);
Niels Möller23775882018-08-16 10:24:12 +0200119 using_capture_timestamps_ = encoded_image.Timestamp() == 0;
sprang3911c262016-04-15 01:24:14 -0700120
palmkviste75f2042016-09-28 06:19:48 -0700121 codec_type_ = codec_type;
122
sprang3911c262016-04-15 01:24:14 -0700123 if (!WriteHeader())
124 return false;
125
Yves Gerey665174f2018-06-19 15:03:05 +0200126 const char* codec_name = CodecTypeToPayloadString(codec_type_);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100127 RTC_LOG(LS_WARNING) << "Created IVF file for codec data of type "
128 << codec_name << " at resolution " << width_ << " x "
129 << height_ << ", using "
130 << (using_capture_timestamps_ ? "1" : "90")
131 << "kHz clock resolution.";
sprang3911c262016-04-15 01:24:14 -0700132 return true;
133}
134
palmkviste75f2042016-09-28 06:19:48 -0700135bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image,
136 VideoCodecType codec_type) {
137 if (!file_.IsOpen())
sprang3911c262016-04-15 01:24:14 -0700138 return false;
139
palmkviste75f2042016-09-28 06:19:48 -0700140 if (num_frames_ == 0 && !InitFromFirstFrame(encoded_image, codec_type))
141 return false;
142 RTC_DCHECK_EQ(codec_type_, codec_type);
143
sprang3911c262016-04-15 01:24:14 -0700144 if ((encoded_image._encodedWidth > 0 || encoded_image._encodedHeight > 0) &&
145 (encoded_image._encodedHeight != height_ ||
146 encoded_image._encodedWidth != width_)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100147 RTC_LOG(LS_WARNING)
sprang3911c262016-04-15 01:24:14 -0700148 << "Incomig frame has diffferent resolution then previous: (" << width_
149 << "x" << height_ << ") -> (" << encoded_image._encodedWidth << "x"
150 << encoded_image._encodedHeight << ")";
151 }
152
153 int64_t timestamp = using_capture_timestamps_
154 ? encoded_image.capture_time_ms_
Niels Möller23775882018-08-16 10:24:12 +0200155 : wrap_handler_.Unwrap(encoded_image.Timestamp());
sprang3911c262016-04-15 01:24:14 -0700156 if (last_timestamp_ != -1 && timestamp <= last_timestamp_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100157 RTC_LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_
158 << " -> " << timestamp;
sprang3911c262016-04-15 01:24:14 -0700159 }
160 last_timestamp_ = timestamp;
161
162 const size_t kFrameHeaderSize = 12;
palmkviste75f2042016-09-28 06:19:48 -0700163 if (byte_limit_ != 0 &&
164 bytes_written_ + kFrameHeaderSize + encoded_image._length > byte_limit_) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100165 RTC_LOG(LS_WARNING) << "Closing IVF file due to reaching size limit: "
166 << byte_limit_ << " bytes.";
palmkviste75f2042016-09-28 06:19:48 -0700167 Close();
168 return false;
169 }
sprang3911c262016-04-15 01:24:14 -0700170 uint8_t frame_header[kFrameHeaderSize] = {};
171 ByteWriter<uint32_t>::WriteLittleEndian(
172 &frame_header[0], static_cast<uint32_t>(encoded_image._length));
173 ByteWriter<uint64_t>::WriteLittleEndian(&frame_header[4], timestamp);
palmkviste75f2042016-09-28 06:19:48 -0700174 if (file_.Write(frame_header, kFrameHeaderSize) < kFrameHeaderSize ||
175 file_.Write(encoded_image._buffer, encoded_image._length) <
176 encoded_image._length) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100177 RTC_LOG(LS_ERROR) << "Unable to write frame to file.";
sprang3911c262016-04-15 01:24:14 -0700178 return false;
179 }
180
palmkviste75f2042016-09-28 06:19:48 -0700181 bytes_written_ += kFrameHeaderSize + encoded_image._length;
182
sprang3911c262016-04-15 01:24:14 -0700183 ++num_frames_;
184 return true;
185}
186
187bool IvfFileWriter::Close() {
palmkviste75f2042016-09-28 06:19:48 -0700188 if (!file_.IsOpen())
sprang3911c262016-04-15 01:24:14 -0700189 return false;
190
191 if (num_frames_ == 0) {
palmkviste75f2042016-09-28 06:19:48 -0700192 file_.Close();
sprang3911c262016-04-15 01:24:14 -0700193 return true;
194 }
195
tommia6219cc2016-06-15 10:30:14 -0700196 bool ret = WriteHeader();
palmkviste75f2042016-09-28 06:19:48 -0700197 file_.Close();
tommia6219cc2016-06-15 10:30:14 -0700198 return ret;
sprang3911c262016-04-15 01:24:14 -0700199}
200
201} // namespace webrtc