OpenShot Library | libopenshot  0.2.7
ChunkWriter.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Source file for ChunkWriter class
4  * @author Jonathan Thomas <jonathan@openshot.org>
5  *
6  * @ref License
7  */
8 
9 /* LICENSE
10  *
11  * Copyright (c) 2008-2019 OpenShot Studios, LLC
12  * <http://www.openshotstudios.com/>. This file is part of
13  * OpenShot Library (libopenshot), an open-source project dedicated to
14  * delivering high quality video editing and animation solutions to the
15  * world. For more information visit <http://www.openshot.org/>.
16  *
17  * OpenShot Library (libopenshot) is free software: you can redistribute it
18  * and/or modify it under the terms of the GNU Lesser General Public License
19  * as published by the Free Software Foundation, either version 3 of the
20  * License, or (at your option) any later version.
21  *
22  * OpenShot Library (libopenshot) is distributed in the hope that it will be
23  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25  * GNU Lesser General Public License for more details.
26  *
27  * You should have received a copy of the GNU Lesser General Public License
28  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29  */
30 
31 #include "ChunkWriter.h"
32 #include "Exceptions.h"
33 
34 using namespace openshot;
35 
36 ChunkWriter::ChunkWriter(std::string path, ReaderBase *reader) :
37  local_reader(reader), path(path), chunk_size(24*3), chunk_count(1), frame_count(1), is_writing(false),
38  default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis"), last_frame_needed(false), is_open(false)
39 {
40  // Change codecs to default
41  info.vcodec = default_vcodec;
42  info.acodec = default_acodec;
43 
44  // Copy info struct from the source reader
45  CopyReaderInfo(local_reader);
46 
47  // Create folder (if it does not exist)
48  create_folder(path);
49 
50  // Write JSON meta data file
51  write_json_meta_data();
52 
53  // Open reader
54  local_reader->Open();
55 }
56 
57 // get a formatted path of a specific chunk
58 std::string ChunkWriter::get_chunk_path(int64_t chunk_number, std::string folder, std::string extension)
59 {
60  // Create path of new chunk video
61  std::stringstream chunk_count_string;
62  chunk_count_string << chunk_number;
63  QString padded_count = "%1"; //chunk_count_string.str().c_str();
64  padded_count = padded_count.arg(chunk_count_string.str().c_str(), 6, '0');
65  if (folder.length() != 0 && extension.length() != 0)
66  // Return path with FOLDER and EXTENSION name
67  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str() + QDir::separator() + padded_count + extension.c_str()).toStdString();
68 
69  else if (folder.length() == 0 && extension.length() != 0)
70  // Return path with NO FOLDER and EXTENSION name
71  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + padded_count + extension.c_str()).toStdString();
72 
73  else if (folder.length() != 0 && extension.length() == 0)
74  // Return path with FOLDER and NO EXTENSION
75  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str()).toStdString();
76  else
77  return "";
78 }
79 
80 // Add a frame to the queue waiting to be encoded.
81 void ChunkWriter::WriteFrame(std::shared_ptr<Frame> frame)
82 {
83  // Check for open reader (or throw exception)
84  if (!is_open)
85  throw WriterClosed("The ChunkWriter is closed. Call Open() before calling this method.", path);
86 
87  // Check if currently writing chunks?
88  if (!is_writing)
89  {
90  // Save thumbnail of chunk start frame
91  frame->Save(get_chunk_path(chunk_count, "", ".jpeg"), 1.0);
92 
93  // Create FFmpegWriter (FINAL quality)
94  create_folder(get_chunk_path(chunk_count, "final", ""));
95  writer_final = new FFmpegWriter(get_chunk_path(chunk_count, "final", default_extension));
96  writer_final->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
97  writer_final->SetVideoOptions(true, default_vcodec, info.fps, info.width, info.height, info.pixel_ratio, false, false, info.video_bit_rate);
98 
99  // Create FFmpegWriter (PREVIEW quality)
100  create_folder(get_chunk_path(chunk_count, "preview", ""));
101  writer_preview = new FFmpegWriter(get_chunk_path(chunk_count, "preview", default_extension));
102  writer_preview->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
103  writer_preview->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.5, info.height * 0.5, info.pixel_ratio, false, false, info.video_bit_rate * 0.5);
104 
105  // Create FFmpegWriter (LOW quality)
106  create_folder(get_chunk_path(chunk_count, "thumb", ""));
107  writer_thumb = new FFmpegWriter(get_chunk_path(chunk_count, "thumb", default_extension));
108  writer_thumb->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
109  writer_thumb->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.25, info.height * 0.25, info.pixel_ratio, false, false, info.video_bit_rate * 0.25);
110 
111  // Prepare Streams
112  writer_final->PrepareStreams();
113  writer_preview->PrepareStreams();
114  writer_thumb->PrepareStreams();
115 
116  // Write header
117  writer_final->WriteHeader();
118  writer_preview->WriteHeader();
119  writer_thumb->WriteHeader();
120 
121  // Keep track that a chunk is being written
122  is_writing = true;
123  last_frame_needed = true;
124  }
125 
126  // If this is not the 1st chunk, always start frame 1 with the last frame from the previous
127  // chunk. This helps to prevent audio resampling issues (because it "stokes" the sample array)
128  if (last_frame_needed)
129  {
130  if (last_frame)
131  {
132  // Write the previous chunks LAST FRAME to the current chunk
133  writer_final->WriteFrame(last_frame);
134  writer_preview->WriteFrame(last_frame);
135  writer_thumb->WriteFrame(last_frame);
136  } else {
137  // Write the 1st frame (of the 1st chunk)... since no previous chunk is available
138  auto blank_frame = std::make_shared<Frame>(
139  1, info.width, info.height, "#000000",
141  blank_frame->AddColor(info.width, info.height, "#000000");
142  writer_final->WriteFrame(blank_frame);
143  writer_preview->WriteFrame(blank_frame);
144  writer_thumb->WriteFrame(blank_frame);
145  }
146 
147  // disable last frame
148  last_frame_needed = false;
149  }
150 
151 
152  //////////////////////////////////////////////////
153  // WRITE THE CURRENT FRAME TO THE CURRENT CHUNK
154  writer_final->WriteFrame(frame);
155  writer_preview->WriteFrame(frame);
156  writer_thumb->WriteFrame(frame);
157  //////////////////////////////////////////////////
158 
159 
160  // Write the frames once it reaches the correct chunk size
161  if (frame_count % chunk_size == 0 && frame_count >= chunk_size)
162  {
163  // Pad an additional 12 frames
164  for (int z = 0; z<12; z++)
165  {
166  // Repeat frame
167  writer_final->WriteFrame(frame);
168  writer_preview->WriteFrame(frame);
169  writer_thumb->WriteFrame(frame);
170  }
171 
172  // Write Footer
173  writer_final->WriteTrailer();
174  writer_preview->WriteTrailer();
175  writer_thumb->WriteTrailer();
176 
177  // Close writer & reader
178  writer_final->Close();
179  writer_preview->Close();
180  writer_thumb->Close();
181 
182  // Increment chunk count
183  chunk_count++;
184 
185  // Stop writing chunk
186  is_writing = false;
187  }
188 
189  // Increment frame counter
190  frame_count++;
191 
192  // Keep track of the last frame added
193  last_frame = frame;
194 }
195 
196 
197 // Write a block of frames from a reader
198 void ChunkWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length)
199 {
200  // Loop through each frame (and encoded it)
201  for (int64_t number = start; number <= length; number++)
202  {
203  // Get the frame
204  std::shared_ptr<Frame> f = reader->GetFrame(number);
205 
206  // Encode frame
207  WriteFrame(f);
208  }
209 }
210 
211 // Write a block of frames from the local cached reader
212 void ChunkWriter::WriteFrame(int64_t start, int64_t length)
213 {
214  // Loop through each frame (and encoded it)
215  for (int64_t number = start; number <= length; number++)
216  {
217  // Get the frame
218  std::shared_ptr<Frame> f = local_reader->GetFrame(number);
219 
220  // Encode frame
221  WriteFrame(f);
222  }
223 }
224 
225 // Close the writer
227 {
228  // Write the frames once it reaches the correct chunk size
229  if (is_writing)
230  {
231  // Pad an additional 12 frames
232  for (int z = 0; z<12; z++)
233  {
234  // Repeat frame
235  writer_final->WriteFrame(last_frame);
236  writer_preview->WriteFrame(last_frame);
237  writer_thumb->WriteFrame(last_frame);
238  }
239 
240  // Write Footer
241  writer_final->WriteTrailer();
242  writer_preview->WriteTrailer();
243  writer_thumb->WriteTrailer();
244 
245  // Close writer & reader
246  writer_final->Close();
247  writer_preview->Close();
248  writer_thumb->Close();
249 
250  // Increment chunk count
251  chunk_count++;
252 
253  // Stop writing chunk
254  is_writing = false;
255  }
256 
257  // close writer
258  is_open = false;
259 
260  // Reset frame counters
261  chunk_count = 0;
262  frame_count = 0;
263 
264  // Open reader
265  local_reader->Close();
266 }
267 
268 // write JSON meta data
269 void ChunkWriter::write_json_meta_data()
270 {
271  // Load path of chunk folder
272  std::string json_path = QDir::cleanPath(QString(path.c_str()) + QDir::separator() + "info.json").toStdString();
273 
274  // Write JSON file
275  std::ofstream myfile;
276  myfile.open (json_path.c_str());
277  myfile << local_reader->Json() << std::endl;
278  myfile.close();
279 }
280 
281 // check for chunk folder
282 void ChunkWriter::create_folder(std::string path)
283 {
284  QDir dir(path.c_str());
285  if (!dir.exists()) {
286  dir.mkpath(".");
287  }
288 }
289 
290 // check for valid chunk json
291 bool ChunkWriter::is_chunk_valid()
292 {
293  return true;
294 }
295 
296 // Open the writer
298 {
299  is_open = true;
300 }
int channels
The number of audio channels used in the audio stream.
Definition: WriterBase.h:73
WriterInfo info
Information about the current media file.
Definition: WriterBase.h:94
int video_bit_rate
The bit rate of the video stream (in bytes)
Definition: WriterBase.h:61
Header file for ChunkWriter class.
ChunkWriter(std::string path, openshot::ReaderBase *reader)
Constructor for ChunkWriter. Throws one of the following exceptions.
Definition: ChunkWriter.cpp:36
This class uses the FFmpeg libraries, to write and encode video files and audio files.
Definition: FFmpegWriter.h:148
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Add a frame to the stack waiting to be encoded.
Definition: ChunkWriter.cpp:81
virtual void Close()=0
Close the reader (and any resources it was consuming)
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:97
int width
The width of the video (in pixels)
Definition: WriterBase.h:58
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: WriterBase.h:70
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition: WriterBase.h:74
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square) ...
Definition: WriterBase.h:62
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Add a frame to the stack waiting to be encoded.
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
Header file for all Exception classes.
void SetVideoOptions(bool has_video, std::string codec, openshot::Fraction fps, int width, int height, openshot::Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate)
Set video export options.
void SetAudioOptions(bool has_audio, std::string codec, int sample_rate, int channels, openshot::ChannelLayout channel_layout, int bit_rate)
Set audio export options.
virtual std::string Json() const =0
Generate JSON string of this object.
void WriteTrailer()
Write the file trailer (after all frames are written). This is called automatically by the Close() me...
void WriteHeader()
Write the file header (after the options are set). This method is called automatically by the Open() ...
void Close()
Close the writer.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: WriterBase.h:72
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:46
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: WriterBase.h:64
Exception when too many seek attempts happen.
Definition: Exceptions.h:390
void PrepareStreams()
Prepare & initialize streams and open codecs. This method is called automatically by the Open() metho...
int height
The height of the video (in pixels)
Definition: WriterBase.h:57
void Open()
Open writer.
void CopyReaderInfo(openshot::ReaderBase *reader)
This method copy&#39;s the info struct of a reader, and sets the writer with the same info...
Definition: WriterBase.cpp:68
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: WriterBase.h:60
void Close()
Close the writer.
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)