blob: b0b6fa9c336185536cf8759e0335703509b1bd70 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia/mem_port.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/pool.h>
24
25
26#define THIS_FILE "mem_player.c"
27
28#define SIGNATURE PJMEDIA_SIG_PORT_MEM_PLAYER
29#define BYTES_PER_SAMPLE 2
30
31struct mem_player
32{
33 pjmedia_port base;
34
35 unsigned options;
36 pj_timestamp timestamp;
37
38 char *buffer;
39 pj_size_t buf_size;
40 char *read_pos;
41
42 pj_bool_t eof;
43 void *user_data;
44 pj_status_t (*cb)(pjmedia_port *port,
45 void *user_data);
46
47};
48
49
50static pj_status_t mem_put_frame(pjmedia_port *this_port,
51 pjmedia_frame *frame);
52static pj_status_t mem_get_frame(pjmedia_port *this_port,
53 pjmedia_frame *frame);
54static pj_status_t mem_on_destroy(pjmedia_port *this_port);
55
56
57PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool,
58 const void *buffer,
59 pj_size_t size,
60 unsigned clock_rate,
61 unsigned channel_count,
62 unsigned samples_per_frame,
63 unsigned bits_per_sample,
64 unsigned options,
65 pjmedia_port **p_port )
66{
67 struct mem_player *port;
68 pj_str_t name = pj_str("memplayer");
69
70 /* Sanity check */
71 PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count &&
72 samples_per_frame && bits_per_sample && p_port,
73 PJ_EINVAL);
74
75 /* Can only support 16bit PCM */
76 PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
77
78
79 port = PJ_POOL_ZALLOC_T(pool, struct mem_player);
80 PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM);
81
82 /* Create the port */
83 pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, clock_rate,
84 channel_count, bits_per_sample, samples_per_frame);
85
86 port->base.put_frame = &mem_put_frame;
87 port->base.get_frame = &mem_get_frame;
88 port->base.on_destroy = &mem_on_destroy;
89
90
91 /* Save the buffer */
92 port->buffer = port->read_pos = (char*)buffer;
93 port->buf_size = size;
94
95 /* Options */
96 port->options = options;
97
98 *p_port = &port->base;
99
100 return PJ_SUCCESS;
101}
102
103
104
105/*
106 * Register a callback to be called when the file reading has reached the
107 * end of buffer.
108 */
109PJ_DEF(pj_status_t) pjmedia_mem_player_set_eof_cb( pjmedia_port *port,
110 void *user_data,
111 pj_status_t (*cb)(pjmedia_port *port,
112 void *usr_data))
113{
114 struct mem_player *player;
115
116 PJ_ASSERT_RETURN(port->info.signature == SIGNATURE,
117 PJ_EINVALIDOP);
118
119 player = (struct mem_player*) port;
120 player->user_data = user_data;
121 player->cb = cb;
122
123 return PJ_SUCCESS;
124}
125
126
127static pj_status_t mem_put_frame( pjmedia_port *this_port,
128 pjmedia_frame *frame)
129{
130 PJ_UNUSED_ARG(this_port);
131 PJ_UNUSED_ARG(frame);
132
133 return PJ_SUCCESS;
134}
135
136
137static pj_status_t mem_get_frame( pjmedia_port *this_port,
138 pjmedia_frame *frame)
139{
140 struct mem_player *player;
141 char *endpos;
142 pj_size_t size_needed, size_written;
143
144 PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
145 PJ_EINVALIDOP);
146
147 player = (struct mem_player*) this_port;
148
149 if (player->eof) {
150 pj_status_t status = PJ_SUCCESS;
151
152 /* Call callback, if any */
153 if (player->cb)
154 status = (*player->cb)(this_port, player->user_data);
155
156 /* If callback returns non PJ_SUCCESS or 'no loop' is specified
157 * return immediately (and don't try to access player port since
158 * it might have been destroyed by the callback).
159 */
160 if ((status != PJ_SUCCESS) || (player->options & PJMEDIA_MEM_NO_LOOP)) {
161 frame->type = PJMEDIA_FRAME_TYPE_NONE;
162 return PJ_EEOF;
163 }
164
165 player->eof = PJ_FALSE;
166 }
167
168 size_needed = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
169 size_written = 0;
170 endpos = player->buffer + player->buf_size;
171
172 while (size_written < size_needed) {
173 char *dst = ((char*)frame->buf) + size_written;
174 pj_size_t max;
175
176 max = size_needed - size_written;
177 if (endpos - player->read_pos < (int)max)
178 max = endpos - player->read_pos;
179
180 pj_memcpy(dst, player->read_pos, max);
181 size_written += max;
182 player->read_pos += max;
183
184 pj_assert(player->read_pos <= endpos);
185
186 if (player->read_pos == endpos) {
187 /* Set EOF flag */
188 player->eof = PJ_TRUE;
189 /* Reset read pointer */
190 player->read_pos = player->buffer;
191
192 /* Pad with zeroes then return for no looped play */
193 if (player->options & PJMEDIA_MEM_NO_LOOP) {
194 pj_size_t null_len;
195
196 null_len = size_needed - size_written;
197 pj_bzero(dst + max, null_len);
198 break;
199 }
200 }
201 }
202
203 frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
204 frame->timestamp.u64 = player->timestamp.u64;
205 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
206
207 player->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
208
209 return PJ_SUCCESS;
210}
211
212
213static pj_status_t mem_on_destroy(pjmedia_port *this_port)
214{
215 PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE,
216 PJ_EINVALIDOP);
217
218 /* Destroy signature */
219 this_port->info.signature = 0;
220
221 return PJ_SUCCESS;
222}
223
224