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

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: mp3_writer.c 1233 2007-04-30 11:05:23Z bennylp $ */
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_PORT_SIGNATURE('F', 'W', 'M', '3')
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 = fport->base.info.clock_rate;
179
 
    LConfig.format.LHV1.dwReSampleRate = 0;
180
 
 
181
 
    if (fport->base.info.channel_count==1)
182
 
        LConfig.format.LHV1.nMode = BE_MP3_MODE_MONO;
183
 
    else if (fport->base.info.channel_count==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
 
              fport->base.info.clock_rate/1000,
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 = fport->base.info.samples_per_frame;
380
 
        unsigned samples_copied = 0;
381
 
 
382
 
        /* Only want to record at most 1 second of silence */
383
 
        if (fport->silence_duration >= fport->base.info.clock_rate)
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 += fport->base.info.samples_per_frame;
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
 
             fport->base.info.samples_per_frame)
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
 
}