1 /*
2 Copyright (C) 2019-2020 Andreas Weber <octave@josoansi.de>
3
4 This file is part of octave-video; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "cap_ffmpeg_impl_ov.hpp"
19
20 // PKG_ADD: autoload ("__ffmpeg_defines__", "cap_ffmpeg_wrapper.oct");
21 // PKG_DEL: autoload ("__ffmpeg_defines__", "cap_ffmpeg_wrapper.oct", "remove");
22 DEFUN_DLD(__ffmpeg_defines__, args, nargout,
23 "-*- texinfo -*-\n\
24 @deftypefn {Loadable Function} {@var{def} =} __ffmpeg_defines__ ()\n\
25 undocumented internal function\n\
26 @end deftypefn")
27 {
28 octave_value_list retval;
29 octave_scalar_map opt;
30
31 opt.contents ("LIBAVUTIL_BUILD") = LIBAVUTIL_BUILD;
32 opt.contents ("LIBAVUTIL_IDENT") = LIBAVUTIL_IDENT;
33
34 opt.contents ("LIBSWSCALE_BUILD") = LIBSWSCALE_BUILD;
35 opt.contents ("LIBSWSCALE_IDENT") = LIBSWSCALE_IDENT;
36
37 opt.contents ("LIBAVCODEC_BUILD") = LIBAVCODEC_BUILD;
38 opt.contents ("LIBAVCODEC_IDENT") = LIBAVCODEC_IDENT;
39
40 opt.contents ("LIBAVFORMAT_BUILD") = LIBAVFORMAT_BUILD;
41 opt.contents ("LIBAVFORMAT_IDENT") = LIBAVFORMAT_IDENT;
42
43
44 //join ident
45 opt.contents ("LIBAV_IDENT") = LIBAVUTIL_IDENT ", " LIBSWSCALE_IDENT ", " LIBAVCODEC_IDENT ", " LIBAVFORMAT_IDENT;
46
47
48 retval.append (opt);
49
50 return retval;
51 }
52
53 // PKG_ADD: autoload ("__ffmpeg_output_formats__", "cap_ffmpeg_wrapper.oct");
54 // PKG_DEL: autoload ("__ffmpeg_output_formats__", "cap_ffmpeg_wrapper.oct", "remove");
55 DEFUN_DLD(__ffmpeg_output_formats__, args, nargout,
56 "-*- texinfo -*-\n\
57 @deftypefn {Loadable Function} {@var{f} =} __ffmpeg_output_formats__ ()\n\
58 undocumented internal function\n\
59 @end deftypefn")
60 {
61 av_register_all();
62
63 octave_idx_type n = 0;
64
65 // first loop to get numer of output formats
66 AVOutputFormat * oformat = av_oformat_next(NULL);
67 while (oformat != NULL)
68 {
69 n++;
70 oformat = av_oformat_next (oformat);
71 }
72
73 Cell names (n, 1);
74 Cell long_names (n, 1);
75 Cell mime_types (n, 1);
76 Cell extensions (n, 1);
77 Cell codecs (n, 1);
78
79 // second loop, now fill the cells
80 oformat = av_oformat_next(NULL);
81 int i = 0;
82 while(oformat != NULL)
83 {
84 names (i) = oformat->name;
85 long_names (i) = oformat->long_name;
86 mime_types (i) = oformat->mime_type;
87 extensions (i) = oformat->extensions;
88
89 octave_map map_codecs;
90
91 if (oformat->codec_tag)
92 {
93 // printf ("%s %s %s\n", oformat->name, oformat->long_name, oformat->mime_type);
94
95 std::vector<std::string> video_codecs;
96 const AVCodecTag * ptags = oformat->codec_tag[0];
97 while (ptags->id != AV_CODEC_ID_NONE)
98 {
99 AVCodecID id = (AVCodecID) ptags->id;
100 // get descriptor
101 const AVCodecDescriptor* d = avcodec_descriptor_get (id);
102 if (d)
103 {
104 // only add encoder video codecs
105 if (d->type == AVMEDIA_TYPE_VIDEO)
106 {
107 // prüfen, ob es einen encoder gibt
108 if (avcodec_find_encoder (d->id))
109 {
110 unsigned int tag = ptags->tag;
111
112 if (! strcmp (oformat->name, "mp4")) // use riff
113 {
114 const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 };
115 tag = av_codec_get_tag(table, id);
116 }
117
118 char buf[5];
119 snprintf (buf, 5, "%c%c%c%c", CV_TAG_TO_PRINTABLE_CHAR4(tag));
120
121 //printf("fourcc tag 0x%08x '%s' codec_id %04X\n", tag, buf, id);
122
123 video_codecs.push_back (buf);
124 }
125 }
126
127 }
128
129 ptags++;
130
131 }
132
133 // unique but keep order
134 {
135 auto last = std::unique(video_codecs.begin(), video_codecs.end());
136 video_codecs.erase (last, video_codecs.end());
137 Cell codec_fourcc (video_codecs.size (), 1);
138 for (unsigned int k = 0; k < video_codecs.size (); ++k)
139 codec_fourcc(k) = video_codecs[k];
140 codecs (i) = codec_fourcc;
141 }
142 }
143
144 oformat = av_oformat_next(oformat);
145 i++;
146 }
147
148 octave_map m;
149
150 m.assign ("name", names);
151 m.assign ("long_name", long_names);
152 m.assign ("mime_type", mime_types);
153 m.assign ("extensions", extensions);
154 m.assign ("codecs", codecs);
155
156 return octave_value (m);
157 }
158
159 /****************** CvCapture_FFMPEG **************************/
160
get_cap_from_ov(octave_value ov)161 CvCapture_FFMPEG* get_cap_from_ov (octave_value ov)
162 {
163 if (!capture_type_loaded)
164 {
165 CvCapture_FFMPEG::register_type();
166 capture_type_loaded = true;
167 }
168
169 if (ov.type_id() != CvCapture_FFMPEG::static_type_id())
170 {
171 error("get_handler_from_ov: Not a valid CvCapture_FFMPEG");
172 return 0;
173 }
174
175 CvCapture_FFMPEG* p = 0;
176 const octave_base_value& rep = ov.get_rep();
177 p = &((CvCapture_FFMPEG &)rep);
178 return p;
179 }
180
181 // PKG_ADD: autoload ("__cap_open__", "cap_ffmpeg_wrapper.oct");
182 // PKG_DEL: autoload ("__cap_open__", "cap_ffmpeg_wrapper.oct", "remove");
183 DEFUN_DLD(__cap_open__, args, nargout,
184 "-*- texinfo -*-\n\
185 @deftypefn {Loadable Function} {@var{h} =} __cap_open__ (@var{filename})\n\
186 Creates an instance of CvCapture_FFMPEG.\n\
187 @end deftypefn")
188 {
189 octave_value_list retval;
190 int nargin = args.length ();
191
192 if (nargin != 1 || !args(0).is_string ())
193 {
194 print_usage();
195 return retval;
196 }
197
198 std::string filename = args(0).string_value ();
199 CvCapture_FFMPEG *h = new CvCapture_FFMPEG ();
200
201 // returns "valid" (true if open was successful)
202 bool valid = h->open (filename.c_str ());
203 if (valid)
204 retval.append (octave_value (h));
205 else
206 error ("Opening '%s' failed : '%s'", filename.c_str (), get_last_err_msg().c_str ());
207 return retval;
208 }
209
210 // PKG_ADD: autoload ("__cap_get_properties__", "cap_ffmpeg_wrapper.oct");
211 // PKG_DEL: autoload ("__cap_get_properties__", "cap_ffmpeg_wrapper.oct", "remove");
212 DEFUN_DLD(__cap_get_properties__, args, nargout,
213 "-*- texinfo -*-\n\
214 @deftypefn {Loadable Function} {[@var{h}, @var{opt}] =} __cap_get_properties__ (@var{h})\n\
215 Gets CvCapture_FFMPEG properties like bitrate, fps, total_frames, duration_sec...\n\
216 @end deftypefn")
217 {
218 octave_value_list retval;
219 int nargin = args.length ();
220
221 if (nargin != 1)
222 error("__cap_get_properties__ needs one parameter");
223
224 CvCapture_FFMPEG* h = get_cap_from_ov (args(0));
225 if (h)
226 {
227 octave_scalar_map opt;
228 opt.contents ("total_frames") = h->get_total_frames ();
229 opt.contents ("duration_sec") = h->get_duration_sec ();
230 opt.contents ("fps") = h->get_fps ();
231 opt.contents ("bitrate") = h->get_bitrate ();
232 opt.contents ("width") = h->frame.width;
233 opt.contents ("height") = h->frame.height;
234
235 // Current position of the video file in milliseconds
236 //opt.contents ("pos") = (h->picture_pts == AV_NOPTS_VALUE_) ? 0 : h->dts_to_sec(h->picture_pts) * 1000;
237
238 // Relative position of the video file: 0=start of the film, 1=end of the film.
239 //opt.contents ("rel_pos") = h->r2d(h->ic->streams[h->video_stream]->time_base);
240
241 // 0-based index of the frame to be decoded/captured next.
242 opt.contents ("frame_number") = h->frame_number;
243
244 opt.contents ("video_codec_name") = h->get_video_codec_name ();
245
246 // aspect ratio
247 // 0, 1 is "undefined"
248 {
249 AVRational s = h->get_sample_aspect_ratio ();
250 opt.contents ("aspect_ration_num") = s.num;
251 opt.contents ("aspect_ration_den") = s.den;
252 }
253
254 retval.append (opt);
255 }
256 return retval;
257 }
258
259 // PKG_ADD: autoload ("__cap_grab_frame__", "cap_ffmpeg_wrapper.oct");
260 // PKG_DEL: autoload ("__cap_grab_frame__", "cap_ffmpeg_wrapper.oct", "remove");
261 DEFUN_DLD(__cap_grab_frame__, args, nargout,
262 "-*- texinfo -*-\n\
263 @deftypefn {Loadable Function} {@var{f} =} __cap_grab_frame__ (@var{h})\n\
264 \n\
265 @end deftypefn")
266 {
267 octave_value_list retval;
268 int nargin = args.length ();
269
270 if (nargin != 1)
271 error("__cap_grab_frame__ needs one parameter");
272
273 CvCapture_FFMPEG* p = get_cap_from_ov (args(0));
274 if (p)
275 return (octave_value (p->grabFrame ()));
276
277 return retval;
278 }
279
280 // PKG_ADD: autoload ("__cap_retrieve_frame__", "cap_ffmpeg_wrapper.oct");
281 // PKG_DEL: autoload ("__cap_retrieve_frame__", "cap_ffmpeg_wrapper.oct", "remove");
282 DEFUN_DLD(__cap_retrieve_frame__, args, nargout,
283 "-*- texinfo -*-\n\
284 @deftypefn {Loadable Function} {@var{f} =} __cap_retrieve_frame__ (@var{h})\n\
285 \n\
286 @end deftypefn")
287 {
288 octave_value_list retval;
289 int nargin = args.length ();
290
291 if (nargin != 1)
292 error("__cap_retrieve_frame__ needs one parameter");
293
294 CvCapture_FFMPEG* p = get_cap_from_ov (args(0));
295 if (p)
296 {
297 unsigned char* data;
298 int width = 0;
299 int height = 0;
300 int step; // AVFrame::linesize, size in bytes of each picture line
301 int cn; // number of colors and should always be 3 here
302
303 bool ret = p->retrieveFrame (0, &data, &step, &width, &height, &cn);
304
305 //printf ("ret = %i, width = %i, height = %i, step = %i, cn = %i\n", ret, width, height, step, cn);
306
307 assert (cn == 3);
308
309 // step may be bigger because of padding
310 assert (step >= width * cn);
311
312 if (ret)
313 {
314 #if 0
315 // Attention: step and cn not handled yet
316 dim_vector dv (3, step/cn, height);
317 uint8NDArray img (dv);
318
319 unsigned char *p = reinterpret_cast<unsigned char*>(img.fortran_vec());
320 memcpy(p, data, img.numel ());
321
322 Array<octave_idx_type> perm (dim_vector (3, 1));
323 perm(0) = 2;
324 perm(1) = 1;
325 perm(2) = 0;
326
327 // FIXME: howto handle padding? Extract submatrix with "extract"?
328 retval(0) = octave_value(img.permute (perm));
329 #else
330
331 dim_vector dv (height, width, cn);
332 uint8NDArray img (dv);
333
334 for (int x = 0; x < width; ++x)
335 for (int y = 0; y < height; ++y)
336 for (int c = 0; c < cn; ++c)
337 img (y, x, c) = data[x * cn + y * step + c];
338
339
340 retval(0) = octave_value(img);
341
342 #endif
343 }
344
345 }
346 return retval;
347 }
348
349 // PKG_ADD: autoload ("__cap_close__", "cap_ffmpeg_wrapper.oct");
350 // PKG_DEL: autoload ("__cap_close__", "cap_ffmpeg_wrapper.oct", "remove");
351 DEFUN_DLD(__cap_close__, args, nargout,
352 "-*- texinfo -*-\n\
353 @deftypefn {Loadable Function} {@var{h} =} __cap_close__ (@var{h})\n\
354 undocumented internal function\n\
355 @end deftypefn")
356 {
357 octave_value_list retval;
358 int nargin = args.length ();
359
360 if (nargin != 1)
361 {
362 print_usage();
363 return retval;
364 }
365
366 CvCapture_FFMPEG* p = get_cap_from_ov (args(0));
367 if (p)
368 p->close ();
369
370 return retval;
371 }
372
373 /************* CvVideoWriter_FFMPEG ****************/
374
get_writer_from_ov(octave_value ov)375 CvVideoWriter_FFMPEG* get_writer_from_ov (octave_value ov)
376 {
377 if (!writer_type_loaded)
378 {
379 CvVideoWriter_FFMPEG::register_type();
380 writer_type_loaded = true;
381 }
382
383 if (ov.type_id() != CvVideoWriter_FFMPEG::static_type_id())
384 {
385 error("get_handler_from_ov: Not a valid CvVideoWriter_FFMPEG");
386 return 0;
387 }
388
389 CvVideoWriter_FFMPEG* p = 0;
390 const octave_base_value& rep = ov.get_rep();
391 p = &((CvVideoWriter_FFMPEG &)rep);
392 return (CvVideoWriter_FFMPEG *) p;
393 }
394
395 // PKG_ADD: autoload ("__writer_open__", "cap_ffmpeg_wrapper.oct");
396 // PKG_DEL: autoload ("__writer_open__", "cap_ffmpeg_wrapper.oct", "remove");
397 DEFUN_DLD(__writer_open__, args, nargout,
398 "-*- texinfo -*-\n\
399 @deftypefn {Loadable Function} {@var{h} =} __writer_open__ (@var{filename}, @var{fourcc}, @var{fps}, @var{width}, @var{height}, @var{isColor})\n\
400 undocumented internal function\n\
401 @end deftypefn")
402 {
403 octave_value_list retval;
404 int nargin = args.length ();
405
406 if (nargin != 6)
407 {
408 print_usage();
409 return retval;
410 }
411
412 if (! writer_type_loaded)
413 {
414 CvVideoWriter_FFMPEG::register_type();
415 writer_type_loaded = true;
416 av_register_all();
417 }
418
419 std::string filename = args(0).string_value ();
420
421 // codec tag, in OpenCV "fourcc" is used interchangeably
422 // empty fourcc selects default codec_id for guessed container
423 unsigned int tag;
424 std::string fourcc = args(1).string_value ();
425
426 // FIXME no error handling yet
427 double fps = args(2).double_value ();
428 int width = args(3).int_value ();
429 int height = args(4).int_value ();
430 bool isColor = args(5).bool_value ();
431
432 if (fourcc.size () == 0)
433 {
434 // get tag for default codec for guessed container from filename
435 AVOutputFormat* foo = av_guess_format (NULL, filename.c_str (), NULL);
436
437 // list supported codecs for guessed format
438 #if 0
439 if (foo->codec_tag)
440 {
441 const AVCodecTag * ptags = foo->codec_tag[0];
442 while (ptags->id != AV_CODEC_ID_NONE)
443 {
444 unsigned int tag = ptags->tag;
445 printf("fourcc tag 0x%08x/'%c%c%c%c' codec_id %04X\n", tag, CV_TAG_TO_PRINTABLE_CHAR4(tag), ptags->id);
446 ptags++;
447 }
448 }
449 #endif
450
451 tag = av_codec_get_tag (foo->codec_tag, foo->video_codec);
452 }
453 else if (fourcc.size () == 4)
454 {
455 tag = MKTAG(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
456 }
457 else
458 error ("fourcc has to be empty or 4 chars long");
459
460 // list codecs
461 //~ AVCodec * codec = av_codec_next(NULL);
462 //~ while(codec != NULL)
463 //~ {
464 //~ fprintf(stderr, "%s\n", codec->long_name);
465 //~ codec = av_codec_next(codec);
466 //~ }
467
468 // list formats
469 //~ AVOutputFormat * oformat = av_oformat_next(NULL);
470 //~ while(oformat != NULL)
471 //~ {
472 //~ printf ("%s; %s; %s; %s\n", oformat->name, oformat->long_name, oformat->mime_type, oformat->extensions);
473
474 //~ //cv_ff_codec_tag_dump (oformat->codec_tag);
475
476 //~ oformat = av_oformat_next(oformat);
477 //~ }
478
479 //~ AVOutputFormat * oformat = av_oformat_next(NULL);
480 //~ while(oformat != NULL)
481 //~ {
482 //~ fprintf(stderr, "%s\n", oformat->long_name);
483 //~ if (oformat->codec_tag != NULL)
484 //~ {
485 //~ int i = 0;
486
487 //~ CV_CODEC_ID cid = CV_CODEC(CODEC_ID_MPEG1VIDEO);
488 //~ while (cid != CV_CODEC(CODEC_ID_NONE))
489 //~ {
490 //~ cid = av_codec_get_id(oformat->codec_tag, i++);
491 //~ fprintf(stderr, " %d\n", cid);
492 //~ }
493 //~ }
494 //~ oformat = av_oformat_next(oformat);
495 //~ }
496
497 //printf ("tag = %i = %#x = %c%c%c%c\n", tag, tag, CV_TAG_TO_PRINTABLE_CHAR4(tag));
498
499 #if 0
500 // that would be a workaround:
501 AVOutputFormat* foo = av_guess_format (NULL, "foo.mp4", NULL);
502 printf ("default video_codec = %i = %#x\n", foo->video_codec, foo->video_codec);
503
504 unsigned int tag = av_codec_get_tag (foo->codec_tag, AV_CODEC_ID_H264);
505 printf ("tag = %i = %#x\n", tag, tag);
506
507 // vom tag über riff zum codec_id:
508 tag = MKTAG('H', '2', '6', '4');
509 const struct AVCodecTag *table[] = { avformat_get_riff_video_tags(), 0 };
510 enum AVCodecID id = av_codec_get_id (table, tag);
511 printf ("id = %i = %#x, AV_CODEC_ID_H264 = %#x\n", id, id, AV_CODEC_ID_H264);
512
513 // und zum tag zurück, Achtung, das ergibt nicht mehr 0x21
514 tag = av_codec_get_tag (table, AV_CODEC_ID_H264);
515 printf ("tag = %i = %#x = %c%c%c%c\n", tag, tag, CV_TAG_TO_PRINTABLE_CHAR4(tag));
516
517 #endif
518
519 // welche API wäre denn von Octave aus gewünscht?
520 // Ich denke direkt AVCodecID angeben wäre sinnvoller, als die fourcc
521
522 /*
523 * codecs anzeigen:
524 * andy@Ryzen5Babe:~/Downloads/libav-12.3$ grep -r show_codecs
525 * cmdutils_common_opts.h: { "codecs" , OPT_EXIT, {.func_arg = show_codecs }, "show available codecs" },
526 * cmdutils.h:int show_codecs(void *optctx, const char *opt, const char *arg);
527 * cmdutils.c:int show_codecs(void *optctx, const char *opt, const char *arg)
528 */
529
530 CvVideoWriter_FFMPEG *h = new CvVideoWriter_FFMPEG ();
531
532 // https://docs.opencv.org/3.4.1/dd/d9e/classcv_1_1VideoWriter.html#ac3478f6257454209fa99249cc03a5c59
533 // fourcc 4-character code of codec used to compress the frames. For example,
534 // VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec,
535 // VideoWriter::fourcc('M','J','P','G') is a motion-jpeg codec etc.
536 // List of codes can be obtained at Video Codecs by FOURCC page.
537 // FFMPEG backend with MP4 container natively uses other values as fourcc code: see http://mp4ra.org/#/codecs,
538 // so you may receive a warning message from OpenCV about fourcc code conversion.
539
540 // fps Framerate of the created video stream.
541 // isColor If it is not zero, the encoder will expect and encode color frames,
542 // otherwise it will work with grayscale frames (the flag is currently supported on Windows only).
543
544 //printf ("h->open (%s, %i, %f, %u, %u, %u);\n", filename.c_str (), tag, fps, width, height, isColor);
545
546 bool valid = h->open (filename.c_str (), tag, fps, width, height, isColor);
547 if (valid)
548 {
549 retval.append (octave_value (h));
550 }
551 else
552 {
553 // FIXME: CvVideoWriter_FFMPEG::open just returns false without explanation why
554 error ("Opening '%s' for writing failed", filename.c_str ());
555 }
556 return retval;
557 }
558
559 // PKG_ADD: autoload ("__writer_get_properties__", "cap_ffmpeg_wrapper.oct");
560 // PKG_DEL: autoload ("__writer_get_properties__", "cap_ffmpeg_wrapper.oct", "remove");
561 DEFUN_DLD(__writer_get_properties__, args, nargout,
562 "-*- texinfo -*-\n\
563 @deftypefn {Loadable Function} {[@var{h}, @var{opt}] =} __cap_get_properties__ (@var{h})\n\
564 Gets CvVideoWriter_FFMPEG properties...\n\
565 @end deftypefn")
566 {
567 octave_value_list retval;
568 int nargin = args.length ();
569
570 if (nargin != 1)
571 error("__writer_get_properties__ needs one parameter");
572
573 CvVideoWriter_FFMPEG* h = get_writer_from_ov (args(0));
574 if (h)
575 {
576 octave_scalar_map opt;
577 opt.contents ("ok") = h->ok;
578 opt.contents ("frame_width") = h->frame_width;
579 opt.contents ("output_format_long_name") = h->fmt->long_name;
580 opt.contents ("output_video_stream_codec") = h->get_video_codec_name ();
581 opt.contents ("frame_height") = h->frame_height;
582 opt.contents ("frame_idx") = h->frame_idx;
583 retval.append (opt);
584 }
585 return retval;
586 }
587
588 // PKG_ADD: autoload ("__writer_write_frame__", "cap_ffmpeg_wrapper.oct");
589 // PKG_DEL: autoload ("__writer_write_frame__", "cap_ffmpeg_wrapper.oct", "remove");
590 DEFUN_DLD(__writer_write_frame__, args, nargout,
591 "-*- texinfo -*-\n\
592 @deftypefn {Loadable Function} {@var{h} =} __writer_write_frame__ (@var{h}, @var{frame})\n\
593 undocumented internal function\n\
594 @end deftypefn")
595 {
596 octave_value_list retval;
597 int nargin = args.length ();
598
599 if (nargin != 2)
600 {
601 print_usage();
602 return retval;
603 }
604
605 //NDArray f = args(1).array_value();
606 uint8NDArray f = args(1).uint8_array_value();
607
608 CvVideoWriter_FFMPEG* p = get_writer_from_ov (args(0));
609 if (p)
610 {
611 int width = f.columns ();
612 int height = f.rows ();
613 int cn = f.dim3 ();
614 int step = width * cn;
615 int origin = 0;
616
617 //printf ("width=%i, height=%i, step=%i\n", width, height, step);
618
619 // permute, see also __cap_retrieve_frame__
620 // for opposite
621
622 Array<octave_idx_type> perm (dim_vector (3, 1));
623 perm(0) = 2;
624 perm(1) = 1;
625 perm(2) = 0;
626
627 f = f.permute (perm);
628
629 unsigned char *t = reinterpret_cast<unsigned char*>(f.fortran_vec());
630
631 bool ret = p->writeFrame (t, step, width, height, cn, origin);
632 if (! ret)
633 error ("CvVideoWriter_FFMPEG::writeFrame failed");
634
635 }
636 return retval;
637 }
638
639 // PKG_ADD: autoload ("__writer_close__", "cap_ffmpeg_wrapper.oct");
640 // PKG_DEL: autoload ("__writer_close__", "cap_ffmpeg_wrapper.oct", "remove");
641 DEFUN_DLD(__writer_close__, args, nargout,
642 "-*- texinfo -*-\n\
643 @deftypefn {Loadable Function} {@var{h} =} __writer_close__ (@var{h})\n\
644 undocumented internal function\n\
645 @end deftypefn")
646 {
647 octave_value_list retval;
648 int nargin = args.length ();
649
650 if (nargin != 1)
651 {
652 print_usage();
653 return retval;
654 }
655
656 CvVideoWriter_FFMPEG* p = get_writer_from_ov (args(0));
657 if (p)
658 p->close ();
659
660 return retval;
661 }
662