xref: /haiku/docs/develop/media/MediaExtractorAddOn.h (revision cdf894ce17f72d23be5e729a5d6877b7124c4654)
1 extern "C" {
2   // Your addon should implement this function.
3   // Return a new instance of your BMediaExtractorAddOn subclass.
4   // This function will be called multiple times, and should return a
5   // new instance each time.  Return NULL if allocation fails.
6   BMediaExtractorAddOn * instantiate_media_extractor_add_on();
7 }
8 
9 // Your add-on must implement a subclass of this class
10 class BMediaExtractorAddOn
11 {
12 public:
13   BMediaExtractorAddOn(void);
14   virtual ~BMediaExtractorAddOn(void);
15 
16   //// stateless functions
17   // these should work without dependency on a current stream
18 
19 /* begin BFileInterface functions */
20   // These are used to enumerate the set of file formats that this
21   // extractor is prepared to read from.  Implementing these meaningfully
22   // is important for discovering all types supported by the system.
23 
24   // Implement per BFileInterface::GetNextFileFormat
25   //
26   // Return codes:
27   // B_OK : No error
28   // B_ERROR : No more formats
29   // GetNextInputFormat: required for BFileInterface functionality
30   virtual status_t GetNextInputFormat(int32 * cookie,
31                                       media_file_format * outFormat) = 0;
32   // Implement per BFileInterface::DisposeFileFormatCookie
33   // DisposeInputFormatCookie: required for BFileInterface functionality
34   virtual void DisposeInputFormatCookie(int32 cookie) = 0;
35 
36 /* begin transcoding functions */
37   // These are used to enumerate the set of file formats that this
38   // extractor is prepared to transcode to.  The default implementation
39   // simply returns no support.
40 
41   // Implement per BFileInterface::GetNextFileFormat
42   //
43   // Return codes:
44   // B_OK : No error
45   // B_ERROR : No more formats
46   virtual status_t GetNextOutputFormat(int32 * cookie,
47                                        media_file_format * outFormat);
48   // Implement per BFileInterface::DisposeFileFormatCookie
49   virtual void DisposeOutputFormatCookie(int32 cookie);
50 /* end transcoding functions */
51 /* end BFileInterface functions */
52 
53 /* begin BMediaAddOn functions */
54   // These are used to discover an extractors quality rating for a
55   // particular media format.
56   // Implement per BMediaAddOn::SniffType
57   //
58   // Return codes:
59   // B_OK : No error
60   // B_MEDIA_NO_HANDLER : This extractor doesn't handle that mime type
61   virtual status_t SniffInputType(BMimeType & mimeType, float * outQuality) = 0;
62 /* begin transcoding function */
63   virtual status_t SniffOutputType(BMimeType & mimeType, float * outQuality);
64 /* end transcoding function */
65 /* end BMediaAddOn functions */
66 
67   // Same as above, but for a media file format
68   // The default implementation of this will iterate through your formats using
69   // the appropriate interface from above, and simply return 0 for the quality
70   // if it finds a matching supported format.
71   //
72   // Return codes:
73   // B_OK : No error
74   // B_MEDIA_NO_HANDLER : This extractor doesn't handle that format
75   virtual status_t SniffInputFormat(const media_file_format & format, float * outQuality);
76 /* begin transcoding function */
77   virtual status_t SniffOutputFormat(const media_file_format & format, float * outQuality);
78 /* end transcoding function */
79 
80   //// state creation functions
81   // calling these functions shouldn't affect the results of the stateless functions
82 
83   // Sets the current stream to source or destination
84   // The default implementation for the BDataIO SetSource is to wrap
85   // the BDataIO object in a buffer and call the BPositionIO SetSource.
86   // The default implementation for the BFile SetSource is to send the
87   // call directly to BPositionIO.  Note that it is highly recommended
88   // to utilize the BNode properties of the BNodeIO/BFile object in
89   // order to dynamically update your extractor state when the file
90   // changes.  It is also recommended to use the BNode properties in
91   // order to access the attributes of the source file; store or load
92   // file specific extractor properties from here.
93   // Note: the extractor is not require to return B_MEDIA_NO_HANDLER at
94   // this point.  However, calling any stateful function after this
95   // should return B_MEDIA_NO_HANDLER.
96   //
97   // Return codes:
98   // B_OK : No error
99   // B_NO_MEMORY : Storage for the buffer could not be allocated.
100   // B_MEDIA_NO_HANDLER : This extractor doesn't handle that format
101   virtual status_t SetSource(const BFile * source);
102   virtual status_t SetSource(const entry_ref * source, int32 flags = 0);
103   virtual status_t SetSource(const BDataIO * source);
104 /* begin transcoding functions */
105   virtual status_t SetDestination(const BFile * source);
106   virtual status_t SetDestination(const entry_ref * source, int32 flags = 0);
107   virtual status_t SetDestination(const BDataIO * source);
108 /* end transcoding functions */
109 
110   //// stateful functions
111   // Calling these functions shouldn't affect the results of the stateless functions.
112   // Calling these functions before calling a state creation function should return
113   // B_NO_INIT.  Calling these functions after calling a state creation function with
114   // an invalid argument should return B_MEDIA_NO_HANDLER.  Generally these
115   // functions may also return any appropriate Storage Kit/File System Errors, such
116   // as B_FILE_NOT_FOUND, B_BUSTED_PIPE, etc.
117 
118   // inspired by BMediaFile::GetFileFormatInfo
119   //
120   // Fills the specified media_file_format structure with
121   // information describing the file format of the stream
122   // currently referenced by the BEncoder.
123   //
124   // Return codes:
125   // B_OK        : No error
126   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
127   // B_NO_MEMORY : Storage for part of the media_file_format
128   //               object couldn't be allocated.
129   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
130   virtual status_t GetFileFormatInfo(media_file_format * mfi) = 0;
131 
132   // The extractor should implement this function in the
133   // manner described for BFileInterface::SniffRef, except that
134   // it uses the current Source instead of an entry_ref
135   //
136   // Return codes:
137   // B_OK : No error
138   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
139   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
140   virtual status_t Sniff(char * outMimeType, float * outQuality) = 0;
141 
142   // implement per BMediaTrack::AddChunk(void)
143   //
144   // Return codes:
145   // B_OK        : No error
146   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
147   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
148   virtual status_t WriteChunk(int32 type,
149                               const void * data,
150                               size_t size);
151 
152 /* begin weird function that is missing but parallels add chunk */
153   // implement per BMediaTrack::ReadChunk(void) <- missing????
154   // umm.. has the same semantics as AddChunk, yeah that's it...
155   //
156   // Return codes:
157   // B_OK        : No error
158   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
159   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
160   virtual status_t ReadChunk(int32 * outType,
161                              const void * outData,
162                              size_t * outSize);
163 /* end weird function that is missing but parallels add chunk */
164 
165   // The extractor should do any cleanup required.  After
166   // this function returns, the source object should be
167   // closed and deleted by the caller, not by Close().
168   // The default implementation simply returns B_OK.
169   //
170   // Return codes:
171   // B_OK : No error
172   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
173   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
174   virtual status_t Close(void);
175 
176   //// shared state functions
177   // The ParameterWeb interface is used to publish both file specific
178   // and extractor specific options.  Accessing a file specific parameter
179   // before calling a state creation function should return B_NO_INIT.
180   // Accessing a file parameter after calling a state creation function
181   // with an invalid argument should return B_MEDIA_NO_HANDLER.  Accessing
182   // extractor specific options should never return these errors, but may
183   // return other errors.
184 
185   // the extractor should provide several basic parameters
186   // through this interface, such as B_TRACK_COUNT, and B_DURATION
187   // see also BMediaFile::GetParameterValue
188   // hmmm... how to pick which stream parameters apply to?
189   // could use a bitwise or with B_OUTPUT_STREAM (and a
190   // B_INPUT_STREAM for completeness)
191   //
192   // Return codes:
193   // B_OK : No error
194   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
195   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
196   virtual status_t GetParameterValue(int32 id, const void * value,
197                                      size_t * size) = 0;
198 
199   // the extractor may optionally supply parameters for the
200   // user to configure, such as buffering information(?)
201   // see also BMediaFile::SetParameterValue
202   //
203   // Return codes:
204   // B_OK : No error
205   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
206   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
207   virtual status_t SetParameterValue(int32 id, const void * value,
208                                      size_t size);
209 
210   // The extractor may return a BParameterWeb for browsing or
211   // configuring the extractor's parameters.  Returns NULL if the
212   // extractor doesn't support this.  The default implementation
213   // simply returns NULL.  Note: if the Source is not in a good
214   // state, this web may not include file specific parameters.
215   //
216   // As a suggestion you should use groups to gather parameters
217   // related to the encoder and separate them from parameters
218   // related to the input stream and output stream (if applicable)
219   //
220   // See also BMediaFile::Web
221   virtual BParameterWeb * Web(void) { return NULL; }
222 
223   // The extractor may return a BView for browsing or configuring
224   // the extractor's parameters.  Returns NULL if the extractor
225   // doesn't support this.  The default implementation simply
226   // returns NULL.
227   virtual BView * GetParameterView(void) (void) { return NULL; }
228 
229 /* begin seek/sync functions for the extractor */
230   // The extractor will seek first on the seek track, just like
231   // BMediaTrack::SeekToTime.  Like SeekToTime, it accepts a flag
232   // argument which tells how to find the nearest acceptable frame.
233   // After finding this frame, it will also seek any other open
234   // streams in an extractor-dependent fashion.  Usually the seek
235   // stream will be a video stream.  If seeked to a keyframe, for
236   // example, the audio stream will be seeked to an appropriate time.
237   //
238   // This may be more efficient than seeking the seek track through
239   // the BMediaTrack interface, and then calling Sync() here.  It
240   // should not be less efficient.
241   //
242   // See also BMediaTrack::SeekToTime
243   // see above for additions to media_seek_type (used for flags)
244   // seekMode per BFile::Seek, only SEEK_SET is required
245   //
246   // Return codes:
247   // B_OK : No error
248   // B_UNSUPPORTED : This extractor does not support general seeking
249   //                 for this stream.
250   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
251   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
252   virtual status_t SeekToTime(bigtime_t * ioTime,
253                               int32 mediaSeekFlags = 0,
254                               int32 seekMode = 0) = 0;
255 
256   // The extractor will seek first on the seek track, just like
257   // BMediaTrack::SeekToFrame.  Like SeekToFrame, it accepts a flag
258   // argument which tells how to find the nearest acceptable frame.
259   // After finding this frame, it will also seek any other open
260   // streams in an extractor-dependent fashion.  Usually the seek
261   // stream will be a video stream.  If seeked to a keyframe, for
262   // example, the audio stream will be seeked to an appropriate time.
263   //
264   // This may be more efficient than seeking the seek track through
265   // the BMediaTrack interface, and then calling Sync() here.  It
266   // should not be less efficient.
267   //
268   // See also BMediaTrack::SeekToFrame
269   // see above for additions to media_seek_type (used for flags)
270   // seekMode per BFile::Seek, only SEEK_SET is required
271   //
272   // Return codes:
273   // B_OK : No error
274   // B_UNSUPPORTED : This extractor does not support general seeking
275   //                 for this stream.
276   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
277   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
278   virtual status_t SeekToFrame(int64 * ioFrame,
279                                int32 mediaSeekFlags = 0,
280                                int32 seekMode = 0) = 0;
281 
282 /* begin seek extensions functions */
283   // The extractor will seek first on the seek track.  It goes to
284   // a position defined by ioChunk*chunkSize, where chunkSize is
285   // defined by the decoder.  For example, some streams are not byte
286   // streams, but rather bitstreams.  In this case the chunkSize may
287   // correspond to 1 bit.  Like the other MediaTrack Seeks, it
288   // accepts a flag argument which tells how to find the nearest
289   // acceptable frame.
290   // After finding this frame, it will also seek any other open
291   // streams in an extractor-dependent fashion.  Usually the seek
292   // stream will be a video stream.  If seeked to a keyframe, for
293   // example, the audio stream will be seeked to an appropriate time.
294   //
295   // This may be more efficient than seeking the seek track through
296   // the BMediaTrack interface, and then calling Sync() here.  It
297   // should not be less efficient.
298   //
299   // see above for additions to media_seek_type (used for flags)
300   // seekMode per BFile::Seek, only SEEK_SET is required
301   //
302   // Return codes:
303   // B_OK : No error
304   // B_UNSUPPORTED : This extractor does not support general seeking
305   //                 for this stream.
306   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
307   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
308   virtual status_t SeekToChunk(int64 * ioChunk,
309                                int32 mediaSeekFlags = 0,
310                                int32 seekMode = 0) = 0;
311 
312   // The extractor will seek first on the seek track.  It goes to a
313   // position defined by numerator/(duration of this file).  For
314   // example: Seek(LONG_LONG_MAX/2) would seek halfway through the
315   // stream.  Like the other MediaTrack Seeks, it accepts a flag
316   // argument which tells how to find the nearest acceptable frame.
317   // If the seekMode is SEEK_SET it will seek a fraction of the way
318   // back to the beginning from the current location.  If the seekMode
319   // is SEEK_END it will seek a fraction of the way to the end from
320   // the current location.  If the seekMode is SEEK_CUR it will seek
321   // as above. (fraction of the entire file duration)
322   // After finding this frame, it will also seek any other open
323   // streams in an extractor-dependent fashion.  Usually the seek
324   // stream will be a video stream.  If seeked to a keyframe, for
325   // example, the audio stream will be seeked to an appropriate time.
326   //
327   // This may be a lot more efficient than seeking to a time or frame
328   // for some streams. (in particular, nonindexed streams)
329   //
330   // This may be more efficient than seeking the seek track through
331   // the BMediaTrack interface, and then calling Sync() here.  It
332   // should not be less efficient.
333   //
334   // Note: because the duration may change over time (if the file is
335   // being written to, for example) the result of seeking with a
336   // particular numerator may also change.  It will usually be later,
337   // but could also be earlier.
338   //
339   // see above for additions to media_seek_type (used for flags)
340   // seekMode per BFile::Seek, only SEEK_CUR is required
341   //
342   // Return codes:
343   // B_OK : No error
344   // B_UNSUPPORTED : This extractor does not support general seeking
345   //                 for this stream.
346   // B_NO_INIT     : The BEncoder doesn't reference a valid stream.
347   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
348   virtual status_t Seek(int64 * numerator,
349                         int32 mediaSeekFlags = 0,
350                         int32 seekMode = 0) = 0;
351 /* end seek extensions functions */
352 
353   // Using the location from the seek stream, seeks any other open
354   // streams in an extractor-dependent fashion.  Usually the seek
355   // stream will be a video stream.  If seeked to a keyframe, for
356   // example, the audio stream will be seeked to an appropriate time.
357   //
358   // Note: if not supplied, the seek stream will be the current one
359   // as retrieved by GetParameterValue, not zero.  Sync() will do
360   // this check for you.
361   //
362   // Return codes:
363   // B_OK : No error
364   // B_UNSUPPORTED : This extractor does not support general syncing
365   //                 for this stream.
366   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
367   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
368   virtual status_t Sync(int32 seekStream = 0) = 0;
369 /* end seek/sync functions for the extractor */
370 
371   // Returns a thing that is useful for MediaTrack to do its business.
372   //
373   // May simply include state but will probably include a pointer back
374   // to this object, and will likely call functions that are defined by
375   // subclasses of this extractor.  For example, the subclass may define
376   // a function like this:
377   // SeekTrackToFrame(BTrack * track, int64 ioFrame, int32 flags = 0) {
378   // ... }
379   // and then when SeekToFrame is called on the BTrack object the work
380   // would be done by the Extractor.
381   //
382   // Also, any track extracted using this function will be seeked by
383   // the extractor seek functions.  Any track not extracted by this
384   // function will not be seeked.  If the seekMode parameter is
385   // supplied as SEEK_CUR the track will be seeked before being
386   // returned, as per Sync().  However because this involves only
387   // one track it may be more efficient than retrieving the track and
388   // then calling Sync();  If seekMode is SEEK_SET then the current
389   // seek time for the track will be no later than the earliest
390   // seekable time.  If seekMode is SEEK_END the current seek time
391   // for the track will be no earlier than the earliest seekable time.
392   // Note: for non-seekable tracks, this may may no difference.
393   // The default for seekMode is SEEK_SET.
394   //
395   // If the seek parameter is passed as false, no pre-seeking will be
396   // performed on the track.  The current seek time may be arbitrary
397   // or even illegal.  Attempting to decode data from the track in
398   // this state will result in an error if the state is illegal.
399   //
400   // Return codes:
401   // B_OK : No error
402   // B_STREAM_NOT_FOUND
403   // B_BAD_INDEX : The index supplied does not correspond to a valid
404   //               track in this stream.
405   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
406   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
407   virtual BTrack * TrackAt(int32 index, int32 seekMode = 0,
408                                         bool seek = true) = 0;
409 
410   // Disclaims interest in a particular track.  After releasing a
411   // track the track will no longer be seeked by the extractor.
412   //
413   // Return codes:
414   // B_OK : No error
415   // B_BAD_TYPE  : This track does not correspond to this extractor.
416   // B_NO_INIT   : The BEncoder doesn't reference a valid stream.
417   // B_MEDIA_NO_HANDLER : This extractor doesn't handle this format
418   status_t ReleaseTrack(BTrack * track);
419 
420 protected:
421   // use to negotiate the format for this track
422   // straight BMediaTrack::DecodedFormat behavior
423   virtual status_t NegotiateOutputFormat(BTrack * track,
424                                          media_format * ioFormat) = 0;
425 
426   // get/set information about a particular track
427   virtual status_t GetParameterValue(BTrack * track, int32 id,
428                                      const void * value, size_t * size) = 0;
429   virtual status_t SetParameterValue(BTrack * track, int32 id,
430                                      const void * value, size_t size);
431   virtual BParameterWeb * Web(BTrack * track) { return NULL; }
432   virtual BView * GetParameterView(BTrack * track) { return NULL; }
433 
434   // seek only this particular track to the given time
435   // straight BMediaTrack::SeekToTime behavior
436   virtual status_t SeekToTime(BTrack * track,
437                               bigtime_t * ioTime,
438                               int32 mediaSeekFlags = 0,
439                               int32 seekMode = 0) = 0;
440   // seek only this particular track to the given frame
441   // straight BMediaTrack::SeekToFrame behavior
442   virtual status_t SeekToFrame(BTrack * track,
443                                int64 * ioFrame,
444                                int32 mediaSeekFlags = 0,
445                                int32 seekMode = 0) = 0;
446   // seek only this particular track to the given chunk
447   // straight BMediaTrack::SeekToChunk behavior
448   virtual status_t SeekToChunk(BTrack * track,
449                                int64 * ioChunk,
450                                int32 mediaSeekFlags = 0,
451                                int32 seekMode = 0) = 0;
452   // seek only this particular track to the given chunk
453   // straight BMediaTrack::Seek behavior
454   virtual status_t Seek(BTrack * track,
455                         int64 * numerator,
456                         int32 mediaSeekFlags = 0,
457                         int32 seekMode = 0) = 0;
458 
459   // read a chunk from this track only
460   // straight BMediaTrack::ReadChunk behavior
461   virtual status_t ReadChunk(BTrack * track,
462                              char ** outBuffer, int32 * ioSize,
463                              media_header * outHeader = NULL) = 0;
464   // read frames from this track only
465   // straight BMediaTrack::ReadChunk behavior
466   virtual status_t ReadFrames(BTrack * track,
467                               void * outBuffer, int64 * outFrameCount,
468                               media_header * outHeader = NULL,
469                               media_decode_info * info = NULL) = 0;
470 /* begin read extensions functions */
471   // read units of time from this track only
472   virtual status_t ReadTime(BTrack * track,
473                             void * outBuffer, int64 * outTimeCount,
474                             media_header * outHeader = NULL,
475                             media_decode_info * info = NULL) = 0;
476   // for completeness sake?
477   // read a percentage from this track only
478   virtual status_t Read(BTrack * track,
479                         void * outBuffer, int64 * outNumerator,
480                         media_header * outHeader = NULL,
481                         media_decode_info * info = NULL) = 0;
482 /* end read extensions functions */
483 
484 private:
485   // this class is used by individual tracks
486   // as a private interface to the extractor
487   class BTrack {
488     // use to negotiate the format for this track
489     virtual status_t NegotiateOutputFormat(media_format * ioFormat) {
490       return BExtractor::NegotiateOutputFormat(ioFormat);
491     }
492 
493     // access to parameters for this track
494     virtual status_t GetParameterValue(int32 id, const void * value,
495                                        size_t * size) {
496       return BExtractor::GetParameterValue(this,id,value,size);
497     }
498     virtual status_t SetParameterValue(int32 id, const void * value,
499                                        size_t size) {
500       return BExtractor::SetParameterValue(this,id,value,size);
501     }
502     virtual BParameterWeb * Web(void) {
503       return BExtractor::Web(this);
504     }
505     virtual BView * GetParameterView(void) {
506       return BExtractor::GetParameterView(this);
507     }
508 
509     // access to seek functionality on this track
510     virtual status_t SeekToTime(bigtime_t * ioTime,
511                               int32 mediaSeekFlags = 0,
512                               int32 seekMode = 0) {
513       return BExtractor::SeekToTime(this,ioTime,mediaSeekFlags,seekMode);
514     }
515     virtual status_t SeekToFrame(int64 * ioFrame,
516                                  int32 mediaSeekFlags = 0,
517                                  int32 seekMode = 0) {
518       return BExtractor::SeekToFrame(this,ioFrame,mediaSeekFlags,seekMode);
519     }
520     virtual status_t SeekToChunk(int64 * ioChunk,
521                                  int32 mediaSeekFlags = 0,
522                                  int32 seekMode = 0) {
523       return BExtractor::SeekToChunk(this,ioChunk,mediaSeekFlags,seekMode);
524     }
525     virtual status_t Seek(int64 * numerator,
526                           int32 mediaSeekFlags = 0,
527                           int32 seekMode = 0) {
528       return BExtractor::Seek(this,numerator,mediaSeekFlags,seekMode);
529     }
530 
531     // access to readers for this track
532     virtual status_t ReadChunk(char ** outBuffer, int32 * ioSize,
533                                media_header * outHeader = NULL) {
534       return BExtractor::ReadChunk(this,outBuffer,ioSize,outHeader);
535     }
536     virtual status_t ReadFrames(void * outBuffer, int64 * outFrameCount,
537                                 media_header * outHeader = NULL,
538                                 media_decode_info * info = NULL) {
539       return BExtractor::ReadFrames(this,outBuffer,outFrameCount,outHeader,info);
540     }
541 /* begin read extensions functions */
542 	virtual status_t ReadTime(void * outBuffer, int64 * outTimeCount,
543                               media_header * outHeader = NULL,
544                               media_decode_info * info = NULL) {
545       return BExtractor::ReadTime(this,outBuffer,outTimeCount,outHeader,info);
546     }
547     // for completeness sake?
548     virtual status_t Read(void * outBuffer, int64 * outNumerator,
549                           media_header * outHeader = NULL,
550                           media_decode_info * info = NULL) {
551       return BExtractor::Read(this,outBuffer,outNumerator,outHeader,info);
552     }
553 /* end read extensions functions */
554     // pad me
555   };
556 
557   // pad me
558 };
559 
560 
561