1 /*
2 * Copyright(c) 2019 Netflix, Inc.
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
10 */
11 
12 /******************************************************************************
13  * @file FrameQueue.cc
14  *
15  * @brief Impelmentation of reconstructed frame queue
16  *
17  ******************************************************************************/
18 
19 #include <stdio.h>
20 #include <vector>
21 #include <algorithm>
22 #include "FrameQueue.h"
23 #include "CompareTools.h"
24 #ifdef ENABLE_DEBUG_MONITOR
25 #include "VideoMonitor.h"
26 #endif
27 
28 #if _WIN32
29 #define fseeko _fseeki64
30 #define ftello _ftelli64
31 #define FOPEN(f, s, m) fopen_s(&f, s, m)
32 #else
33 #define fseeko fseek
34 #define ftello ftell
35 #define FOPEN(f, s, m) f = fopen(s, m)
36 #endif
37 
38 using svt_av1_e2e_tools::compare_image;
39 
compare(FrameQueue * other)40 bool FrameQueue::compare(FrameQueue *other) {
41     if (frame_count_ != other->get_frame_count()) {
42         printf("frame count(%u<-->%u) are different",
43                frame_count_,
44                other->get_frame_count());
45         return false;
46     }
47     bool is_same = true;
48     for (uint32_t i = 0; i < frame_count_; i++) {
49         VideoFrame *frame = take_frame_inorder(i);
50         VideoFrame *other_frame = other->take_frame_inorder(i);
51         is_same = compare_image(frame, other_frame);
52         if (!is_same) {
53             printf("ref_frame(%u) compare failed!!\n",
54                    (uint32_t)frame->timestamp);
55             break;
56         }
57     }
58 
59     return is_same;
60 }
61 
62 class FrameQueueFile : public FrameQueue {
63   public:
FrameQueueFile(const VideoFrameParam & param,const char * file_path)64     FrameQueueFile(const VideoFrameParam &param, const char *file_path)
65         : FrameQueue(param) {
66         queue_type_ = FRAME_QUEUE_FILE;
67         max_frame_ts_ = 0;
68         FOPEN(recon_file_, file_path, "wb");
69         record_list_.clear();
70     }
~FrameQueueFile()71     virtual ~FrameQueueFile() {
72         if (recon_file_) {
73             fflush(recon_file_);
74             fclose(recon_file_);
75             recon_file_ = nullptr;
76         }
77         record_list_.clear();
78     }
add_frame(VideoFrame * frame)79     void add_frame(VideoFrame *frame) override {
80         if (recon_file_ && frame->buf_size &&
81             frame->timestamp < (uint64_t)frame_count_) {
82             if (frame->timestamp >=
83                 max_frame_ts_) {  // new frame is larger than max timestamp
84                 fseeko(recon_file_, 0, SEEK_END);
85                 for (size_t i = max_frame_ts_; i < frame->timestamp + 1; ++i) {
86                     fwrite(frame->buffer, 1, frame->buf_size, recon_file_);
87                 }
88                 max_frame_ts_ = frame->timestamp;
89             }
90 
91             rewind(recon_file_);
92             uint64_t frame_num = frame->timestamp;
93             while (frame_num > 0) {
94                 int ret = fseeko(recon_file_, frame->buf_size, SEEK_CUR);
95                 if (ret != 0) {
96                     return;
97                 }
98                 frame_num--;
99             }
100             fwrite(frame->buffer, 1, frame->buf_size, recon_file_);
101             fflush(recon_file_);
102             record_list_.push_back((uint32_t)frame->timestamp);
103         }
104         delete frame;
105     }
take_frame(const uint64_t time_stamp)106     VideoFrame *take_frame(const uint64_t time_stamp) override {
107         if (recon_file_ == nullptr)
108             return nullptr;
109 
110         VideoFrame *new_frame = nullptr;
111         fseeko(recon_file_, 0, SEEK_END);
112         int64_t actual_size = ftello(recon_file_);
113         if (actual_size > 0 &&
114             ((uint64_t)actual_size) > ((time_stamp + 1) * frame_size_)) {
115             int ret = fseeko(recon_file_, time_stamp * frame_size_, 0);
116             if (ret != 0) {
117                 // printf("Error in fseeko  returnVal %i\n", ret);
118                 return nullptr;
119             }
120             new_frame = get_empty_frame();
121             if (new_frame) {
122                 size_t read_size =
123                     fread(new_frame->buffer, 1, frame_size_, recon_file_);
124                 if (read_size != frame_size_) {
125                     printf("read recon file error!\n");
126                     delete_frame(new_frame);
127                     new_frame = nullptr;
128                 } else {
129                     new_frame->timestamp = time_stamp;
130                 }
131             }
132         }
133 
134         return new_frame;
135     }
take_frame_inorder(const uint32_t index)136     VideoFrame *take_frame_inorder(const uint32_t index) override {
137         return take_frame(index);
138     }
recycle_frame(VideoFrame * frame)139     void recycle_frame(VideoFrame *frame) override {
140         delete_frame(frame);
141     }
delete_frame(VideoFrame * frame)142     void delete_frame(VideoFrame *frame) override {
143         delete frame;
144     }
is_compelete()145     bool is_compelete() override {
146         if (record_list_.size() < frame_count_)
147             return false;
148         std::sort(record_list_.begin(), record_list_.end());
149         if (record_list_.at(frame_count_ - 1) != frame_count_ - 1)
150             return false;
151         return true;
152     }
153 
154   public:
155     FILE *recon_file_; /**< file handle to dave reconstructed video frames, set
156                           it to public for accessable by create_frame_queue */
157 
158   protected:
159     uint64_t max_frame_ts_; /**< maximun timestamp of current frames in list */
160     std::vector<uint32_t> record_list_; /**< list of frame timstamp, to help
161                                            check if the file is completed*/
162 };
163 
164 class FrameQueueBufferSort_ASC {
165   public:
operator ()(VideoFrame * a,VideoFrame * b) const166     bool operator()(VideoFrame *a, VideoFrame *b) const {
167         return a->timestamp < b->timestamp;
168     }
169 };
170 
171 class FrameQueueBuffer : public FrameQueue {
172   public:
FrameQueueBuffer(VideoFrameParam fmt)173     FrameQueueBuffer(VideoFrameParam fmt) : FrameQueue(fmt) {
174         queue_type_ = FRAME_QUEUE_BUFFER;
175         frame_list_.clear();
176     }
~FrameQueueBuffer()177     virtual ~FrameQueueBuffer() {
178         while (frame_list_.size() > 0) {
179             delete frame_list_.back();
180             frame_list_.pop_back();
181         }
182     }
add_frame(VideoFrame * frame)183     void add_frame(VideoFrame *frame) override {
184         if (frame->timestamp < (uint64_t)frame_count_) {
185             frame_list_.push_back(frame);
186             std::sort(frame_list_.begin(),
187                       frame_list_.end(),
188                       FrameQueueBufferSort_ASC());
189         } else  // drop the frames out of limitation
190             delete frame;
191     }
take_frame(const uint64_t time_stamp)192     VideoFrame *take_frame(const uint64_t time_stamp) override {
193         for (VideoFrame *frame : frame_list_) {
194             if (frame->timestamp == time_stamp)
195                 return frame;
196         }
197         return nullptr;
198     }
take_frame_inorder(const uint32_t index)199     VideoFrame *take_frame_inorder(const uint32_t index) override {
200         if (index < frame_list_.size())
201             return frame_list_.at(index);
202         return nullptr;
203     }
recycle_frame(VideoFrame * frame)204     void recycle_frame(VideoFrame *frame) override {
205         frame->trim_buffer();
206     }
delete_frame(VideoFrame * frame)207     void delete_frame(VideoFrame *frame) override {
208         std::vector<VideoFrame *>::iterator it =
209             std::find(frame_list_.begin(), frame_list_.end(), frame);
210         if (it != frame_list_.end()) {  // if the video frame is in list
211             delete *it;
212             frame_list_.erase(it);
213         } else  // only delete the video frame not in list
214             delete frame;
215     }
is_compelete()216     bool is_compelete() override {
217         if (frame_list_.size() < frame_count_)
218             return false;
219 
220         VideoFrame *frame = frame_list_.at(frame_count_ - 1);
221         if (frame == nullptr || frame->timestamp != frame_count_ - 1)
222             return false;
223         return true;
224     }
225 
226   protected:
227     std::vector<VideoFrame *> frame_list_; /**< list of frame containers */
228 };
229 
230 class RefQueue : public ICompareQueue, FrameQueueBuffer {
231   public:
RefQueue(VideoFrameParam fmt,FrameQueue * my_friend)232     RefQueue(VideoFrameParam fmt, FrameQueue *my_friend)
233         : FrameQueueBuffer(fmt) {
234         friend_ = my_friend;
235         frame_vec_.clear();
236 #ifdef ENABLE_DEBUG_MONITOR
237         recon_monitor_ = nullptr;
238         ref_monitor_ = nullptr;
239 #endif
240     }
~RefQueue()241     virtual ~RefQueue() {
242         while (frame_vec_.size()) {
243             const VideoFrame *p = frame_vec_.back();
244             frame_vec_.pop_back();
245             if (p) {
246                 // printf("Reference queue still remain frames when
247                 // delete(%u)\n",
248                 //       (uint32_t)p->timestamp);
249                 delete p;
250             }
251         }
252         friend_ = nullptr;
253 #ifdef ENABLE_DEBUG_MONITOR
254         if (recon_monitor_) {
255             delete recon_monitor_;
256             recon_monitor_ = nullptr;
257         }
258         if (ref_monitor_) {
259             delete ref_monitor_;
260             ref_monitor_ = nullptr;
261         }
262 #endif
263     }
264 
265   public:
compare_video(VideoFrame & frame)266     bool compare_video(VideoFrame &frame) override {
267         VideoFrame *friend_frame = friend_->take_frame(frame.timestamp);
268         if (friend_frame) {
269             draw_frames(&frame, friend_frame);
270             bool is_same = compare_image(friend_frame, &frame);
271             if (!is_same) {
272                 printf("ref_frame(%u) compare failed!!\n",
273                        (uint32_t)frame.timestamp);
274             }
275             friend_->recycle_frame(friend_frame);
276             return is_same;
277         } else {
278             clone_frame(frame);
279         }
280         return true; /**< default return suceess if not found recon frame */
281     }
flush_video()282     bool flush_video() override {
283         bool is_all_same = true;
284         for (VideoFrame *frame : frame_vec_) {
285             VideoFrame *friend_frame = friend_->take_frame(frame->timestamp);
286             if (friend_frame) {
287                 draw_frames(frame, friend_frame);
288                 if (!compare_image(friend_frame, frame)) {
289                     printf("ref_frame(%u) compare failed!!\n",
290                            (uint32_t)frame->timestamp);
291                     is_all_same = false;
292                 }
293                 friend_->recycle_frame(friend_frame);
294             }
295         }
296         return is_all_same;
297     }
298 
299   private:
clone_frame(const VideoFrame & frame)300     void clone_frame(const VideoFrame &frame) {
301         VideoFrame *new_frame = new VideoFrame(frame);
302         if (new_frame)
303             frame_vec_.push_back(new_frame);
304         else
305             printf("out of memory for clone video frame!!\n");
306     }
draw_frames(const VideoFrame * frame,const VideoFrame * friend_frame)307     void draw_frames(const VideoFrame *frame, const VideoFrame *friend_frame) {
308 #ifdef ENABLE_DEBUG_MONITOR
309         if (ref_monitor_ == nullptr) {
310             /** walk-around for bit-depth is fixed set 10-bit from ref-decoder,
311              * here to use bit-depth of friend frame*/
312             ref_monitor_ = new VideoMonitor(frame->width,
313                                             frame->height,
314                                             frame->stride[0],
315                                             friend_frame->bits_per_sample,
316                                             false,
317                                             "Ref decode");
318         }
319         if (ref_monitor_) {
320             ref_monitor_->draw_frame(
321                 frame->planes[0], frame->planes[1], frame->planes[2]);
322         }
323         // Output to monitor for debug
324         if (recon_monitor_ == nullptr) {
325             recon_monitor_ = new VideoMonitor(friend_frame->width,
326                                               friend_frame->height,
327                                               friend_frame->stride[0],
328                                               friend_frame->bits_per_sample,
329                                               false,
330                                               "Recon");
331         }
332         if (recon_monitor_) {
333             recon_monitor_->draw_frame(friend_frame->planes[0],
334                                        friend_frame->planes[1],
335                                        friend_frame->planes[2]);
336         }
337 #else
338         (void)frame;
339         (void)friend_frame;
340 #endif
341     }
342 
343   private:
344     FrameQueue *friend_;
345     std::vector<VideoFrame *> frame_vec_;
346 #ifdef ENABLE_DEBUG_MONITOR
347     VideoMonitor *recon_monitor_;
348     VideoMonitor *ref_monitor_;
349 #endif
350 };
351 
create_frame_queue(const VideoFrameParam & param,const char * file_path)352 FrameQueue *create_frame_queue(const VideoFrameParam &param,
353                                const char *file_path) {
354     FrameQueueFile *new_queue = new FrameQueueFile(param, file_path);
355     if (new_queue) {
356         if (new_queue->recon_file_ == nullptr) {
357             delete new_queue;
358             new_queue = nullptr;
359         }
360     }
361     return new_queue;
362 }
363 
create_frame_queue(const VideoFrameParam & param)364 FrameQueue *create_frame_queue(const VideoFrameParam &param) {
365     return new FrameQueueBuffer(param);
366 }
367 
create_ref_compare_queue(const VideoFrameParam & param,FrameQueue * recon)368 ICompareQueue *create_ref_compare_queue(const VideoFrameParam &param,
369                                         FrameQueue *recon) {
370     return new RefQueue(param, recon);
371 }
372