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