1 /*****************************************************************************
2  * Copyright (C) 2013 x265 project
3  *
4  * Authors: Steve Borho <steve@borho.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
19  *
20  * This program is also available under a commercial proprietary license.
21  * For more information, contact us at license @ x265.com.
22  *****************************************************************************/
23 
24 #if _MSC_VER
25 #pragma warning(disable: 4127) // conditional expression is constant, yes I know
26 #endif
27 
28 #include "x265.h"
29 #include "x265-extras.h"
30 #include "x265cli.h"
31 
32 #include "common.h"
33 #include "input/input.h"
34 #include "output/output.h"
35 #include "output/reconplay.h"
36 
37 #include "param.h"
38 #include "cpu.h"
39 
40 #if HAVE_VLD
41 /* Visual Leak Detector */
42 #include <vld.h>
43 #endif
44 
45 #include <signal.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 
49 #include <string>
50 #include <ostream>
51 #include <fstream>
52 #include <queue>
53 
54 #define CONSOLE_TITLE_SIZE 200
55 #ifdef _WIN32
56 #include <windows.h>
57 static char orgConsoleTitle[CONSOLE_TITLE_SIZE] = "";
58 #else
59 #define GetConsoleTitle(t, n)
60 #define SetConsoleTitle(t)
61 #define SetThreadExecutionState(es)
62 #endif
63 
64 using namespace X265_NS;
65 
66 /* Ctrl-C handler */
67 static volatile sig_atomic_t b_ctrl_c /* = 0 */;
sigint_handler(int)68 static void sigint_handler(int)
69 {
70     b_ctrl_c = 1;
71 }
72 
73 struct CLIOptions
74 {
75     InputFile* input;
76     ReconFile* recon;
77     OutputFile* output;
78     FILE*       qpfile;
79     FILE*       csvfpt;
80     const char* csvfn;
81     const char* reconPlayCmd;
82     const x265_api* api;
83     x265_param* param;
84     bool bProgress;
85     bool bForceY4m;
86     bool bDither;
87     int csvLogLevel;
88     uint32_t seek;              // number of frames to skip from the beginning
89     uint32_t framesToBeEncoded; // number of frames to encode
90     uint64_t totalbytes;
91     int64_t startTime;
92     int64_t prevUpdateTime;
93 
94     /* in microseconds */
95     static const int UPDATE_INTERVAL = 250000;
96 
CLIOptionsCLIOptions97     CLIOptions()
98     {
99         input = NULL;
100         recon = NULL;
101         output = NULL;
102         qpfile = NULL;
103         csvfpt = NULL;
104         csvfn = NULL;
105         reconPlayCmd = NULL;
106         api = NULL;
107         param = NULL;
108         framesToBeEncoded = seek = 0;
109         totalbytes = 0;
110         bProgress = true;
111         bForceY4m = false;
112         startTime = x265_mdate();
113         prevUpdateTime = 0;
114         bDither = false;
115         csvLogLevel = 0;
116     }
117 
118     void destroy();
119     void printStatus(uint32_t frameNum);
120     bool parse(int argc, char **argv);
121     bool parseQPFile(x265_picture &pic_org);
122 };
123 
destroy()124 void CLIOptions::destroy()
125 {
126     if (input)
127         input->release();
128     input = NULL;
129     if (recon)
130         recon->release();
131     recon = NULL;
132     if (qpfile)
133         fclose(qpfile);
134     qpfile = NULL;
135     if (csvfpt)
136         fclose(csvfpt);
137     csvfpt = NULL;
138     if (output)
139         output->release();
140     output = NULL;
141 }
142 
printStatus(uint32_t frameNum)143 void CLIOptions::printStatus(uint32_t frameNum)
144 {
145     char buf[200];
146     int64_t time = x265_mdate();
147 
148     if (!bProgress || !frameNum || (prevUpdateTime && time - prevUpdateTime < UPDATE_INTERVAL))
149         return;
150 
151     int64_t elapsed = time - startTime;
152     double fps = elapsed > 0 ? frameNum * 1000000. / elapsed : 0;
153     float bitrate = 0.008f * totalbytes * (param->fpsNum / param->fpsDenom) / ((float)frameNum);
154     if (framesToBeEncoded)
155     {
156         int eta = (int)(elapsed * (framesToBeEncoded - frameNum) / ((int64_t)frameNum * 1000000));
157         sprintf(buf, "x265 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d",
158                 100. * frameNum / framesToBeEncoded, frameNum, framesToBeEncoded, fps, bitrate,
159                 eta / 3600, (eta / 60) % 60, eta % 60);
160     }
161     else
162         sprintf(buf, "x265 %d frames: %.2f fps, %.2f kb/s", frameNum, fps, bitrate);
163 
164     fprintf(stderr, "%s  \r", buf + 5);
165     SetConsoleTitle(buf);
166     fflush(stderr); // needed in windows
167     prevUpdateTime = time;
168 }
169 
parse(int argc,char ** argv)170 bool CLIOptions::parse(int argc, char **argv)
171 {
172     bool bError = false;
173     int bShowHelp = false;
174     int inputBitDepth = 8;
175     int outputBitDepth = 0;
176     int reconFileBitDepth = 0;
177     const char *inputfn = NULL;
178     const char *reconfn = NULL;
179     const char *outputfn = NULL;
180     const char *preset = NULL;
181     const char *tune = NULL;
182     const char *profile = NULL;
183 
184     if (argc <= 1)
185     {
186         x265_log(NULL, X265_LOG_ERROR, "No input file. Run x265 --help for a list of options.\n");
187         return true;
188     }
189 
190     /* Presets are applied before all other options. */
191     for (optind = 0;; )
192     {
193         int c = getopt_long(argc, argv, short_options, long_options, NULL);
194         if (c == -1)
195             break;
196         else if (c == 'p')
197             preset = optarg;
198         else if (c == 't')
199             tune = optarg;
200         else if (c == 'D')
201             outputBitDepth = atoi(optarg);
202         else if (c == 'P')
203             profile = optarg;
204         else if (c == '?')
205             bShowHelp = true;
206     }
207 
208     if (!outputBitDepth && profile)
209     {
210         /* try to derive the output bit depth from the requested profile */
211         if (strstr(profile, "10"))
212             outputBitDepth = 10;
213         else if (strstr(profile, "12"))
214             outputBitDepth = 12;
215         else
216             outputBitDepth = 8;
217     }
218 
219     api = x265_api_get(outputBitDepth);
220     if (!api)
221     {
222         x265_log(NULL, X265_LOG_WARNING, "falling back to default bit-depth\n");
223         api = x265_api_get(0);
224     }
225 
226     param = api->param_alloc();
227     if (!param)
228     {
229         x265_log(NULL, X265_LOG_ERROR, "param alloc failed\n");
230         return true;
231     }
232 
233     if (api->param_default_preset(param, preset, tune) < 0)
234     {
235         x265_log(NULL, X265_LOG_ERROR, "preset or tune unrecognized\n");
236         return true;
237     }
238 
239     if (bShowHelp)
240     {
241         printVersion(param, api);
242         showHelp(param);
243     }
244 
245     for (optind = 0;; )
246     {
247         int long_options_index = -1;
248         int c = getopt_long(argc, argv, short_options, long_options, &long_options_index);
249         if (c == -1)
250             break;
251 
252         switch (c)
253         {
254         case 'h':
255             printVersion(param, api);
256             showHelp(param);
257             break;
258 
259         case 'V':
260             printVersion(param, api);
261             x265_report_simd(param);
262             exit(0);
263 
264         default:
265             if (long_options_index < 0 && c > 0)
266             {
267                 for (size_t i = 0; i < sizeof(long_options) / sizeof(long_options[0]); i++)
268                 {
269                     if (long_options[i].val == c)
270                     {
271                         long_options_index = (int)i;
272                         break;
273                     }
274                 }
275 
276                 if (long_options_index < 0)
277                 {
278                     /* getopt_long might have already printed an error message */
279                     if (c != 63)
280                         x265_log(NULL, X265_LOG_WARNING, "internal error: short option '%c' has no long option\n", c);
281                     return true;
282                 }
283             }
284             if (long_options_index < 0)
285             {
286                 x265_log(NULL, X265_LOG_WARNING, "short option '%c' unrecognized\n", c);
287                 return true;
288             }
289 #define OPT(longname) \
290     else if (!strcmp(long_options[long_options_index].name, longname))
291 #define OPT2(name1, name2) \
292     else if (!strcmp(long_options[long_options_index].name, name1) || \
293              !strcmp(long_options[long_options_index].name, name2))
294 
295             if (0) ;
296             OPT2("frame-skip", "seek") this->seek = (uint32_t)x265_atoi(optarg, bError);
297             OPT("frames") this->framesToBeEncoded = (uint32_t)x265_atoi(optarg, bError);
298             OPT("csv") this->csvfn = optarg;
299             OPT("csv-log-level") this->csvLogLevel = x265_atoi(optarg, bError);
300             OPT("no-progress") this->bProgress = false;
301             OPT("output") outputfn = optarg;
302             OPT("input") inputfn = optarg;
303             OPT("recon") reconfn = optarg;
304             OPT("input-depth") inputBitDepth = (uint32_t)x265_atoi(optarg, bError);
305             OPT("dither") this->bDither = true;
306             OPT("recon-depth") reconFileBitDepth = (uint32_t)x265_atoi(optarg, bError);
307             OPT("y4m") this->bForceY4m = true;
308             OPT("profile") /* handled above */;
309             OPT("preset")  /* handled above */;
310             OPT("tune")    /* handled above */;
311             OPT("output-depth")   /* handled above */;
312             OPT("recon-y4m-exec") reconPlayCmd = optarg;
313             OPT("qpfile")
314             {
315                 this->qpfile = fopen(optarg, "rb");
316                 if (!this->qpfile)
317                 {
318                     x265_log(param, X265_LOG_ERROR, "%s qpfile not found or error in opening qp file\n", optarg);
319                     return false;
320                 }
321             }
322             else
323                 bError |= !!api->param_parse(param, long_options[long_options_index].name, optarg);
324 
325             if (bError)
326             {
327                 const char *name = long_options_index > 0 ? long_options[long_options_index].name : argv[optind - 2];
328                 x265_log(NULL, X265_LOG_ERROR, "invalid argument: %s = %s\n", name, optarg);
329                 return true;
330             }
331 #undef OPT
332         }
333     }
334 
335     if (optind < argc && !inputfn)
336         inputfn = argv[optind++];
337     if (optind < argc && !outputfn)
338         outputfn = argv[optind++];
339     if (optind < argc)
340     {
341         x265_log(param, X265_LOG_WARNING, "extra unused command arguments given <%s>\n", argv[optind]);
342         return true;
343     }
344 
345     if (argc <= 1)
346     {
347         api->param_default(param);
348         printVersion(param, api);
349         showHelp(param);
350     }
351 
352     if (!inputfn || !outputfn)
353     {
354         x265_log(param, X265_LOG_ERROR, "input or output file not specified, try --help for help\n");
355         return true;
356     }
357 
358     if (param->internalBitDepth != api->bit_depth)
359     {
360         x265_log(param, X265_LOG_ERROR, "Only bit depths of %d are supported in this build\n", api->bit_depth);
361         return true;
362     }
363 
364     InputFileInfo info;
365     info.filename = inputfn;
366     info.depth = inputBitDepth;
367     info.csp = param->internalCsp;
368     info.width = param->sourceWidth;
369     info.height = param->sourceHeight;
370     info.fpsNum = param->fpsNum;
371     info.fpsDenom = param->fpsDenom;
372     info.sarWidth = param->vui.sarWidth;
373     info.sarHeight = param->vui.sarHeight;
374     info.skipFrames = seek;
375     info.frameCount = 0;
376     getParamAspectRatio(param, info.sarWidth, info.sarHeight);
377 
378     this->input = InputFile::open(info, this->bForceY4m);
379     if (!this->input || this->input->isFail())
380     {
381         x265_log(param, X265_LOG_ERROR, "unable to open input file <%s>\n", inputfn);
382         return true;
383     }
384 
385     if (info.depth < 8 || info.depth > 16)
386     {
387         x265_log(param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n", inputBitDepth);
388         return true;
389     }
390 
391     /* Unconditionally accept height/width/csp from file info */
392     param->sourceWidth = info.width;
393     param->sourceHeight = info.height;
394     param->internalCsp = info.csp;
395 
396     /* Accept fps and sar from file info if not specified by user */
397     if (param->fpsDenom == 0 || param->fpsNum == 0)
398     {
399         param->fpsDenom = info.fpsDenom;
400         param->fpsNum = info.fpsNum;
401     }
402     if (!param->vui.aspectRatioIdc && info.sarWidth && info.sarHeight)
403         setParamAspectRatio(param, info.sarWidth, info.sarHeight);
404     if (this->framesToBeEncoded == 0 && info.frameCount > (int)seek)
405         this->framesToBeEncoded = info.frameCount - seek;
406     param->totalFrames = this->framesToBeEncoded;
407 
408     /* Force CFR until we have support for VFR */
409     info.timebaseNum = param->fpsDenom;
410     info.timebaseDenom = param->fpsNum;
411 
412     if (api->param_apply_profile(param, profile))
413         return true;
414 
415     if (param->logLevel >= X265_LOG_INFO)
416     {
417         char buf[128];
418         int p = sprintf(buf, "%dx%d fps %d/%d %sp%d", param->sourceWidth, param->sourceHeight,
419                         param->fpsNum, param->fpsDenom, x265_source_csp_names[param->internalCsp], info.depth);
420 
421         int width, height;
422         getParamAspectRatio(param, width, height);
423         if (width && height)
424             p += sprintf(buf + p, " sar %d:%d", width, height);
425 
426         if (framesToBeEncoded <= 0 || info.frameCount <= 0)
427             strcpy(buf + p, " unknown frame count");
428         else
429             sprintf(buf + p, " frames %u - %d of %d", this->seek, this->seek + this->framesToBeEncoded - 1, info.frameCount);
430 
431         general_log(param, input->getName(), X265_LOG_INFO, "%s\n", buf);
432     }
433 
434     this->input->startReader();
435 
436     if (reconfn)
437     {
438         if (reconFileBitDepth == 0)
439             reconFileBitDepth = param->internalBitDepth;
440         this->recon = ReconFile::open(reconfn, param->sourceWidth, param->sourceHeight, reconFileBitDepth,
441                                       param->fpsNum, param->fpsDenom, param->internalCsp);
442         if (this->recon->isFail())
443         {
444             x265_log(param, X265_LOG_WARNING, "unable to write reconstructed outputs file\n");
445             this->recon->release();
446             this->recon = 0;
447         }
448         else
449             general_log(param, this->recon->getName(), X265_LOG_INFO,
450                     "reconstructed images %dx%d fps %d/%d %s\n",
451                     param->sourceWidth, param->sourceHeight, param->fpsNum, param->fpsDenom,
452                     x265_source_csp_names[param->internalCsp]);
453     }
454 
455     this->output = OutputFile::open(outputfn, info);
456     if (this->output->isFail())
457     {
458         x265_log(param, X265_LOG_ERROR, "failed to open output file <%s> for writing\n", outputfn);
459         return true;
460     }
461     general_log(param, this->output->getName(), X265_LOG_INFO, "output file: %s\n", outputfn);
462     return false;
463 }
464 
parseQPFile(x265_picture & pic_org)465 bool CLIOptions::parseQPFile(x265_picture &pic_org)
466 {
467     int32_t num = -1, qp, ret;
468     char type;
469     uint32_t filePos;
470     pic_org.forceqp = 0;
471     pic_org.sliceType = X265_TYPE_AUTO;
472     while (num < pic_org.poc)
473     {
474         filePos = ftell(qpfile);
475         qp = -1;
476         ret = fscanf(qpfile, "%d %c%*[ \t]%d\n", &num, &type, &qp);
477 
478         if (num > pic_org.poc || ret == EOF)
479         {
480             fseek(qpfile, filePos, SEEK_SET);
481             break;
482         }
483         if (num < pic_org.poc && ret >= 2)
484             continue;
485         if (ret == 3 && qp >= 0)
486             pic_org.forceqp = qp + 1;
487         if (type == 'I') pic_org.sliceType = X265_TYPE_IDR;
488         else if (type == 'i') pic_org.sliceType = X265_TYPE_I;
489         else if (type == 'P') pic_org.sliceType = X265_TYPE_P;
490         else if (type == 'B') pic_org.sliceType = X265_TYPE_BREF;
491         else if (type == 'b') pic_org.sliceType = X265_TYPE_B;
492         else ret = 0;
493         if (ret < 2 || qp < -1 || qp > 51)
494             return 0;
495     }
496     return 1;
497 }
498 
499 /* CLI return codes:
500  *
501  * 0 - encode successful
502  * 1 - unable to parse command line
503  * 2 - unable to open encoder
504  * 3 - unable to generate stream headers
505  * 4 - encoder abort
506  * 5 - unable to open csv file */
507 
main(int argc,char ** argv)508 int main(int argc, char **argv)
509 {
510 #if HAVE_VLD
511     // This uses Microsoft's proprietary WCHAR type, but this only builds on Windows to start with
512     VLDSetReportOptions(VLD_OPT_REPORT_TO_DEBUGGER | VLD_OPT_REPORT_TO_FILE, L"x265_leaks.txt");
513 #endif
514     PROFILE_INIT();
515     THREAD_NAME("API", 0);
516 
517     GetConsoleTitle(orgConsoleTitle, CONSOLE_TITLE_SIZE);
518     SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED);
519 
520     ReconPlay* reconPlay = NULL;
521     CLIOptions cliopt;
522 
523     if (cliopt.parse(argc, argv))
524     {
525         cliopt.destroy();
526         if (cliopt.api)
527             cliopt.api->param_free(cliopt.param);
528         exit(1);
529     }
530 
531     x265_param* param = cliopt.param;
532     const x265_api* api = cliopt.api;
533 
534     /* This allows muxers to modify bitstream format */
535     cliopt.output->setParam(param);
536 
537     if (cliopt.reconPlayCmd)
538         reconPlay = new ReconPlay(cliopt.reconPlayCmd, *param);
539 
540     /* note: we could try to acquire a different libx265 API here based on
541      * the profile found during option parsing, but it must be done before
542      * opening an encoder */
543 
544     x265_encoder *encoder = api->encoder_open(param);
545     if (!encoder)
546     {
547         x265_log(param, X265_LOG_ERROR, "failed to open encoder\n");
548         cliopt.destroy();
549         api->param_free(param);
550         api->cleanup();
551         exit(2);
552     }
553 
554     /* get the encoder parameters post-initialization */
555     api->encoder_parameters(encoder, param);
556 
557     if (cliopt.csvfn)
558     {
559         cliopt.csvfpt = x265_csvlog_open(*api, *param, cliopt.csvfn, cliopt.csvLogLevel);
560         if (!cliopt.csvfpt)
561         {
562             x265_log(param, X265_LOG_ERROR, "Unable to open CSV log file <%s>, aborting\n", cliopt.csvfn);
563             cliopt.destroy();
564             if (cliopt.api)
565                 cliopt.api->param_free(cliopt.param);
566             exit(5);
567         }
568     }
569 
570     /* Control-C handler */
571     if (signal(SIGINT, sigint_handler) == SIG_ERR)
572         x265_log(param, X265_LOG_ERROR, "Unable to register CTRL+C handler: %s\n", strerror(errno));
573 
574     x265_picture pic_orig, pic_out;
575     x265_picture *pic_in = &pic_orig;
576     /* Allocate recon picture if analysisMode is enabled */
577     std::priority_queue<int64_t>* pts_queue = cliopt.output->needPTS() ? new std::priority_queue<int64_t>() : NULL;
578     x265_picture *pic_recon = (cliopt.recon || !!param->analysisMode || pts_queue || reconPlay || cliopt.csvLogLevel) ? &pic_out : NULL;
579     uint32_t inFrameCount = 0;
580     uint32_t outFrameCount = 0;
581     x265_nal *p_nal;
582     x265_stats stats;
583     uint32_t nal;
584     int16_t *errorBuf = NULL;
585     int ret = 0;
586 
587     if (!param->bRepeatHeaders)
588     {
589         if (api->encoder_headers(encoder, &p_nal, &nal) < 0)
590         {
591             x265_log(param, X265_LOG_ERROR, "Failure generating stream headers\n");
592             ret = 3;
593             goto fail;
594         }
595         else
596             cliopt.totalbytes += cliopt.output->writeHeaders(p_nal, nal);
597     }
598 
599     api->picture_init(param, pic_in);
600 
601     if (cliopt.bDither)
602     {
603         errorBuf = X265_MALLOC(int16_t, param->sourceWidth + 1);
604         if (errorBuf)
605             memset(errorBuf, 0, (param->sourceWidth + 1) * sizeof(int16_t));
606         else
607             cliopt.bDither = false;
608     }
609 
610     // main encoder loop
611     while (pic_in && !b_ctrl_c)
612     {
613         pic_orig.poc = inFrameCount;
614         if (cliopt.qpfile)
615         {
616             if (!cliopt.parseQPFile(pic_orig))
617             {
618                 x265_log(NULL, X265_LOG_ERROR, "can't parse qpfile for frame %d\n", pic_in->poc);
619                 fclose(cliopt.qpfile);
620                 cliopt.qpfile = NULL;
621             }
622         }
623 
624         if (cliopt.framesToBeEncoded && inFrameCount >= cliopt.framesToBeEncoded)
625             pic_in = NULL;
626         else if (cliopt.input->readPicture(pic_orig))
627             inFrameCount++;
628         else
629             pic_in = NULL;
630 
631         if (pic_in)
632         {
633             if (pic_in->bitDepth > param->internalBitDepth && cliopt.bDither)
634             {
635                 x265_dither_image(*api, *pic_in, param->sourceWidth, param->sourceHeight, errorBuf, param->internalBitDepth);
636                 pic_in->bitDepth = param->internalBitDepth;
637             }
638             /* Overwrite PTS */
639             pic_in->pts = pic_in->poc;
640         }
641 
642         int numEncoded = api->encoder_encode(encoder, &p_nal, &nal, pic_in, pic_recon);
643         if (numEncoded < 0)
644         {
645             b_ctrl_c = 1;
646             ret = 4;
647             break;
648         }
649 
650         if (reconPlay && numEncoded)
651             reconPlay->writePicture(*pic_recon);
652 
653         outFrameCount += numEncoded;
654 
655         if (numEncoded && pic_recon && cliopt.recon)
656             cliopt.recon->writePicture(pic_out);
657         if (nal)
658         {
659             cliopt.totalbytes += cliopt.output->writeFrame(p_nal, nal, pic_out);
660             if (pts_queue)
661             {
662                 pts_queue->push(-pic_out.pts);
663                 if (pts_queue->size() > 2)
664                     pts_queue->pop();
665             }
666         }
667 
668         cliopt.printStatus(outFrameCount);
669         if (numEncoded && cliopt.csvLogLevel)
670             x265_csvlog_frame(cliopt.csvfpt, *param, *pic_recon, cliopt.csvLogLevel);
671     }
672 
673     /* Flush the encoder */
674     while (!b_ctrl_c)
675     {
676         int numEncoded = api->encoder_encode(encoder, &p_nal, &nal, NULL, pic_recon);
677         if (numEncoded < 0)
678         {
679             ret = 4;
680             break;
681         }
682 
683         if (reconPlay && numEncoded)
684             reconPlay->writePicture(*pic_recon);
685 
686         outFrameCount += numEncoded;
687         if (numEncoded && pic_recon && cliopt.recon)
688             cliopt.recon->writePicture(pic_out);
689         if (nal)
690         {
691             cliopt.totalbytes += cliopt.output->writeFrame(p_nal, nal, pic_out);
692             if (pts_queue)
693             {
694                 pts_queue->push(-pic_out.pts);
695                 if (pts_queue->size() > 2)
696                     pts_queue->pop();
697             }
698         }
699 
700         cliopt.printStatus(outFrameCount);
701         if (numEncoded && cliopt.csvLogLevel)
702             x265_csvlog_frame(cliopt.csvfpt, *param, *pic_recon, cliopt.csvLogLevel);
703 
704         if (!numEncoded)
705             break;
706     }
707 
708     /* clear progress report */
709     if (cliopt.bProgress)
710         fprintf(stderr, "%*s\r", 80, " ");
711 
712 fail:
713 
714     delete reconPlay;
715 
716     api->encoder_get_stats(encoder, &stats, sizeof(stats));
717     if (cliopt.csvfpt && !b_ctrl_c)
718         x265_csvlog_encode(cliopt.csvfpt, *api, *param, stats, cliopt.csvLogLevel, argc, argv);
719     api->encoder_close(encoder);
720 
721     int64_t second_largest_pts = 0;
722     int64_t largest_pts = 0;
723     if (pts_queue && pts_queue->size() >= 2)
724     {
725         second_largest_pts = -pts_queue->top();
726         pts_queue->pop();
727         largest_pts = -pts_queue->top();
728         pts_queue->pop();
729         delete pts_queue;
730         pts_queue = NULL;
731     }
732     cliopt.output->closeFile(largest_pts, second_largest_pts);
733 
734     if (b_ctrl_c)
735         general_log(param, NULL, X265_LOG_INFO, "aborted at input frame %d, output frame %d\n",
736                     cliopt.seek + inFrameCount, stats.encodedPictureCount);
737 
738     api->cleanup(); /* Free library singletons */
739 
740     cliopt.destroy();
741 
742     api->param_free(param);
743 
744     X265_FREE(errorBuf);
745 
746     SetConsoleTitle(orgConsoleTitle);
747     SetThreadExecutionState(ES_CONTINUOUS);
748 
749 #if HAVE_VLD
750     assert(VLDReportLeaks() == 0);
751 #endif
752 
753     return ret;
754 }
755