static GstFlowReturn gst_qt_mux_send_moov (GstQTMux * qtmux, guint64 * _offset, guint64 padded_moov_size, gboolean mind_fast, gboolean fsync_after) { guint64 offset = 0, size = 0; guint8 *data; GstBuffer *buf; GstFlowReturn ret = GST_FLOW_OK; GList *l; guint64 current_time = atoms_get_current_qt_time (); /* update modification times */ qtmux->moov->mvhd.time_info.modification_time = current_time; GST_OBJECT_LOCK (qtmux); for (l = GST_ELEMENT_CAST (qtmux)->sinkpads; l; l = l->next) { GstQTMuxPad *qtpad = (GstQTMuxPad *) l->data; qtpad->trak->mdia.mdhd.time_info.modification_time = current_time; qtpad->trak->tkhd.modification_time = current_time; /* ohos.ext.func.0018 * add additional features to set orientationHint in mp4 file. * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ #ifdef OHOS_EXT_FUNC switch (qtmux->rotation) { case 90: qtpad->trak->tkhd.matrix[0] = 0; qtpad->trak->tkhd.matrix[1] = 1 << 16; qtpad->trak->tkhd.matrix[3] = 65535 << 16; qtpad->trak->tkhd.matrix[4] = 0; break; case 180: qtpad->trak->tkhd.matrix[0] = 65535 << 16; qtpad->trak->tkhd.matrix[4] = 65535 << 16; break; case 270: qtpad->trak->tkhd.matrix[0] = 0; qtpad->trak->tkhd.matrix[1] = 65535 << 16; qtpad->trak->tkhd.matrix[3] = 1 << 16; qtpad->trak->tkhd.matrix[4] = 0; break; default: break; } #endif } /* ohos.ext.func.0016 * add additional features to set geographic location information in mp4 file * the feature is default disable. */ #ifdef OHOS_EXT_FUNC if (qtmux->enable_geolocation) { qtmux->moov->udta.set_location = TRUE; qtmux->moov->udta.latitude = qtmux->latitudex10000; qtmux->moov->udta.longitude = qtmux->longitudex10000; } #endif GST_OBJECT_UNLOCK (qtmux); /* serialize moov */ offset = size = 0; data = NULL; GST_LOG_OBJECT (qtmux, "Copying movie header into buffer"); if (!atom_moov_copy_data (qtmux->moov, &data, &size, &offset)) goto serialize_error; qtmux->last_moov_size = offset; /* Check we have enough reserved space for this and a Free atom */ if (padded_moov_size > 0 && offset + 8 > padded_moov_size) goto too_small_reserved; buf = _gst_buffer_new_take_data (data, offset); GST_DEBUG_OBJECT (qtmux, "Pushing moov atoms"); /* If at EOS, this is the final moov, put in the streamheader * (apparently used by a flumotion util) */ if (qtmux->state == GST_QT_MUX_STATE_EOS) gst_qt_mux_set_header_on_caps (qtmux, buf); if (fsync_after) GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_SYNC_AFTER); ret = gst_qt_mux_send_buffer (qtmux, buf, _offset, mind_fast); /* Write out a free atom if needed */ if (ret == GST_FLOW_OK && offset < padded_moov_size) { GST_LOG_OBJECT (qtmux, "Writing out free atom of size %u", (guint32) (padded_moov_size - offset)); ret = gst_qt_mux_send_free_atom (qtmux, _offset, padded_moov_size - offset, fsync_after); } return ret; too_small_reserved: { GST_ELEMENT_ERROR (qtmux, STREAM, MUX, ("Not enough free reserved header space"), ("Needed %" G_GUINT64_FORMAT " bytes, reserved %" G_GUINT64_FORMAT, offset + 8, padded_moov_size)); return GST_FLOW_ERROR; } serialize_error: { g_free (data); return GST_FLOW_ERROR; } }分析下这段代码
时间: 2025-08-08 20:12:38 浏览: 3
<think>我们正在分析GstQTMux插件的代码逻辑,重点关注moov原子的发送、时间戳更新、旋转矩阵设置和地理位置信息添加。由于代码量较大,我们将分模块解析关键函数和流程。
1. **moov原子的发送**
moov原子(Movie Atom)是MP4文件格式中描述媒体样本信息的容器。在GstQTMux中,moov原子的构建和发送发生在文件头部(初始化阶段)和文件尾部(结束阶段)。关键函数包括:
- `gst_qt_mux_start_file()`: 初始化moov结构,设置基本属性(如timescale、duration等)。
- `gst_qt_mux_add_stream()`: 为每个轨道创建trak原子。
- `gst_qt_mux_stop_file()`: 将完整的moov原子写入文件开头(对于碎片化写入则不同)。
在非碎片化模式下,moov原子在文件开始时会预留空间,在文件结束时再写入实际数据。这是因为moov需要知道整个文件的媒体信息(如时长、样本数量等)。
2. **时间戳更新**
GstQTMux处理时间戳的核心在于将GStreamer的时间转换为MP4的时间基(timescale)。关键点包括:
- 每个轨道有自己的timescale,通过`gst_qt_mux_stream_set_timescale()`设置。
- 时间戳转换:`gst_util_uint64_scale(timestamp, stream->timescale, GST_SECOND)`将GStreamer时间戳(纳秒)转换为轨道时间基下的时间戳。
- 在`gst_qt_mux_prepare_buffer()`中,为每个样本设置解码时间戳(DTS)和显示时间戳(PTS),并写入样本表(stbl)中。
- 引用[2]中提到,无B帧时PTS与DTS一致;有B帧时,需要计算POC(显示顺序)。在GstQTMux中,这通过比较PTS和DTS来实现,并记录在ctts(composition time to sample)原子中。
3. **旋转矩阵设置**
MP4文件可以通过tkhd(Track Header Atom)中的矩阵变换实现视频旋转。在GstQTMux中,旋转信息通常来自上游的标签(tag)事件。关键函数:
- `gst_qt_mux_process_tag()`: 处理GST_TAG_VIDEO_ORIENTATION标签,解析旋转角度(0, 90, 180, 270)。
- `gst_qt_mux_set_rotation()`: 将旋转角度转换为一个3x3矩阵,并设置到tkhd原子中。例如,90度旋转的矩阵为:
[0, -1, 0]
[1, 0, 0]
[0, 0, 1]
然后调用`gst_qt_trak_set_rotation_matrix()`将矩阵写入trak的tkhd原子。
4. **地理位置信息添加**
地理位置信息通过GStreamer的GstEventCustom事件或标签事件传递。在GstQTMux中:
- 处理标签事件(GST_EVENT_TAG)时,解析GST_TAG_GEO_LOCATION标签(包含纬度、经度、高度等)。
- 使用`gst_qt_mux_add_location()`函数将地理位置信息写入moov原子的udta(User Data)部分,具体为创建'©xyz'原子(注意:这里使用版权原子,因为地理位置信息没有标准原子,通常用此方式存储)。
- 格式:'©xyz'原子内容为字符串,格式为"<经度> <纬度> <高度>",例如"+48.8577+002.2950+000/".
下面我们通过代码片段(伪代码)说明关键逻辑:
**moov原子构建(简化)**
```c
static gboolean
gst_qt_mux_start_file (GstQTMux * qtmux)
{
// 初始化moov
qtmux->moov = gst_qt_moov_new ();
// 设置moov的mvhd(Movie Header Atom)信息
gst_qt_moov_set_mvhd (qtmux->moov, ...);
...
}
static gboolean
gst_qt_mux_stop_file (GstQTMux * qtmux)
{
// 更新moov的持续时间等
...
// 将moov原子写入文件开头(非碎片化模式)
if (!qtmux->fragmented) {
gst_qt_moov_render (qtmux->moov, &buffer);
// 将buffer写入文件开头(之前已经预留了空间)
}
}
```
**时间戳更新(在准备样本时)**
```c
static GstFlowReturn
gst_qt_mux_prepare_buffer (GstQTMux * qtmux, GstQTMuxStream * stream, GstBuffer * buffer)
{
// 转换时间戳
pts = GST_BUFFER_PTS (buffer);
dts = GST_BUFFER_DTS (buffer);
// 转换为轨道的时间基
stream_pts = gst_util_uint64_scale (pts, stream->timescale, GST_SECOND);
stream_dts = gst_util_uint64_scale (dts, stream->timescale, GST_SECOND);
// 记录到样本表
gst_qt_mux_stream_add_sample (stream, buffer, stream_dts, stream_pts - stream_dts, ...);
}
```
**旋转矩阵设置**
```c
static void
gst_qt_mux_process_tag (GstQTMux * qtmux, GstTagList * taglist)
{
if (gst_tag_list_get_uint (taglist, GST_TAG_VIDEO_ORIENTATION, &rotation)) {
gst_qt_mux_set_rotation (qtmux, rotation);
}
}
static void
gst_qt_mux_set_rotation (GstQTMux * qtmux, guint rotation)
{
gdouble matrix[9];
switch (rotation) {
case 90:
matrix[0] = 0; matrix[1] = -1; matrix[2] = 0;
matrix[3] = 1; matrix[4] = 0; matrix[5] = 0;
matrix[6] = 0; matrix[7] = 0; matrix[8] = 1;
break;
// 其他角度类似
}
// 设置到每个视频轨道的trak中
for (遍历所有视频轨道) {
gst_qt_trak_set_rotation_matrix (trak, matrix);
}
}
```
**地理位置信息添加**
```c
static void
gst_qt_mux_process_tag (GstQTMux * qtmux, GstTagList * taglist)
{
if (gst_tag_list_get_tag_size (taglist, GST_TAG_GEO_LOCATION) > 0) {
gdouble latitude, longitude, altitude;
if (gst_tag_list_get_geo_location (taglist, "geo-location", &latitude, &longitude, &altitude)) {
gst_qt_mux_add_location (qtmux, latitude, longitude, altitude);
}
}
}
static void
gst_qt_mux_add_location (GstQTMux * qtmux, gdouble latitude, gdouble longitude, gdouble altitude)
{
// 构建字符串,格式: "+经度+纬度+高度/"
gchar *location = g_strdup_printf ("%c%.4f%c%.4f%c%.4f/",
longitude>=0?'+':'-', fabs(longitude),
latitude>=0?'+':'-', fabs(latitude),
altitude>=0?'+':'-', fabs(altitude));
// 将location字符串添加到moov的udta原子中,创建'©xyz'子原子
gst_qt_moov_add_location (qtmux->moov, location);
g_free (location);
}
```
以上是对GstQTMux关键逻辑的分析。请注意,实际代码可能更复杂,涉及错误处理、内存管理、状态转换等。此外,碎片化模式(fragmented)的处理方式有所不同,moov原子在开始时写入基本信息,而样本信息则分散在moof和mdat中。
§§相关问题§§
1. GstQTMux如何处理多轨道(如音频和视频)的同步?
2. 在碎片化MP4(fMP4)模式下,GstQTMux如何组织moof和mdat原子?
3. GstQTMux支持哪些标签(tags)并将其转换为MP4的元数据?
4. 如何扩展GstQTMux以支持自定义元数据?</think>### GStreamer QTMux 代码逻辑分析
#### 1. **MOOV 原子发送逻辑**
MOOV 原子(Movie Box)是 MP4 文件格式的元数据容器,存储轨道、时长、样本索引等关键信息。在 GstQTMux 中的实现逻辑:
- **初始化阶段**
在文件开始时创建 MOOV 原子结构体,初始化 `mvhd`(Movie Header Box)和 `trak`(Track Box)子原子。
代码路径:`gst_qt_mux_start_file()` → `gst_qt_moov_new()`
- **实时填充**
接收数据流时动态更新:
- `mvhd.duration` 根据最长轨道的时长更新
- `trak.mdia.mdhd.duration` 记录各轨道时长
- `trak.tkhd.duration` 同步轨道时长
- **最终写入**
文件结束时调用 `gst_qt_mux_stop_file()` 将 MOOV 原子写入文件头部(非碎片化模式)或作为独立片段(碎片化模式)。
> **关键设计**:MOOV 原子在非碎片化模式中仅写入一次(文件末尾),碎片化模式中每个片段均包含 `moof`+`mdat` 对[^1]。
#### 2. **时间戳更新机制**
时间戳处理涉及媒体流同步和 MP4 时间基转换:
```c
// 时间基转换示例(纳秒 → 轨道时间基)
timestamp_in_timescale = gst_util_uint64_scale(
buffer_pts, stream->timescale, GST_SECOND
);
```
- **时间基管理**
每个轨道通过 `gst_qt_mux_stream_set_timescale()` 设置独立时间基,通常:
- 视频:90000 Hz(匹配 MPEG 时间基)
- 音频:采样率(如 44100 Hz)
- **PTS/DTS 处理**
- 无 B 帧时直接复制 PTS 到 DTS
- 含 B 帧时通过 `ctts`(Composition Time Box)存储 PTS 偏移值[^2]
- **关键函数**
`gst_qt_mux_prepare_buffer()` 处理时间戳转换,`gst_qt_mux_stream_add_sample()` 写入 `stts`(Decoding Time Box)和 `stss`(Sync Sample Box)。
#### 3. **旋转矩阵设置**
视频旋转通过 `tkhd`(Track Header Box)的变换矩阵实现:
```c
// 设置 90° 旋转矩阵示例
static gdouble rotate_90_matrix[9] = {
0, -1, 0, // 第一行
1, 0, 0, // 第二行
0, 0, 1 // 第三行
};
```
- **触发条件**
解析 `GST_TAG_VIDEO_ORIENTATION` 标签:
```c
gst_tag_list_get_uint(taglist, GST_TAG_VIDEO_ORIENTATION, &rotation_angle);
```
- **矩阵写入**
`gst_qt_trak_set_rotation_matrix()` 将旋转矩阵写入 `tkhd.matrix` 字段,支持 0°、90°、180°、270° 标准角度。
#### 4. **地理位置信息添加**
地理位置数据存储在 `udta`(User Data Box)的 `©xyz` 原子中:
- **数据格式**
ISO-6709 标准字符串:`"+48.8577+002.2950+000/"`(纬度/经度/海拔)
- **处理流程**:
1. 接收 `GST_TAG_GEO_LOCATION` 标签事件
2. 调用 `gst_qt_mux_add_location()` 格式化为字符串
3. 通过 `gst_qt_moov_add_udta_entry()` 写入 `udta` 容器
### 关键代码引用
- **MOOV 构建**:`gstqtmux.c` → `GstQTMux` 结构体维护 MOOV 状态
- **时间戳转换**:`gstqtmux.c` → `gst_qt_mux_stream_set_timescale()`
- **旋转矩阵**:`gstqtmux.c` → `gst_qt_mux_set_rotation()`
- **地理信息**:`gstqtmux.c` → `gst_qt_mux_add_location()`
阅读全文
相关推荐




















