OpenShot Library | libopenshot  0.2.5
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 "../include/ChunkWriter.h"
32 
33 using namespace openshot;
34 
35 ChunkWriter::ChunkWriter(std::string path, ReaderBase *reader) :
36  local_reader(reader), path(path), chunk_size(24*3), chunk_count(1), frame_count(1), is_writing(false),
37  default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis"), last_frame_needed(false), is_open(false)
38 {
39  // Change codecs to default
40  info.vcodec = default_vcodec;
41  info.acodec = default_acodec;
42 
43  // Copy info struct from the source reader
44  CopyReaderInfo(local_reader);
45 
46  // Create folder (if it does not exist)
47  create_folder(path);
48 
49  // Write JSON meta data file
50  write_json_meta_data();
51 
52  // Open reader
53  local_reader->Open();
54 }
55 
56 // get a formatted path of a specific chunk
57 std::string ChunkWriter::get_chunk_path(int64_t chunk_number, std::string folder, std::string extension)
58 {
59  // Create path of new chunk video
60  std::stringstream chunk_count_string;
61  chunk_count_string << chunk_number;
62  QString padded_count = "%1"; //chunk_count_string.str().c_str();
63  padded_count = padded_count.arg(chunk_count_string.str().c_str(), 6, '0');
64  if (folder.length() != 0 && extension.length() != 0)
65  // Return path with FOLDER and EXTENSION name
66  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str() + QDir::separator() + padded_count + extension.c_str()).toStdString();
67 
68  else if (folder.length() == 0 && extension.length() != 0)
69  // Return path with NO FOLDER and EXTENSION name
70  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + padded_count + extension.c_str()).toStdString();
71 
72  else if (folder.length() != 0 && extension.length() == 0)
73  // Return path with FOLDER and NO EXTENSION
74  return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str()).toStdString();
75  else
76  return "";
77 }
78 
79 // Add a frame to the queue waiting to be encoded.
80 void ChunkWriter::WriteFrame(std::shared_ptr<Frame> frame)
81 {
82  // Check for open reader (or throw exception)
83  if (!is_open)
84  throw WriterClosed("The ChunkWriter is closed. Call Open() before calling this method.", path);
85 
86  // Check if currently writing chunks?
87  if (!is_writing)
88  {
89  // Save thumbnail of chunk start frame
90  frame->Save(get_chunk_path(chunk_count, "", ".jpeg"), 1.0);
91 
92  // Create FFmpegWriter (FINAL quality)
93  create_folder(get_chunk_path(chunk_count, "final", ""));
94  writer_final = new FFmpegWriter(get_chunk_path(chunk_count, "final", default_extension));
95  writer_final->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
96  writer_final->SetVideoOptions(true, default_vcodec, info.fps, info.width, info.height, info.pixel_ratio, false, false, info.video_bit_rate);
97 
98  // Create FFmpegWriter (PREVIEW quality)
99  create_folder(get_chunk_path(chunk_count, "preview", ""));
100  writer_preview = new FFmpegWriter(get_chunk_path(chunk_count, "preview", default_extension));
101  writer_preview->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
102  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);
103 
104  // Create FFmpegWriter (LOW quality)
105  create_folder(get_chunk_path(chunk_count, "thumb", ""));
106  writer_thumb = new FFmpegWriter(get_chunk_path(chunk_count, "thumb", default_extension));
107  writer_thumb->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.channel_layout, 128000);
108  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);
109 
110  // Prepare Streams
111  writer_final->PrepareStreams();
112  writer_preview->PrepareStreams();
113  writer_thumb->PrepareStreams();
114 
115  // Write header
116  writer_final->WriteHeader();
117  writer_preview->WriteHeader();
118  writer_thumb->WriteHeader();
119 
120  // Keep track that a chunk is being written
121  is_writing = true;
122  last_frame_needed = true;
123  }
124 
125  // If this is not the 1st chunk, always start frame 1 with the last frame from the previous
126  // chunk. This helps to prevent audio resampling issues (because it "stokes" the sample array)
127  if (last_frame_needed)
128  {
129  if (last_frame)
130  {
131  // Write the previous chunks LAST FRAME to the current chunk
132  writer_final->WriteFrame(last_frame);
133  writer_preview->WriteFrame(last_frame);
134  writer_thumb->WriteFrame(last_frame);
135  } else {
136  // Write the 1st frame (of the 1st chunk)... since no previous chunk is available
137  std::shared_ptr<Frame> blank_frame(new Frame(1, info.width, info.height, "#000000", info.sample_rate, info.channels));
138  blank_frame->AddColor(info.width, info.height, "#000000");
139  writer_final->WriteFrame(blank_frame);
140  writer_preview->WriteFrame(blank_frame);
141  writer_thumb->WriteFrame(blank_frame);
142  }
143 
144  // disable last frame
145  last_frame_needed = false;
146  }
147 
148 
149  //////////////////////////////////////////////////
150  // WRITE THE CURRENT FRAME TO THE CURRENT CHUNK
151  writer_final->WriteFrame(frame);
152  writer_preview->WriteFrame(frame);
153  writer_thumb->WriteFrame(frame);
154  //////////////////////////////////////////////////
155 
156 
157  // Write the frames once it reaches the correct chunk size
158  if (frame_count % chunk_size == 0 && frame_count >= chunk_size)
159  {
160  std::cout << "Done with chunk" << std::endl;
161  std::cout << "frame_count: " << frame_count << std::endl;
162  std::cout << "chunk_size: " << chunk_size << std::endl;
163 
164  // Pad an additional 12 frames
165  for (int z = 0; z<12; z++)
166  {
167  // Repeat frame
168  writer_final->WriteFrame(frame);
169  writer_preview->WriteFrame(frame);
170  writer_thumb->WriteFrame(frame);
171  }
172 
173  // Write Footer
174  writer_final->WriteTrailer();
175  writer_preview->WriteTrailer();
176  writer_thumb->WriteTrailer();
177 
178  // Close writer & reader
179  writer_final->Close();
180  writer_preview->Close();
181  writer_thumb->Close();
182 
183  // Increment chunk count
184  chunk_count++;
185 
186  // Stop writing chunk
187  is_writing = false;
188  }
189 
190  // Increment frame counter
191  frame_count++;
192 
193  // Keep track of the last frame added
194  last_frame = frame;
195 }
196 
197 
198 // Write a block of frames from a reader
199 void ChunkWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length)
200 {
201  // Loop through each frame (and encoded it)
202  for (int64_t number = start; number <= length; number++)
203  {
204  // Get the frame
205  std::shared_ptr<Frame> f = reader->GetFrame(number);
206 
207  // Encode frame
208  WriteFrame(f);
209  }
210 }
211 
212 // Write a block of frames from the local cached reader
213 void ChunkWriter::WriteFrame(int64_t start, int64_t length)
214 {
215  // Loop through each frame (and encoded it)
216  for (int64_t number = start; number <= length; number++)
217  {
218  // Get the frame
219  std::shared_ptr<Frame> f = local_reader->GetFrame(number);
220 
221  // Encode frame
222  WriteFrame(f);
223  }
224 }
225 
226 // Close the writer
228 {
229  // Write the frames once it reaches the correct chunk size
230  if (is_writing)
231  {
232  std::cout << "Final chunk" << std::endl;
233  std::cout << "frame_count: " << frame_count << std::endl;
234  std::cout << "chunk_size: " << chunk_size << std::endl;
235 
236  // Pad an additional 12 frames
237  for (int z = 0; z<12; z++)
238  {
239  // Repeat frame
240  writer_final->WriteFrame(last_frame);
241  writer_preview->WriteFrame(last_frame);
242  writer_thumb->WriteFrame(last_frame);
243  }
244 
245  // Write Footer
246  writer_final->WriteTrailer();
247  writer_preview->WriteTrailer();
248  writer_thumb->WriteTrailer();
249 
250  // Close writer & reader
251  writer_final->Close();
252  writer_preview->Close();
253  writer_thumb->Close();
254 
255  // Increment chunk count
256  chunk_count++;
257 
258  // Stop writing chunk
259  is_writing = false;
260  }
261 
262  // close writer
263  is_open = false;
264 
265  // Reset frame counters
266  chunk_count = 0;
267  frame_count = 0;
268 
269  // Open reader
270  local_reader->Close();
271 }
272 
273 // write JSON meta data
274 void ChunkWriter::write_json_meta_data()
275 {
276  // Load path of chunk folder
277  std::string json_path = QDir::cleanPath(QString(path.c_str()) + QDir::separator() + "info.json").toStdString();
278 
279  // Write JSON file
280  std::ofstream myfile;
281  myfile.open (json_path.c_str());
282  myfile << local_reader->Json() << std::endl;
283  myfile.close();
284 }
285 
286 // check for chunk folder
287 void ChunkWriter::create_folder(std::string path)
288 {
289  QDir dir(path.c_str());
290  if (!dir.exists()) {
291  dir.mkpath(".");
292  }
293 }
294 
295 // check for valid chunk json
296 bool ChunkWriter::is_chunk_valid()
297 {
298  return true;
299 }
300 
301 // Open the writer
303 {
304  is_open = true;
305 }
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
This class represents a single frame of video (i.e. image & audio data)
Definition: Frame.h:106
ChunkWriter(std::string path, openshot::ReaderBase *reader)
Constructor for ChunkWriter. Throws one of the following exceptions.
Definition: ChunkWriter.cpp:35
This class uses the FFmpeg libraries, to write and encode video files and audio files.
Definition: FFmpegWriter.h:151
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Add a frame to the stack waiting to be encoded.
Definition: ChunkWriter.cpp:80
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
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
Get and Set JSON methods.
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.
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: WriterBase.h:64
Exception when a writer is closed, and a frame is requested.
Definition: Exceptions.h:385
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:67
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)