HarmonyOS 6.0 AVCodec Kit adds userData passthrough for custom data sources, allowing
OH_AVDataSourcecallbacks to access business context directly. This solves key challenges in multi-instance media processing, thread isolation, and C/C++ bridging. Keywords: HarmonyOS 6.0, AVCodec Kit, userData passthrough.
Technical Specification Snapshot
| Parameter | Description |
|---|---|
| Platform | HarmonyOS 6.0 |
| API Version | API 20 |
| Language | C / C++ |
| Core Modules | AVCodec Kit, AVSource |
| Key API | OH_AVSource_CreateWithDataSourceExt |
| Key Structures | OH_AVDataSource, OH_AVDataSourceExt |
| Callback Capability | readAt supports receiving userData |
| Applicable Scenarios | Encrypted media, memory cache, database BLOB, multi-stream pipelines |
| Original Post Engagement | 94 views, 5 likes, 6 bookmarks |
| Core Dependencies | native_avsource.h, native_avcodec_base.h |
AI Visual Insight: This image is the article cover screenshot. Its primary purpose is to identify the topic and page layout. It does not expose technical details such as interface structures, call chains, or memory layouts, so its visual value is mainly for content orientation rather than implementation guidance.
HarmonyOS 6.0 closes the context-passing gap in custom data sources
In earlier versions of AVCodec Kit, developers could provide media read capabilities through OH_AVDataSource, but the readAt callback could not naturally carry business context. Once you entered scenarios involving multi-instance processing, concurrent demuxing, or object-oriented encapsulation, this limitation became a serious problem.
Common workarounds included global variables, static members, or external mapping tables. These approaches can work, but they break encapsulation boundaries and increase the risk of thread contention and instance cross-talk. They are especially problematic in complex media applications such as players, editors, and VR multi-stream systems.
The legacy data source abstraction was lightweight but not extensible enough
typedef struct OH_AVDataSource {
int64_t size; // Total length of the data source
OH_AVDataSourceReadAt readAt; // Callback for reading data at an offset
} OH_AVDataSource;
This structure shows that the legacy API only focused on “how large the data is” and “how to read it,” but not on “who is reading” or “what context the read operation depends on.”
The new extension API turns userData into a standard passthrough channel
HarmonyOS 6.0 introduces OH_AVSource_CreateWithDataSourceExt and OH_AVDataSourceExt. The core value is not simply “one more parameter,” but that media callbacks now have a standardized context entry point for the first time.
When creating an OH_AVSource, developers can pass a void *userData, and the system returns it in subsequent callbacks. This allows the callback function to access instance-level state, object handles, cache pools, decryptors, or log tags directly.
The new API design preserves compatibility during API evolution
OH_AVSource *OH_AVSource_CreateWithDataSourceExt(
OH_AVDataSourceExt *dataSource,
void *userData
);
It exists alongside the legacy API, which means HarmonyOS preserves backward compatibility while placing enhanced capabilities in an extended version. This is a stable and practical way to evolve Native APIs.
The userData mechanism directly solves three engineering pain points
The first pain point is multi-instance isolation. Each media source can bind its own independent context, so you no longer need to infer which instance a callback belongs to from global state.
The second pain point is C-to-C++ bridging. Although AVCodec Kit exposes a C API, userData can carry a C++ object pointer and restore object-oriented semantics inside the callback.
The third pain point is state aggregation. Keys, caches, connection handles, track metadata, and other runtime state can be placed into a single context structure, which reduces scattered parameters and lowers maintenance cost.
Define an aggregated context structure first
#include <multimedia/player_framework/native_avsource.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <stdint.h>
typedef struct {
uint8_t *data; // Start address of the media buffer
int64_t size; // Current data source length
const char *sourceName; // Used for logging and instance identification
void *userContext; // Reserved extension field, such as a decryptor or database handle
} CustomDataSourceContext;
The goal of this structure is to aggregate all state required for reading into a single object, making callback passthrough and resource management easier.
The readAt callback should prioritize boundary checks and context validation
Once userData is supported, the first step inside readAt should no longer be calling memcpy directly. Instead, validate the context, offset, and buffer first. Since all instance state enters through this path, a single error can lead to an out-of-bounds read or a crash.
int32_t CustomDataSource_ReadAt(void *userData, int64_t offset,
uint8_t *buffer, int32_t length)
{
CustomDataSourceContext *ctx = (CustomDataSourceContext *)userData; // Restore the instance context
if (ctx == NULL || buffer == NULL || offset < 0 || length <= 0) {
return -1; // Invalid arguments, return an error immediately
}
if (offset >= ctx->size) {
return 0; // Reached EOF
}
int64_t remain = ctx->size - offset;
int32_t bytesToRead = (length < remain) ? length : (int32_t)remain;
memcpy(buffer, ctx->data + offset, bytesToRead); // Copy the target segment from the custom data source
return bytesToRead;
}
This code demonstrates the minimum viable loop for a custom data source: restore the context, perform boundary checks, and return readable data at the requested offset.
You must design the lifecycle clearly when creating an AVSource
The real source of bugs is usually not the callback signature, but the resource release order. While OH_AVSource is alive, the system may enter readAt again at any time. Therefore, the object pointed to by userData must never be released too early.
int CreateMediaSource(CustomDataSourceContext *ctx)
{
OH_AVDataSourceExt dataSource;
dataSource.size = ctx->size; // Bind the total data length
dataSource.readAt = CustomDataSource_ReadAt; // Bind the read callback
OH_AVSource *source = OH_AVSource_CreateWithDataSourceExt(&dataSource, ctx);
if (source == NULL) {
return -1; // Creation failed
}
// ... Connect to Demuxer / Player later
OH_AVSource_Destroy(source); // Destroy source first to ensure no further callbacks occur
return 0;
}
The key point in this flow is not the syntax, but the order: destroy source first, then release ctx and its internal resources.
Lifecycle rules must remain aligned with OH_AVSource
- The lifetime of
userDatamust cover the entire lifetime ofOH_AVSource. - The buffers, handles, and key objects referenced by
readAtmust not be released early. - If asynchronous chains exist, make sure upper-layer playback or demuxing has stopped before destruction.
- Do not pass temporary stack objects directly as
userData.
Multi-instance scenarios show the value of the new API most clearly
Player list previews, video editing timelines, picture-in-picture, and multi-track composition all create multiple media sources at the same time. In these cases, sharing one callback implementation is ideal, but only if each callback can identify its owning instance.
typedef struct {
OH_AVSource *source;
CustomDataSourceContext *ctx;
} MediaInstance;
// Each instance holds its own ctx
// When the callback runs, userData automatically maps it to the correct instance
This design avoids repetitive implementations such as “one callback per source,” and it also removes the need for reverse lookups through a global hash table. The resulting structure is clearer, and the threading model is easier to reason about.
This capability is especially well suited to four types of media systems
Encrypted media playback can carry decryption context directly into the read stage
When media segments are stored in encrypted form, userData can carry keys, decryptor instances, and block mapping information. The callback can read ciphertext by offset, decrypt it, and write it into the output buffer on demand, which reduces intermediate copies.
Memory-cached playback can encapsulate cache state and preload strategy in the context
Network players typically maintain a ring buffer, downloaded ranges, and prefetch thresholds. By placing this state in userData, readAt can decide whether to return data immediately or trigger refill logic based on cache hit status.
Database-backed media can carry record location metadata directly
If media is stored in a database BLOB, userData can store the database connection, record ID, and offset mapping. This avoids writing temporary files and works well for albums, chat histories, and offline media containers.
Multi-stream merge scenarios make track-level distinction easier
In VR, multi-camera, or composite-track applications, different tracks may come from different sources. userData can include a track ID, time base, or routing tag, allowing a shared callback to become track-aware.
This API update fundamentally improves the engineering usability of AVCodec Kit
From an interface perspective, the change simply turns context passing into a standard capability. From an engineering perspective, it makes HarmonyOS Native media development feel much closer to the design patterns used in mature system libraries. Developers no longer need to build patch-style architecture around callback limitations.
For team collaboration, this also means cleaner encapsulation boundaries. The data reading layer depends only on the context structure, does not pollute the global namespace, and does not leak instance state externally. That makes it a better fit for large-scale project maintenance.
FAQ
Q1: What is the best thing to pass through userData?
The best choice is an instance-level context structure pointer, such as a buffer address, length, log tag, decryptor handle, database connection, or C++ object pointer. Do not pass the address of a temporary local variable.
Q2: How should I choose between OH_AVDataSourceExt and the legacy OH_AVDataSource?
If your scenario only reads a static file and has no instance-state dependency, the legacy API is still acceptable. If your design involves multiple instances, custom caching, decryption, databases, or object bridging, prefer the extended API.
Q3: What is the most common pitfall?
The biggest risk is incorrect lifecycle management: releasing userData, the underlying buffer, or related handles before OH_AVSource is destroyed. The second common issue is insufficient boundary checking in the callback, which can cause out-of-bounds access or incorrect EOF handling.
AI Readability Summary
This article explains the new OH_AVSource_CreateWithDataSourceExt and OH_AVDataSourceExt capabilities introduced in HarmonyOS 6.0 AVCodec Kit. It shows how to safely pass userData through custom media data source callbacks to eliminate reliance on global variables, improve multi-instance isolation, and manage lifecycle constraints correctly.