From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTP id AFC1D48EAF for ; Mon, 29 Apr 2024 09:24:25 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id AA7B668D46A; Mon, 29 Apr 2024 12:24:22 +0300 (EEST) Received: from b-painless.mh.aa.net.uk (b-painless.mh.aa.net.uk [81.187.30.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3537968C929 for ; Mon, 29 Apr 2024 12:24:16 +0300 (EEST) Received: from 0.b.4.b.7.4.0.8.c.4.a.5.d.8.b.2.0.5.8.0.9.1.8.0.0.b.8.0.1.0.0.2.ip6.arpa ([2001:8b0:819:850:2b8d:5a4c:8047:b4b0] helo=andrews-2024-laptop.lan) by painless-b.tch.aa.net.uk with esmtp (Exim 4.96) (envelope-from ) id 1s1NF9-003PiE-1t; Mon, 29 Apr 2024 10:24:15 +0100 From: Andrew Sayers To: ffmpeg-devel@ffmpeg.org Date: Mon, 29 Apr 2024 10:24:04 +0100 Message-ID: <20240429092407.2267590-1-ffmpeg-devel@pileofstuff.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 1/4] doc: Explain what "context" means X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Andrew Sayers Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: Derived from detailed explanations kindly provided by Stefano Sabatini: https://ffmpeg.org/pipermail/ffmpeg-devel/2024-April/325903.html --- doc/context.md | 308 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 doc/context.md diff --git a/doc/context.md b/doc/context.md new file mode 100644 index 0000000000..73297f53aa --- /dev/null +++ b/doc/context.md @@ -0,0 +1,308 @@ +# Introduction to contexts + +Like many C projects, FFmpeg has adopted the subset of +[object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming) +techniques that help solve its problems. Object-like structures are called "contexts", +and this document provides a general introduction to how they work. + +Object-oriented programming tends to focus on +[access](https://en.wikipedia.org/wiki/Access_modifiers) as a primary concern. +For example, members of a class are often designated "private" to indicate +they are only accessible to code that's part of the class. Less focus is put on +[reflection](https://en.wikipedia.org/wiki/Reflection_(computer_programming)), +where it is provided at all. For example, C++ has no built-in way to serialize +the state of an arbitrary object. + +Reflection is extremely important for FFmpeg, because user-facing +options are implemented by reflecting the state of contexts. Limiting +access is a secondary concern, mainly important for ensuring +implementation details can change between versions. + +Knowledge of object-oriented programming concepts can help when learning FFmpeg, +but it's important not to fixate on FFmpeg's access control features, +nor to overlook its reflection capabilities. This document compares +FFmpeg and OOP techniques where relevant, but does not require an understanding +of object-oriented programming. + +## Example: modify text then print it + +The example below shows a context structure that receives input strings, +modifies them in some context-dependant way, then prints them to a specified +filehandle. + +```c +/** + * Type information, accessible at runtime. + * + * Useful when configuring objects. + */ +enum ModifyThenPrintDialect { + MODIFY_THEN_PRINT_DIALECT_PLAIN_TEXT = 0, + MODIFY_THEN_PRINT_DIALECT_REGEX = 1, + MODIFY_THEN_PRINT_DIALECT_REGEX_PCRE = 2 +}; + +/** + * User-facing information about types + * + * Useful for describing contexts to the user. + */ +static const char *ModifyThenPrintDialectName[] = { + "plain text", + "regular expression", + "Perl-compatible regular expression" +}; + +/** + * Context for functions that modify strings before printing them + */ +struct ModifyThenPrintContext { + + /** + * Information about the type of this particular instance + * + * Object-oriented programs would probably replace this with "sub-classes", + * where each class extends the base class to implement one dialect. + * But information about a type can also work more like "mixins", + * where several pieces of unrelated functionality are blended together + * to create an interface for a specific purpose. + */ + enum ModifyThenPrintDialect dialect; + + /** + * Internal context + * + * Contains anything that isn't part of the public interface. + * The most obvious OOP analogy is to "private" members that are only + * accessible to code that's part of the class. But it could also contain + * information about "virtual functions" - functions that every sub-class + * guarantees to implement, but in their own class-specific way. + */ + void *priv_data; + + /** + * User-configurable options + * + * Best set through an API, but can be set directly if necessary + * + * Data from users needs to be validated before it's set, and the API + * might e.g. want to update some internal buffer for performance reasons. + * Setting these directly is always less robust than using an API, + * but might be worthwhile if you're willing to spend the time checking + * for edge cases, and don't mind your code misbehaving if future + * versions change the API but not the structure. + * + * Object-oriented programs would likely make these "protected" members, + * initialised in a constructor and accessed with getters and setters. + * That means code *related* to this class can access them directly, + * while unrelated code accesses them indirectly by calling functions. + * + * But the "protected access" analogy is quite limited. In particular, + * protected members don't have any special reflective properties, + * whereas FFmpeg options are usually configurable by end users. + */ + char *replace_this; + char *with_this; + + /** + * Programmer-configurable variable + * + * Object-oriented programs would represent this as a public member. + */ + FILE *out; + +}; + +/** + * Allocate and initialize a ModifyThenPrintContext + * + * Creates a new pointer, then fills in some sensible defaults. + * + * We can reasonably assume this function will initialise `priv_data` + * with a dialect-specific object, but shouldn't make any assumptions + * about what that object is. + * + * Object-oriented programs would likely implement this with "allocator" + * and "constructor" functions, (i.e. separate functions that reserve memory + * for an object and set its initial values). + */ +int ModifyThenPrintContext_alloc_context(struct ModifyThenPrintContext **ctx, + enum ModifyThenPrintDialect dialect, + FILE *out); + +/** + * Uninitialize and deallocate a ModifyThenPrintContext + * + * Does any work required by the internal context (e.g. deallocating + * `priv_data`), then deallocates the main context itself. + * + * Object-oriented programs would likely implement this with + * destructor and deallocator functions (i.e. separate functions + * that do the opposite of the allocator and constructor above). + */ +int ModifyThenPrintContext_free(struct ModifyThenPrintContext *ctx); + +/** + * Configure a ModifyThenPrintContext + * + * Checks that the arguments are valid in the context's dialect, + * then updates the options as necessary + * + * Object-oriented programs would likely represent this as a + * pair of "setter" functions (functions whose only job is to + * validate and update a value). + */ +int ModifyThenPrintContext_configure(struct ModifyThenPrintContext *ctx, + char *replace_this, + char *with_this); + +/** + * Print a single message + * + * Object-oriented programs would likely represent this with an ordinary + * member function (a function called like `context->function(...)` + * instead of `function( context, ... )`). + */ +int ModifyThenPrintContext_print(struct ModifyThenPrintContext *ctx, + char *msg); + +/** + * Print the contents of a ModifyThenPrintContext to a filehandle + * + * Provides human-readable information about keys and values. + * + * Object-oriented programs would likely represent this with some kind of + * `serialize` function (a function that iterates through all members, + * printing each one's name and contents to the file). These can be member + * functions, but in practice many languages implement it some other way - + * an example of the lack of focus on reflection in object-oriented languages. + */ +int ModifyThenPrintContext_dump(struct ModifyThenPrintContext **ctx, + FILE *dump_fh); + +/** + * How this context might be used in practice + */ +int print_hello_world() +{ + + int ret = 0; + + struct ModifyThenPrintContext *ctx; + + if ( ModifyThenPrintContext_alloc_context( &ctx, MODIFY_THEN_PRINT_DIALECT_REGEX, stdout ) < 0 ) { + ret = -1; + goto EXIT_WITHOUT_CLEANUP; + } + + if ( ModifyThenPrintContext_configure(ctx, "Hi|Hullo", "Hello") < 0 ) { + ret = -1; + goto FINISH; + } + + if ( ModifyThenPrintContext_print( ctx, "Hi, world!\n" ) < 0 ) { + ret = -1; + goto FINISH; + } + + if ( ModifyThenPrintContext_print( ctx, "Hullo, world!\n" ) < 0 ) { + ret = -1; + goto FINISH; + } + + FINISH: + if ( ModifyThenPrintContext_free( ctx ) ) { + ret = -1; + goto EXIT_WITHOUT_CLEANUP; + } + + EXIT_WITHOUT_CLEANUP: + return ret; + +} +``` + +## FFmpeg context structures + +The example above shows a generic context structure and its associated +functions. Some FFmpeg contexts are no more complex than that example, +just as some objects are just key/value stores with some functions attached. +But here are some examples to show the variety of contexts available in FFmpeg. + +AVHashContext presents a generic API for hashing data. @ref hash.h +"Its associated functions" show how to create, use and destroy a hash. +The exact algorithm is specified by passing a string to av_hash_alloc(), +and the list of strings can be retrieved from av_hash_names(). +Algorithm-specific internal context is stored in AVHashContext::ctx. + +AVCodecContext is a complex context representing data about an encoding or +decoding session. @ref avcodec.h "Its associated functions" provide an +abstract interface to encode and decode data. Like most widely-used contexts, +its first member is an AVClass pointer containing instance-specific information. +That means it's an *AVClass context structure* (discussed below). + +X264Context contains the internal context for an AVCodecContext that uses +the x264 library. @ref libx264.c "Its associated functions" provide a concrete +implementation for interacting with libx264. This class is not directly +accessible through FFmpeg's public interface, so it's easier to change +X264Context than to change AVCodecContext between releases. + +## Reflection with AVClass and AVOptions + +AVClass is a struct containing general information about a context. +It's a generic version of `ModifyThenPrintDialect` in the example. +An *AVClass context structure* is a context whose first member +is an AVClass that reflects the context's interface. + +AVOption is a struct containing information about a member of a context, +or of an internal context. `replace_this` and `with_this` in the example +would echa have an associated AVOption value stored in the context's AVClass. +An *@ref avoptions "AVOptions"-enabled struct* is a structure which reflects +its contents using the @ref avoptions "AVOptions" API. + +The terms *AVClass context structure* and *@ref avoptions "AVOptions"-enabled +struct* have become synonymous in modern usage, although you might notice some +distinction when looking at code written after AVClass was developed but +before @ref avoptions "AVOptions" was added. + +Even though the name "AVClass" implies an analogy to an object-oriented +base [class](https://en.wikipedia.org/wiki/Class_(computer_programming)), they +behave more like [C++ concepts](https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)) +or [Java interfaces](https://en.wikipedia.org/wiki/Interface_(Java)). Unlike a +base class, an AVClass doesn't imply any theoretical relationship between objects, +and contexts of the same type will often have different AVClass values. It's even +theoretically possible for a single AVClass to be shared between contexts of +different types. + +To understand how AVClass and @ref avoptions "AVOptions" work, +consider the requirements for a `libx264` encoder: + +- it has to support common encoder options like "bitrate" +- it has to support encoder-specific options like "profile" + - the exact options could change quickly if a legal ruling forces a change of backend +- it must not expose implementation details (e.g. its encoding buffer) + - otherwise, trivial improvements would force an increase of the major API version +- it has to provide useful feedback to users about unsupported options + +Common encoder options like "bitrate" need to be stored in AVCodecContext itself +to avoid duplicating code, while encoder-specific options like "profile" have to +be stored in the X264Context instance stored in AVCodecContext::priv_data. +But both need to be presented to users (along with help text, default values etc.) +in a way that makes it practical to build user interfaces to get and set those options. + +A context's AVClass contains a list of AVOption members, describing the +user-configurable options in the class. It also contains a tree of further AVClass +members that represent internal context either for the current object or for any +object of the same type that could potentially exist in the current version of FFmpeg. + +The @ref avoptions "AVOptions" API accepts any AVClass context structure, +looks through its AVOption data, and uses that to examine, introspect, and modify +the structure. Because the API doesn't contain any type-specific information, +it can be used to create a general user interface that adapts automatically +when e.g. a new version of FFmpeg adds a new configuration option. + +## Summary + +FFmpeg uses "contexts" in ways that often resemble object-oriented programming. +But it focuses less on encapsulation within arbitrarily complex systems, +and more on providing reflectivity to make good user interfaces. -- 2.43.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".