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 }
openshot::ChunkWriter::Open
void Open()
Open writer.
Definition: ChunkWriter.cpp:302
openshot::WriterInfo::video_bit_rate
int video_bit_rate
The bit rate of the video stream (in bytes)
Definition: WriterBase.h:61
openshot::WriterClosed
Exception when a writer is closed, and a frame is requested.
Definition: Exceptions.h:386
openshot::ReaderBase::GetFrame
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::ReaderBase::Json
virtual std::string Json() const =0
Get and Set JSON methods.
openshot::WriterInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: WriterBase.h:60
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AudioBufferSource.h:39
openshot::WriterInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition: WriterBase.h:73
openshot::FFmpegWriter::SetVideoOptions
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.
Definition: FFmpegWriter.cpp:169
openshot::Frame
This class represents a single frame of video (i.e. image & audio data)
Definition: Frame.h:107
openshot::ChunkWriter::WriteFrame
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Add a frame to the stack waiting to be encoded.
Definition: ChunkWriter.cpp:80
openshot::WriterInfo::width
int width
The width of the video (in pixels)
Definition: WriterBase.h:58
openshot::WriterInfo::acodec
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: WriterBase.h:70
openshot::FFmpegWriter
This class uses the FFmpeg libraries, to write and encode video files and audio files.
Definition: FFmpegWriter.h:151
openshot::WriterInfo::pixel_ratio
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
openshot::ReaderBase::Open
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
openshot::WriterInfo::channel_layout
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition: WriterBase.h:74
path
path
Definition: FFmpegWriter.cpp:1410
openshot::FFmpegWriter::WriteFrame
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Add a frame to the stack waiting to be encoded.
Definition: FFmpegWriter.cpp:619
openshot::FFmpegWriter::Close
void Close()
Close the writer.
Definition: FFmpegWriter.cpp:1011
openshot::WriterInfo::height
int height
The height of the video (in pixels)
Definition: WriterBase.h:57
openshot::Frame::Save
void Save(std::string path, float scale, std::string format="PNG", int quality=100)
Save the frame image to the specified path. The image format can be BMP, JPG, JPEG,...
Definition: Frame.cpp:578
openshot::FFmpegWriter::WriteTrailer
void WriteTrailer()
Write the file trailer (after all frames are written). This is called automatically by the Close() me...
Definition: FFmpegWriter.cpp:774
openshot::WriterBase::CopyReaderInfo
void CopyReaderInfo(openshot::ReaderBase *reader)
This method copy's the info struct of a reader, and sets the writer with the same info.
Definition: WriterBase.cpp:67
openshot::ChunkWriter::Close
void Close()
Close the writer.
Definition: ChunkWriter.cpp:227
openshot::ReaderBase
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:98
openshot::WriterInfo::vcodec
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: WriterBase.h:64
openshot::WriterInfo::sample_rate
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: WriterBase.h:72
openshot::ReaderBase::Close
virtual void Close()=0
Close the reader (and any resources it was consuming)
openshot::FFmpegWriter::SetAudioOptions
void SetAudioOptions(bool has_audio, std::string codec, int sample_rate, int channels, openshot::ChannelLayout channel_layout, int bit_rate)
Set audio export options.
Definition: FFmpegWriter.cpp:289
openshot::ChunkWriter::ChunkWriter
ChunkWriter(std::string path, openshot::ReaderBase *reader)
Constructor for ChunkWriter. Throws one of the following exceptions.
Definition: ChunkWriter.cpp:35
openshot::FFmpegWriter::PrepareStreams
void PrepareStreams()
Prepare & initialize streams and open codecs. This method is called automatically by the Open() metho...
Definition: FFmpegWriter.cpp:561
openshot::WriterBase::info
WriterInfo info
Information about the current media file.
Definition: WriterBase.h:94
openshot::Frame::AddColor
void AddColor(int new_width, int new_height, std::string new_color)
Add (or replace) pixel data to the frame (based on a solid color)
Definition: Frame.cpp:733
openshot::FFmpegWriter::WriteHeader
void WriteHeader()
Write the file header (after the options are set). This method is called automatically by the Open() ...
Definition: FFmpegWriter.cpp:575