Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2020-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MHAS write filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/bitstream.h>
29 :
30 : #include <gpac/avparse.h>
31 :
32 :
33 : typedef struct
34 : {
35 : //opts
36 : Bool exporter, syncp;
37 :
38 : //only one input pid declared
39 : GF_FilterPid *ipid;
40 : //only one output pid declared
41 : GF_FilterPid *opid;
42 :
43 : Bool is_mpha;
44 :
45 : GF_BitStream *bs_w;
46 :
47 : u8 *dsi;
48 : u32 dsi_size;
49 : u32 dsi_crc;
50 : Bool update_dsi;
51 : GF_Fraction fdsi;
52 : u64 last_cts;
53 : u32 timescale;
54 : } GF_MHASMxCtx;
55 :
56 :
57 :
58 :
59 1 : GF_Err mhasmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
60 : {
61 : u32 crc;
62 : const GF_PropertyValue *p;
63 1 : GF_MHASMxCtx *ctx = gf_filter_get_udta(filter);
64 :
65 1 : if (is_remove) {
66 0 : ctx->ipid = NULL;
67 0 : if (ctx->opid) {
68 0 : gf_filter_pid_remove(ctx->opid);
69 0 : ctx->opid = NULL;
70 : }
71 : return GF_OK;
72 : }
73 1 : if (! gf_filter_pid_check_caps(pid))
74 : return GF_NOT_SUPPORTED;
75 :
76 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
77 1 : if (!p) return GF_NOT_SUPPORTED;
78 1 : ctx->is_mpha = (p->value.uint==GF_CODECID_MPHA) ? GF_TRUE : GF_FALSE;
79 :
80 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
81 1 : if (!p) return GF_NOT_SUPPORTED;
82 :
83 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
84 1 : if (!p) {
85 0 : if (ctx->is_mpha)
86 : return GF_NOT_SUPPORTED;
87 0 : ctx->dsi_crc = 0;
88 0 : ctx->dsi = NULL;
89 0 : ctx->dsi_size = 0;
90 1 : } else if (p->value.data.size<=5) {
91 : return GF_NON_COMPLIANT_BITSTREAM;
92 : } else {
93 1 : crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
94 1 : if (crc != ctx->dsi_crc) {
95 1 : ctx->dsi_crc = crc;
96 1 : ctx->update_dsi = GF_TRUE;
97 1 : ctx->dsi = p->value.data.ptr + 5;
98 1 : ctx->dsi_size = p->value.data.size - 5;
99 : }
100 : }
101 :
102 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
103 1 : if (!p) return GF_NOT_SUPPORTED;
104 1 : ctx->timescale = p->value.uint;
105 :
106 1 : if (!ctx->opid) {
107 1 : ctx->opid = gf_filter_pid_new(filter);
108 : }
109 1 : ctx->ipid = pid;
110 1 : gf_filter_pid_copy_properties(ctx->opid, pid);
111 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
112 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, &PROP_BOOL(GF_TRUE) );
113 1 : gf_filter_pid_set_framing_mode(ctx->ipid, GF_TRUE);
114 1 : return GF_OK;
115 : }
116 :
117 :
118 :
119 1340 : GF_Err mhasmx_process(GF_Filter *filter)
120 : {
121 1340 : GF_MHASMxCtx *ctx = gf_filter_get_udta(filter);
122 : GF_FilterPacket *pck, *dst_pck;
123 : u8 *data, *output;
124 : u32 pck_size, size;
125 : Bool sap;
126 : Bool has_sync = GF_FALSE;
127 :
128 1340 : pck = gf_filter_pid_get_packet(ctx->ipid);
129 1340 : if (!pck) {
130 49 : if (gf_filter_pid_is_eos(ctx->ipid)) {
131 1 : gf_filter_pid_set_eos(ctx->opid);
132 1 : return GF_EOS;
133 : }
134 : return GF_OK;
135 : }
136 :
137 1291 : data = (char *) gf_filter_pck_get_data(pck, &pck_size);
138 :
139 1291 : sap = (gf_filter_pck_get_sap(pck)==GF_FILTER_SAP_1) ? GF_TRUE : GF_FALSE;
140 :
141 1291 : if (ctx->is_mpha) {
142 : u32 hdr_size = 0;
143 1291 : if (ctx->syncp || sap) {
144 : hdr_size += 3;
145 : }
146 1291 : if (sap) {
147 31 : hdr_size += ctx->dsi_size + 2; //base header needs 2 bytes
148 31 : if (ctx->dsi_size > 2046)
149 0 : hdr_size += 3;
150 : }
151 1291 : hdr_size += 2; //base header needs 2 bytes
152 1291 : if (pck_size>2046)
153 0 : hdr_size += 3;
154 :
155 1291 : size = pck_size + hdr_size;
156 1291 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
157 1291 : if (!dst_pck) return GF_OUT_OF_MEM;
158 :
159 1291 : gf_bs_reassign_buffer(ctx->bs_w, output, size);
160 :
161 : //write MASH headers
162 1291 : if (ctx->syncp || sap) {
163 1291 : gf_bs_write_u8(ctx->bs_w, 0xC0);
164 1291 : gf_bs_write_u8(ctx->bs_w, 0x01);
165 1291 : gf_bs_write_u8(ctx->bs_w, 0xA5);
166 : }
167 1291 : if (sap) {
168 31 : gf_bs_write_int(ctx->bs_w, 1, 3); //pck type = config
169 31 : gf_bs_write_int(ctx->bs_w, 0, 2); //label = 0
170 : //size
171 31 : if (ctx->dsi_size > 2046) {
172 0 : gf_bs_write_int(ctx->bs_w, 2047, 11);
173 0 : gf_bs_write_int(ctx->bs_w, ctx->dsi_size - 2047, 24);
174 : } else {
175 31 : gf_bs_write_int(ctx->bs_w, ctx->dsi_size, 11);
176 : }
177 31 : gf_bs_write_data(ctx->bs_w, ctx->dsi, ctx->dsi_size);
178 : }
179 1291 : gf_bs_write_int(ctx->bs_w, 2, 3); //pck type = frame
180 1291 : gf_bs_write_int(ctx->bs_w, 1, 2); //label
181 1291 : if (pck_size > 2046) {
182 0 : gf_bs_write_int(ctx->bs_w, 2047, 11);
183 0 : gf_bs_write_int(ctx->bs_w, pck_size - 2047, 24);
184 : } else {
185 1291 : gf_bs_write_int(ctx->bs_w, pck_size, 11);
186 : }
187 : //copy payload
188 1291 : memcpy(output+hdr_size, data, pck_size);
189 :
190 : } else {
191 0 : if ((data[0]==0xC0) && (data[1]==0x01) && (data[2]==0xA5))
192 : has_sync = GF_TRUE;
193 :
194 0 : size = pck_size;
195 0 : if ((ctx->syncp && !has_sync) || (sap && !has_sync)) {
196 0 : size += 3;
197 0 : dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
198 0 : if (output) {
199 0 : output[0] = 0xC0;
200 0 : output[1] = 0x01;
201 0 : output[2] = 0xA5;
202 0 : memcpy(output+3, data, pck_size);
203 : }
204 : } else {
205 0 : dst_pck = gf_filter_pck_new_ref(ctx->opid, 0, 0, pck);
206 : }
207 0 : if (!dst_pck) return GF_OUT_OF_MEM;
208 : }
209 :
210 1291 : gf_filter_pck_merge_properties(pck, dst_pck);
211 1291 : gf_filter_pck_set_byte_offset(dst_pck, GF_FILTER_NO_BO);
212 :
213 1291 : gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
214 :
215 1291 : gf_filter_pck_send(dst_pck);
216 1291 : gf_filter_pid_drop_packet(ctx->ipid);
217 1291 : return GF_OK;
218 : }
219 :
220 1 : static GF_Err mhasmx_initialize(GF_Filter *filter)
221 : {
222 1 : GF_MHASMxCtx *ctx = gf_filter_get_udta(filter);
223 1 : ctx->bs_w = gf_bs_new((u8*)ctx, 1, GF_BITSTREAM_WRITE);
224 1 : return GF_OK;
225 : }
226 :
227 1 : static void mhasmx_finalize(GF_Filter *filter)
228 : {
229 1 : GF_MHASMxCtx *ctx = gf_filter_get_udta(filter);
230 1 : if (ctx->bs_w) gf_bs_del(ctx->bs_w);
231 1 : }
232 :
233 : static const GF_FilterCapability MHASMxCaps[] =
234 : {
235 : CAP_UINT(GF_CAPS_INPUT_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
236 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_MPHA),
237 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
238 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
239 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
240 : CAP_BOOL(GF_CAPS_OUTPUT, GF_PROP_PID_UNFRAMED, GF_TRUE),
241 : };
242 :
243 :
244 : #define OFFS(_n) #_n, offsetof(GF_MHASMxCtx, _n)
245 : static const GF_FilterArgs MHASMxArgs[] =
246 : {
247 : { OFFS(syncp), "if set, insert sync packet at each frame, otherwise only at SAP", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_ADVANCED},
248 : {0}
249 : };
250 :
251 :
252 : GF_FilterRegister MHASMxRegister = {
253 : .name = "ufmhas",
254 : GF_FS_SET_DESCRIPTION("MHAS writer")
255 : GF_FS_SET_HELP("This filter converts MPEG-H Audio streams into MHAS encapsulated data.")
256 : .private_size = sizeof(GF_MHASMxCtx),
257 : .args = MHASMxArgs,
258 : .initialize = mhasmx_initialize,
259 : .finalize = mhasmx_finalize,
260 : SETCAPS(MHASMxCaps),
261 : .configure_pid = mhasmx_configure_pid,
262 : .process = mhasmx_process
263 : };
264 :
265 :
266 2877 : const GF_FilterRegister *mhasmx_register(GF_FilterSession *session)
267 : {
268 2877 : return &MHASMxRegister;
269 : }
270 :
|