~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.2.1/third_party/mp3/mp3_writer.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: mp3_writer.c 4483 2013-04-19 09:52:02Z ming $ */
 
2
/* 
 
3
 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
18
 */
 
19
 
 
20
/*
 
21
 * Contributed by:
 
22
 *  Toni < buldozer at aufbix dot org >
 
23
 */
 
24
#include "mp3_port.h"
 
25
#include <pjmedia/errno.h>
 
26
#include <pj/assert.h>
 
27
#include <pj/file_access.h>
 
28
#include <pj/file_io.h>
 
29
#include <pj/log.h>
 
30
#include <pj/pool.h>
 
31
#include <pj/string.h>
 
32
#include <pj/unicode.h>
 
33
 
 
34
 
 
35
/* Include BladeDLL declarations */
 
36
#include "BladeMP3EncDLL.h"
 
37
 
 
38
 
 
39
#define THIS_FILE           "mp3_writer.c"
 
40
#define SIGNATURE           PJMEDIA_SIG_CLASS_PORT_AUD('M','W')
 
41
#define BYTES_PER_SAMPLE    2
 
42
 
 
43
static struct BladeDLL
 
44
{
 
45
    void                *hModule;
 
46
    int                  refCount;
 
47
    BEINITSTREAM         beInitStream;
 
48
    BEENCODECHUNK        beEncodeChunk;
 
49
    BEDEINITSTREAM       beDeinitStream;
 
50
    BECLOSESTREAM        beCloseStream;
 
51
    BEVERSION            beVersion;
 
52
    BEWRITEVBRHEADER     beWriteVBRHeader;
 
53
    BEWRITEINFOTAG       beWriteInfoTag;
 
54
} BladeDLL;
 
55
 
 
56
 
 
57
struct mp3_file_port
 
58
{
 
59
    pjmedia_port    base;
 
60
    pj_size_t       total;
 
61
    pj_oshandle_t   fd;
 
62
    pj_size_t       cb_size;
 
63
    pj_status_t    (*cb)(pjmedia_port*, void*);
 
64
 
 
65
    unsigned        silence_duration;
 
66
 
 
67
    pj_str_t                    mp3_filename;
 
68
    pjmedia_mp3_encoder_option  mp3_option;
 
69
    unsigned                    mp3_samples_per_frame;
 
70
    pj_int16_t                 *mp3_sample_buf;
 
71
    unsigned                    mp3_sample_pos;
 
72
    HBE_STREAM                  mp3_stream;
 
73
    unsigned char              *mp3_buf;
 
74
};
 
75
 
 
76
 
 
77
static pj_status_t file_put_frame(pjmedia_port *this_port, 
 
78
                                  const pjmedia_frame *frame);
 
79
static pj_status_t file_get_frame(pjmedia_port *this_port, 
 
80
                                  pjmedia_frame *frame);
 
81
static pj_status_t file_on_destroy(pjmedia_port *this_port);
 
82
 
 
83
 
 
84
#if defined(PJ_WIN32) || defined(_WIN32) || defined(WIN32)
 
85
 
 
86
#include <windows.h>
 
87
#define DLL_NAME    PJ_T("LAME_ENC.DLL")
 
88
 
 
89
/*
 
90
 * Load BladeEncoder DLL.
 
91
 */
 
92
static pj_status_t init_blade_dll(void)
 
93
{
 
94
    if (BladeDLL.refCount == 0) {
 
95
        #define GET_PROC(type, name)  \
 
96
            BladeDLL.name = (type)GetProcAddress(BladeDLL.hModule, PJ_T(#name)); \
 
97
            if (BladeDLL.name == NULL) { \
 
98
                PJ_LOG(1,(THIS_FILE, "Unable to find %s in %s", #name, DLL_NAME)); \
 
99
                return PJ_RETURN_OS_ERROR(GetLastError()); \
 
100
            }
 
101
 
 
102
        BE_VERSION beVersion;
 
103
        BladeDLL.hModule = (void*)LoadLibrary(DLL_NAME);
 
104
        if (BladeDLL.hModule == NULL) {
 
105
            pj_status_t status = PJ_RETURN_OS_ERROR(GetLastError());
 
106
            char errmsg[PJ_ERR_MSG_SIZE];
 
107
 
 
108
            pj_strerror(status, errmsg, sizeof(errmsg));
 
109
            PJ_LOG(1,(THIS_FILE, "Unable to load %s: %s", DLL_NAME, errmsg));
 
110
            return status;
 
111
        }
 
112
 
 
113
        GET_PROC(BEINITSTREAM, beInitStream);
 
114
        GET_PROC(BEENCODECHUNK, beEncodeChunk);
 
115
        GET_PROC(BEDEINITSTREAM, beDeinitStream);
 
116
        GET_PROC(BECLOSESTREAM, beCloseStream);
 
117
        GET_PROC(BEVERSION, beVersion);
 
118
        GET_PROC(BEWRITEVBRHEADER, beWriteVBRHeader);
 
119
        GET_PROC(BEWRITEINFOTAG, beWriteInfoTag);
 
120
 
 
121
        #undef GET_PROC
 
122
 
 
123
        BladeDLL.beVersion(&beVersion);
 
124
        PJ_LOG(4,(THIS_FILE, "%s encoder v%d.%d loaded (%s)", DLL_NAME,
 
125
                  beVersion.byMajorVersion, beVersion.byMinorVersion,
 
126
                  beVersion.zHomepage));
 
127
    }
 
128
    ++BladeDLL.refCount;
 
129
    return PJ_SUCCESS;
 
130
}
 
131
 
 
132
/*
 
133
 * Decrement the reference counter of the DLL.
 
134
 */
 
135
static void deinit_blade_dll()
 
136
{
 
137
    --BladeDLL.refCount;
 
138
    if (BladeDLL.refCount == 0 && BladeDLL.hModule) {
 
139
        FreeLibrary(BladeDLL.hModule);
 
140
        BladeDLL.hModule = NULL;
 
141
        PJ_LOG(4,(THIS_FILE, "%s unloaded", DLL_NAME));
 
142
    }
 
143
}
 
144
 
 
145
#else
 
146
 
 
147
static pj_status_t init_blade_dll(void)
 
148
{
 
149
    PJ_LOG(1,(THIS_FILE, "Error: MP3 writer port only works on Windows for now"));
 
150
    return PJ_ENOTSUP;
 
151
}
 
152
 
 
153
static void deinit_blade_dll()
 
154
{
 
155
}
 
156
#endif
 
157
 
 
158
 
 
159
 
 
160
/*
 
161
 * Initialize MP3 encoder.
 
162
 */
 
163
static pj_status_t init_mp3_encoder(struct mp3_file_port *fport,
 
164
                                    pj_pool_t *pool)
 
165
{
 
166
    BE_CONFIG LConfig;
 
167
    unsigned  long InSamples;
 
168
    unsigned  long OutBuffSize;
 
169
    long MP3Err;
 
170
 
 
171
    /*
 
172
     * Initialize encoder configuration.
 
173
     */
 
174
    pj_bzero(&LConfig, sizeof(BE_CONFIG));
 
175
    LConfig.dwConfig = BE_CONFIG_LAME;
 
176
    LConfig.format.LHV1.dwStructVersion = 1;
 
177
    LConfig.format.LHV1.dwStructSize = sizeof(BE_CONFIG);
 
178
    LConfig.format.LHV1.dwSampleRate = PJMEDIA_PIA_SRATE(&fport->base.info);
 
179
    LConfig.format.LHV1.dwReSampleRate = 0;
 
180
 
 
181
    if (PJMEDIA_PIA_CCNT(&fport->base.info)==1)
 
182
        LConfig.format.LHV1.nMode = BE_MP3_MODE_MONO;
 
183
    else if (PJMEDIA_PIA_CCNT(&fport->base.info)==2)
 
184
        LConfig.format.LHV1.nMode = BE_MP3_MODE_STEREO;
 
185
    else
 
186
        return PJMEDIA_ENCCHANNEL;
 
187
 
 
188
    LConfig.format.LHV1.dwBitrate = fport->mp3_option.bit_rate / 1000;
 
189
    LConfig.format.LHV1.nPreset = LQP_NOPRESET;
 
190
    LConfig.format.LHV1.bCopyright = 0;
 
191
    LConfig.format.LHV1.bCRC = 1;
 
192
    LConfig.format.LHV1.bOriginal = 1;
 
193
    LConfig.format.LHV1.bPrivate = 0;
 
194
 
 
195
    if (!fport->mp3_option.vbr) {
 
196
        LConfig.format.LHV1.nVbrMethod = VBR_METHOD_NONE;
 
197
        LConfig.format.LHV1.bWriteVBRHeader = 0;
 
198
        LConfig.format.LHV1.bEnableVBR = 0;
 
199
    } else {
 
200
        LConfig.format.LHV1.nVbrMethod = VBR_METHOD_DEFAULT;
 
201
        LConfig.format.LHV1.bWriteVBRHeader = 1;
 
202
        LConfig.format.LHV1.dwVbrAbr_bps = fport->mp3_option.bit_rate;
 
203
        LConfig.format.LHV1.nVBRQuality =  (pj_uint16_t)
 
204
                                           fport->mp3_option.quality;
 
205
        LConfig.format.LHV1.bEnableVBR = 1;
 
206
    }
 
207
 
 
208
    LConfig.format.LHV1.nQuality = (pj_uint16_t) 
 
209
                                   (((0-fport->mp3_option.quality-1)<<8) | 
 
210
                                    fport->mp3_option.quality);
 
211
 
 
212
    /*
 
213
     * Init MP3 stream.
 
214
     */
 
215
    InSamples = 0;
 
216
    MP3Err = BladeDLL.beInitStream(&LConfig, &InSamples, &OutBuffSize,
 
217
                                   &fport->mp3_stream);
 
218
    if (MP3Err != BE_ERR_SUCCESSFUL) 
 
219
        return PJMEDIA_ERROR;
 
220
 
 
221
    /*
 
222
     * Allocate sample buffer.
 
223
     */
 
224
    fport->mp3_samples_per_frame = (unsigned)InSamples;
 
225
    fport->mp3_sample_buf = pj_pool_alloc(pool, fport->mp3_samples_per_frame * 2);
 
226
    if (!fport->mp3_sample_buf)
 
227
        return PJ_ENOMEM;
 
228
 
 
229
    /*
 
230
     * Allocate encoded MP3 buffer.
 
231
     */
 
232
    fport->mp3_buf = pj_pool_alloc(pool, (pj_size_t)OutBuffSize);
 
233
    if (fport->mp3_buf == NULL)
 
234
        return PJ_ENOMEM;
 
235
 
 
236
    
 
237
    return PJ_SUCCESS;
 
238
}
 
239
 
 
240
 
 
241
/*
 
242
 * Create MP3 file writer port.
 
243
 */
 
244
PJ_DEF(pj_status_t) 
 
245
pjmedia_mp3_writer_port_create( pj_pool_t *pool,
 
246
                                const char *filename,
 
247
                                unsigned sampling_rate,
 
248
                                unsigned channel_count,
 
249
                                unsigned samples_per_frame,
 
250
                                unsigned bits_per_sample,
 
251
                                const pjmedia_mp3_encoder_option *param_option,
 
252
                                pjmedia_port **p_port )
 
253
{
 
254
    struct mp3_file_port *fport;
 
255
    pj_status_t status;
 
256
 
 
257
    status = init_blade_dll();
 
258
    if (status != PJ_SUCCESS)
 
259
        return status;
 
260
 
 
261
    /* Check arguments. */
 
262
    PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL);
 
263
 
 
264
    /* Only supports 16bits per sample for now. */
 
265
    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
 
266
 
 
267
    /* Create file port instance. */
 
268
    fport = pj_pool_zalloc(pool, sizeof(struct mp3_file_port));
 
269
    PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM);
 
270
 
 
271
    /* Initialize port info. */
 
272
    pj_strdup2_with_null(pool, &fport->mp3_filename, filename);
 
273
    pjmedia_port_info_init(&fport->base.info, &fport->mp3_filename, SIGNATURE,
 
274
                           sampling_rate, channel_count, bits_per_sample,
 
275
                           samples_per_frame);
 
276
 
 
277
    fport->base.get_frame = &file_get_frame;
 
278
    fport->base.put_frame = &file_put_frame;
 
279
    fport->base.on_destroy = &file_on_destroy;
 
280
 
 
281
 
 
282
    /* Open file in write and read mode.
 
283
     * We need the read mode because we'll modify the WAVE header once
 
284
     * the recording has completed.
 
285
     */
 
286
    status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd);
 
287
    if (status != PJ_SUCCESS) {
 
288
        deinit_blade_dll();
 
289
        return status;
 
290
    }
 
291
 
 
292
    /* Copy and initialize option with default settings */
 
293
    if (param_option) {
 
294
        pj_memcpy(&fport->mp3_option, param_option, 
 
295
                   sizeof(pjmedia_mp3_encoder_option));
 
296
    } else {
 
297
        pj_bzero(&fport->mp3_option, sizeof(pjmedia_mp3_encoder_option));
 
298
        fport->mp3_option.vbr = PJ_TRUE;
 
299
    }
 
300
 
 
301
    /* Calculate bitrate if it's not specified, only if it's not VBR. */
 
302
    if (fport->mp3_option.bit_rate == 0 && !fport->mp3_option.vbr) 
 
303
        fport->mp3_option.bit_rate = sampling_rate * channel_count;
 
304
 
 
305
    /* Set default quality if it's not specified */
 
306
    if (fport->mp3_option.quality == 0) 
 
307
        fport->mp3_option.quality = 2;
 
308
 
 
309
    /* Init mp3 encoder */
 
310
    status = init_mp3_encoder(fport, pool);
 
311
    if (status != PJ_SUCCESS) {
 
312
        pj_file_close(fport->fd);
 
313
        deinit_blade_dll();
 
314
        return status;
 
315
    }
 
316
 
 
317
    /* Done. */
 
318
    *p_port = &fport->base;
 
319
 
 
320
    PJ_LOG(4,(THIS_FILE, 
 
321
              "MP3 file writer '%.*s' created: samp.rate=%dKHz, "
 
322
              "bitrate=%dkbps%s, quality=%d",
 
323
              (int)fport->base.info.name.slen,
 
324
              fport->base.info.name.ptr,
 
325
                  PJMEDIA_PIA_SRATE(&fport->base.info),
 
326
              fport->mp3_option.bit_rate/1000,
 
327
              (fport->mp3_option.vbr ? " (VBR)" : ""),
 
328
              fport->mp3_option.quality));
 
329
 
 
330
    return PJ_SUCCESS;
 
331
}
 
332
 
 
333
 
 
334
 
 
335
/*
 
336
 * Register callback.
 
337
 */
 
338
PJ_DEF(pj_status_t) 
 
339
pjmedia_mp3_writer_port_set_cb( pjmedia_port *port,
 
340
                                pj_size_t pos,
 
341
                                void *user_data,
 
342
                                pj_status_t (*cb)(pjmedia_port *port,
 
343
                                                  void *usr_data))
 
344
{
 
345
    struct mp3_file_port *fport;
 
346
 
 
347
    /* Sanity check */
 
348
    PJ_ASSERT_RETURN(port && cb, PJ_EINVAL);
 
349
 
 
350
    /* Check that this is really a writer port */
 
351
    PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
 
352
 
 
353
    fport = (struct mp3_file_port*) port;
 
354
 
 
355
    fport->cb_size = pos;
 
356
    fport->base.port_data.pdata = user_data;
 
357
    fport->cb = cb;
 
358
 
 
359
    return PJ_SUCCESS;
 
360
 
 
361
}
 
362
 
 
363
 
 
364
/*
 
365
 * Put a frame into the buffer. When the buffer is full, flush the buffer
 
366
 * to the file.
 
367
 */
 
368
static pj_status_t file_put_frame(pjmedia_port *this_port, 
 
369
                                  const pjmedia_frame *frame)
 
370
{
 
371
    struct mp3_file_port *fport = (struct mp3_file_port *)this_port;
 
372
    unsigned long MP3Err;
 
373
    pj_ssize_t  bytes;
 
374
    pj_status_t status;
 
375
    unsigned long WriteSize;
 
376
 
 
377
    /* Record silence if input is no-frame */
 
378
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE || frame->size == 0) {
 
379
        unsigned samples_left = PJMEDIA_PIA_SPF(&fport->base.info);
 
380
        unsigned samples_copied = 0;
 
381
 
 
382
        /* Only want to record at most 1 second of silence */
 
383
        if (fport->silence_duration >= PJMEDIA_PIA_SRATE(&fport->base.info))
 
384
            return PJ_SUCCESS;
 
385
 
 
386
        while (samples_left) {
 
387
            unsigned samples_needed = fport->mp3_samples_per_frame -
 
388
                                      fport->mp3_sample_pos;
 
389
            if (samples_needed > samples_left)
 
390
                samples_needed = samples_left;
 
391
 
 
392
            pjmedia_zero_samples(fport->mp3_sample_buf + fport->mp3_sample_pos,
 
393
                                 samples_needed);
 
394
            fport->mp3_sample_pos += samples_needed;
 
395
            samples_left -= samples_needed;
 
396
            samples_copied += samples_needed;
 
397
 
 
398
            /* Encode if we have full frame */
 
399
            if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) {
 
400
                
 
401
                /* Clear position */
 
402
                fport->mp3_sample_pos = 0;
 
403
 
 
404
                /* Encode ! */
 
405
                MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
 
406
                                                fport->mp3_samples_per_frame,
 
407
                                                fport->mp3_sample_buf, 
 
408
                                                fport->mp3_buf, 
 
409
                                                &WriteSize);
 
410
                if (MP3Err != BE_ERR_SUCCESSFUL)
 
411
                    return PJMEDIA_ERROR;
 
412
 
 
413
                /* Write the chunk */
 
414
                bytes = WriteSize;
 
415
                status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
 
416
                if (status != PJ_SUCCESS)
 
417
                    return status;
 
418
 
 
419
                /* Increment total written. */
 
420
                fport->total += bytes;
 
421
            }
 
422
        }
 
423
 
 
424
        fport->silence_duration += PJMEDIA_PIA_SPF(&fport->base.info);
 
425
 
 
426
    }
 
427
    /* If encoder is expecting different sample size, then we need to
 
428
     * buffer the samples.
 
429
     */
 
430
    else if (fport->mp3_samples_per_frame != 
 
431
             PJMEDIA_PIA_SPF(&fport->base.info)) 
 
432
    {
 
433
        unsigned samples_left = frame->size / 2;
 
434
        unsigned samples_copied = 0;
 
435
        const pj_int16_t *src_samples = frame->buf;
 
436
 
 
437
        fport->silence_duration = 0;
 
438
 
 
439
        while (samples_left) {
 
440
            unsigned samples_needed = fport->mp3_samples_per_frame -
 
441
                                      fport->mp3_sample_pos;
 
442
            if (samples_needed > samples_left)
 
443
                samples_needed = samples_left;
 
444
 
 
445
            pjmedia_copy_samples(fport->mp3_sample_buf + fport->mp3_sample_pos,
 
446
                                 src_samples + samples_copied,
 
447
                                 samples_needed);
 
448
            fport->mp3_sample_pos += samples_needed;
 
449
            samples_left -= samples_needed;
 
450
            samples_copied += samples_needed;
 
451
 
 
452
            /* Encode if we have full frame */
 
453
            if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) {
 
454
                
 
455
                /* Clear position */
 
456
                fport->mp3_sample_pos = 0;
 
457
 
 
458
                /* Encode ! */
 
459
                MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
 
460
                                                fport->mp3_samples_per_frame,
 
461
                                                fport->mp3_sample_buf, 
 
462
                                                fport->mp3_buf, 
 
463
                                                &WriteSize);
 
464
                if (MP3Err != BE_ERR_SUCCESSFUL)
 
465
                    return PJMEDIA_ERROR;
 
466
 
 
467
                /* Write the chunk */
 
468
                bytes = WriteSize;
 
469
                status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
 
470
                if (status != PJ_SUCCESS)
 
471
                    return status;
 
472
 
 
473
                /* Increment total written. */
 
474
                fport->total += bytes;
 
475
            }
 
476
        }
 
477
 
 
478
    } else {
 
479
 
 
480
        fport->silence_duration = 0;
 
481
 
 
482
        /* Encode ! */
 
483
        MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
 
484
                                        fport->mp3_samples_per_frame,
 
485
                                        frame->buf, 
 
486
                                        fport->mp3_buf, 
 
487
                                        &WriteSize);
 
488
        if (MP3Err != BE_ERR_SUCCESSFUL)
 
489
            return PJMEDIA_ERROR;
 
490
 
 
491
        /* Write the chunk */
 
492
        bytes = WriteSize;
 
493
        status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
 
494
        if (status != PJ_SUCCESS)
 
495
            return status;
 
496
 
 
497
        /* Increment total written. */
 
498
        fport->total += bytes;
 
499
    }
 
500
 
 
501
    /* Increment total written, and check if we need to call callback */
 
502
    
 
503
    if (fport->cb && fport->total >= fport->cb_size) {
 
504
        pj_status_t (*cb)(pjmedia_port*, void*);
 
505
        pj_status_t status;
 
506
 
 
507
        cb = fport->cb;
 
508
        fport->cb = NULL;
 
509
 
 
510
        status = (*cb)(this_port, this_port->port_data.pdata);
 
511
        return status;
 
512
    }
 
513
 
 
514
    return PJ_SUCCESS;
 
515
}
 
516
 
 
517
/*
 
518
 * Get frame, basicy is a no-op operation.
 
519
 */
 
520
static pj_status_t file_get_frame(pjmedia_port *this_port, 
 
521
                                  pjmedia_frame *frame)
 
522
{
 
523
    PJ_UNUSED_ARG(this_port);
 
524
    PJ_UNUSED_ARG(frame);
 
525
    return PJ_EINVALIDOP;
 
526
}
 
527
 
 
528
 
 
529
/*
 
530
 * Close the port, modify file header with updated file length.
 
531
 */
 
532
static pj_status_t file_on_destroy(pjmedia_port *this_port)
 
533
{
 
534
    struct mp3_file_port *fport = (struct mp3_file_port*)this_port;
 
535
    pj_status_t status;
 
536
    unsigned long WriteSize;
 
537
    unsigned long MP3Err;
 
538
 
 
539
 
 
540
    /* Close encoder */
 
541
    MP3Err = BladeDLL.beDeinitStream(fport->mp3_stream, fport->mp3_buf, 
 
542
                                     &WriteSize);
 
543
    if (MP3Err == BE_ERR_SUCCESSFUL) {
 
544
        pj_ssize_t bytes = WriteSize;
 
545
        status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
 
546
    }
 
547
 
 
548
    /* Close file */
 
549
    status = pj_file_close(fport->fd);
 
550
 
 
551
    /* Write additional VBR header */
 
552
    if (fport->mp3_option.vbr) {
 
553
        MP3Err = BladeDLL.beWriteVBRHeader(fport->mp3_filename.ptr);
 
554
    }
 
555
 
 
556
 
 
557
    /* Decrement DLL reference counter */
 
558
    deinit_blade_dll();
 
559
 
 
560
    /* Done. */
 
561
    return PJ_SUCCESS;
 
562
}
 
563