ffaudio-core.cc
1 |
/*
|
---|---|
2 |
* Audacious FFaudio Plugin
|
3 |
* Copyright © 2009 William Pitcock <nenolod@dereferenced.org>
|
4 |
* Matti Hämäläinen <ccr@tnsp.org>
|
5 |
* Copyright © 2011 John Lindgren <john.lindgren@tds.net>
|
6 |
* Video-playing capability added Copyright © 2015 Jim Turner <turnerjw784@yahoo.com>
|
7 |
*
|
8 |
* Redistribution and use in source and binary forms, with or without
|
9 |
* modification, are permitted provided that the following conditions are met:
|
10 |
*
|
11 |
* 1. Redistributions of source code must retain the above copyright notice,
|
12 |
* this list of conditions, and the following disclaimer.
|
13 |
*
|
14 |
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
15 |
* this list of conditions, and the following disclaimer in the documentation
|
16 |
* provided with the distribution.
|
17 |
*
|
18 |
* This software is provided "as is" and without any warranty, express or
|
19 |
* implied. In no event shall the authors be liable for any damages arising from
|
20 |
*/
|
21 |
|
22 |
#include <stdlib.h> |
23 |
#include <stdio.h> |
24 |
#include <string.h> |
25 |
|
26 |
#include <pthread.h> |
27 |
|
28 |
#undef FFAUDIO_DOUBLECHECK /* Doublecheck probing result for debugging purposes */ |
29 |
#undef FFAUDIO_NO_BLACKLIST /* Don't blacklist any recognized codecs/formats */ |
30 |
|
31 |
#include "ffaudio-stdinc.h" |
32 |
|
33 |
#include <audacious/audtag.h> |
34 |
#include <libaudcore/audstrings.h> |
35 |
#include <libaudcore/i18n.h> |
36 |
#include <libaudcore/multihash.h> |
37 |
#include <libaudcore/runtime.h> |
38 |
|
39 |
#include <stdint.h> |
40 |
#include "libavutil/common.h" |
41 |
#include "libavutil/dict.h" |
42 |
#include "libavutil/log.h" |
43 |
#include "libavformat/version.h" |
44 |
#include "libavformat/avio.h" |
45 |
extern "C" { |
46 |
#include <SDL_syswm.h> |
47 |
#include <libswscale/swscale.h> |
48 |
} |
49 |
#include <X11/Xlib.h> |
50 |
#include <SDL.h> |
51 |
#include <SDL_thread.h> |
52 |
|
53 |
#define SDL_AUDIO_BUFFER_SIZE 4096 |
54 |
#define MAX_AUDIO_FRAME_SIZE 192000 |
55 |
|
56 |
|
57 |
class FFaudio : public InputPlugin |
58 |
{ |
59 |
public:
|
60 |
static const char about[]; |
61 |
static const char * const exts[], * const mimes[]; |
62 |
|
63 |
static constexpr PluginInfo info = { |
64 |
N_("FFmpeg Plugin"),
|
65 |
PACKAGE, |
66 |
about |
67 |
}; |
68 |
|
69 |
static constexpr auto iinfo = InputInfo (FlagWritesTag) |
70 |
.with_priority (10) /* lowest priority fallback */ |
71 |
.with_exts (exts) |
72 |
.with_mimes (mimes); |
73 |
|
74 |
constexpr FFaudio () : InputPlugin (info, iinfo) {}
|
75 |
|
76 |
bool init ();
|
77 |
void cleanup ();
|
78 |
|
79 |
bool is_our_file (const char * filename, VFSFile & file); |
80 |
Tuple read_tuple (const char * filename, VFSFile & file); |
81 |
Index<char> read_image (const char * filename, VFSFile & file); |
82 |
bool write_tuple (const char * filename, VFSFile & file, const Tuple & tuple); |
83 |
bool play (const char * filename, VFSFile & file); |
84 |
}; |
85 |
|
86 |
EXPORT FFaudio aud_plugin_instance; |
87 |
|
88 |
static bool play_video; /* JWT: TRUE IF USER IS CURRENTLY PLAYING VIDEO (KILLING VID. WINDOW TURNS OFF! */ |
89 |
|
90 |
typedef struct |
91 |
{ |
92 |
int stream_idx;
|
93 |
AVStream * stream; |
94 |
AVCodecContext * context; |
95 |
AVCodec * codec; |
96 |
} |
97 |
CodecInfo; |
98 |
|
99 |
/*
|
100 |
JWT: ADDED ALL THIS QUEUE STUFF TO SMOOTH VIDEO PERFORMANCE SO THAT VIDEO FRAMES WOULD
|
101 |
BE OUTPUT MORE INTERLACED WITH THE AUDIO FRAMES BY QUEUEING VIDEO FRAMES UNTIL AN
|
102 |
AUDIO FRAME IS PROCESSED, THEN DEQUEUEING AND PROCESSING 'EM WITH EACH AUDIO FRAME.
|
103 |
THE SIZE OF THIS QUEUE IS SET BY video_qsize CONFIG PARAMETER AND DEFAULTS TO 16.
|
104 |
HAVING TOO MANY CAN RESULT IN DELAYED VIDEO, SO EXPERIMENT. IDEALLY, PACKETS SHOULD
|
105 |
BE PROCESSED: V A V A V A..., BUT THIS HANDLES:
|
106 |
V1 V2 V3 V4 V5 A1 A2 A3 A4 A5 A6 A7 V7 A8... AS:
|
107 |
(q:V1 V2 V3 V4 V5 V6) A1 A2 dq:V1 A3 A4 dq:V2 A5 A6 dq:V3 A7 A8...
|
108 |
WE DON'T WANT TO INTERRUPT AUDIO PERFORMANCE AND I DON'T KNOW HOW TO THREAD IT UP,
|
109 |
BUT THIS SIMPLE APPROACH SEEMS TO WORK PRETTY SMOOTH FOR ME! OTHERWISE TRY
|
110 |
INCREASING video_qsize IN config file OTHERWISE.
|
111 |
BORROWED THESE FUNCTIONS FROM:
|
112 |
http://www.thelearningpoint.net/computer-science/data-structures-queues--with-c-program-source-code
|
113 |
*/
|
114 |
|
115 |
typedef struct |
116 |
{ |
117 |
int capacity;
|
118 |
int size;
|
119 |
int front;
|
120 |
int rear;
|
121 |
AVPacket *elements; |
122 |
} |
123 |
pktQueue; |
124 |
|
125 |
pktQueue * createQueue (int maxElements)
|
126 |
{ |
127 |
/* Create a Queue */
|
128 |
pktQueue *Q; |
129 |
Q = (pktQueue *)malloc(sizeof(pktQueue));
|
130 |
/* Initialise its properties */
|
131 |
Q->elements = (AVPacket *)malloc(sizeof(AVPacket)*maxElements);
|
132 |
Q->size = 0;
|
133 |
Q->capacity = maxElements; |
134 |
Q->front = 0;
|
135 |
Q->rear = -1;
|
136 |
/* Return the pointer */
|
137 |
return Q;
|
138 |
} |
139 |
|
140 |
bool Dequeue (pktQueue *Q)
|
141 |
{ |
142 |
/* If Queue size is zero then it is empty. So we cannot pop */
|
143 |
if(Q->size==0) |
144 |
return false; |
145 |
/* Removing an element is equivalent to incrementing index of front by one */
|
146 |
else
|
147 |
{ |
148 |
Q->size--; |
149 |
if (Q->elements[Q->front].data)
|
150 |
av_free_packet(&Q->elements[Q->front]); |
151 |
|
152 |
Q->front++; |
153 |
/* As we fill elements in circular fashion */
|
154 |
if(Q->front==Q->capacity)
|
155 |
Q->front=0;
|
156 |
} |
157 |
return true; |
158 |
} |
159 |
|
160 |
/* JWT:FLUSH AND FREE EVERYTHING IN THE QUEUE */
|
161 |
void QFlush (pktQueue *Q)
|
162 |
{ |
163 |
while (Q->size > 0) |
164 |
{ |
165 |
Q->size--; |
166 |
if (Q->elements[Q->front].data)
|
167 |
av_free_packet(&Q->elements[Q->front]); |
168 |
|
169 |
Q->front++; |
170 |
/* As we fill elements in circular fashion */
|
171 |
if(Q->front==Q->capacity)
|
172 |
Q->front=0;
|
173 |
} |
174 |
|
175 |
} |
176 |
|
177 |
AVPacket * QFront (pktQueue *Q) |
178 |
{ |
179 |
if(Q->size==0) |
180 |
{ |
181 |
AUDDBG("Queue is Empty\n");
|
182 |
return nullptr; |
183 |
} |
184 |
/* Return the element which is at the front*/
|
185 |
return &Q->elements[Q->front];
|
186 |
} |
187 |
|
188 |
bool isQueueFull (pktQueue *Q)
|
189 |
{ |
190 |
return (Q->size == Q->capacity) ? true : false; |
191 |
} |
192 |
|
193 |
bool Enqueue (pktQueue *Q, AVPacket element)
|
194 |
{ |
195 |
/* If the Queue is full, we cannot push an element into it as there is no space for it.*/
|
196 |
if(Q->size == Q->capacity)
|
197 |
{ |
198 |
printf("Queue is Full\n");
|
199 |
return false; |
200 |
} |
201 |
else
|
202 |
{ |
203 |
Q->size++; |
204 |
Q->rear = Q->rear + 1;
|
205 |
/* As we fill the queue in circular fashion */
|
206 |
if(Q->rear == Q->capacity)
|
207 |
{ |
208 |
Q->rear = 0;
|
209 |
} |
210 |
/* Insert the element in its rear side */
|
211 |
Q->elements[Q->rear] = element; |
212 |
} |
213 |
return true; |
214 |
} |
215 |
|
216 |
/* JWT:END OF ADDED VIDEO PACKET QUEUEING FUNCTIONS */
|
217 |
|
218 |
static SimpleHash<String, AVInputFormat *> extension_dict;
|
219 |
|
220 |
static void create_extension_dict (); |
221 |
|
222 |
static int lockmgr (void * * mutexp, enum AVLockOp op) |
223 |
{ |
224 |
switch (op)
|
225 |
{ |
226 |
case AV_LOCK_CREATE:
|
227 |
* mutexp = new pthread_mutex_t;
|
228 |
pthread_mutex_init ((pthread_mutex_t *) * mutexp, nullptr);
|
229 |
break;
|
230 |
case AV_LOCK_OBTAIN:
|
231 |
pthread_mutex_lock ((pthread_mutex_t *) * mutexp); |
232 |
break;
|
233 |
case AV_LOCK_RELEASE:
|
234 |
pthread_mutex_unlock ((pthread_mutex_t *) * mutexp); |
235 |
break;
|
236 |
case AV_LOCK_DESTROY:
|
237 |
pthread_mutex_destroy ((pthread_mutex_t *) * mutexp); |
238 |
delete (pthread_mutex_t *) * mutexp;
|
239 |
break;
|
240 |
} |
241 |
|
242 |
return 0; |
243 |
} |
244 |
|
245 |
static void ffaudio_log_cb (void * avcl, int av_level, const char * fmt, va_list va) |
246 |
{ |
247 |
audlog::Level level = audlog::Debug; |
248 |
char message [2048]; |
249 |
|
250 |
switch (av_level)
|
251 |
{ |
252 |
case AV_LOG_QUIET:
|
253 |
return;
|
254 |
case AV_LOG_PANIC:
|
255 |
case AV_LOG_FATAL:
|
256 |
case AV_LOG_ERROR:
|
257 |
level = audlog::Error; |
258 |
break;
|
259 |
case AV_LOG_WARNING:
|
260 |
level = audlog::Warning; |
261 |
break;
|
262 |
case AV_LOG_INFO:
|
263 |
level = audlog::Info; |
264 |
break;
|
265 |
default:
|
266 |
break;
|
267 |
} |
268 |
|
269 |
AVClass * avc = avcl ? * (AVClass * *) avcl : nullptr;
|
270 |
|
271 |
vsnprintf (message, sizeof message, fmt, va);
|
272 |
|
273 |
audlog::log (level, __FILE__, __LINE__, avc ? avc->item_name(avcl) : __FUNCTION__, |
274 |
"<%p> %s", avcl, message);
|
275 |
} |
276 |
|
277 |
bool FFaudio::init ()
|
278 |
{ |
279 |
av_register_all(); |
280 |
av_lockmgr_register (lockmgr); |
281 |
|
282 |
create_extension_dict (); |
283 |
|
284 |
av_log_set_callback (ffaudio_log_cb); |
285 |
|
286 |
return true; |
287 |
} |
288 |
|
289 |
void FFaudio::cleanup ()
|
290 |
{ |
291 |
extension_dict.clear (); |
292 |
|
293 |
av_lockmgr_register (nullptr);
|
294 |
} |
295 |
|
296 |
static const char * ffaudio_strerror (int error) |
297 |
{ |
298 |
static char buf[256]; |
299 |
return (! av_strerror (error, buf, sizeof buf)) ? buf : "unknown error"; |
300 |
} |
301 |
|
302 |
static void create_extension_dict () |
303 |
{ |
304 |
AVInputFormat * f; |
305 |
for (f = av_iformat_next (nullptr); f; f = av_iformat_next (f)) |
306 |
{ |
307 |
if (! f->extensions)
|
308 |
continue;
|
309 |
|
310 |
StringBuf exts = str_tolower (f->extensions); |
311 |
Index<String> extlist = str_list_to_index (exts, ",");
|
312 |
|
313 |
for (auto & ext : extlist) |
314 |
extension_dict.add (ext, std::move (f)); |
315 |
} |
316 |
} |
317 |
|
318 |
static AVInputFormat * get_format_by_extension (const char * name) |
319 |
{ |
320 |
StringBuf ext = uri_get_extension (name); |
321 |
if (! ext)
|
322 |
return nullptr; |
323 |
|
324 |
AUDDBG ("Get format by extension: %s\n", name);
|
325 |
AVInputFormat * * f = extension_dict.lookup (String (str_tolower (ext))); |
326 |
|
327 |
if (f && * f)
|
328 |
AUDDBG ("Format %s.\n", (* f)->name);
|
329 |
else
|
330 |
AUDDBG ("Format unknown.\n");
|
331 |
|
332 |
return f ? * f : nullptr; |
333 |
} |
334 |
|
335 |
static AVInputFormat * get_format_by_content (const char * name, VFSFile & file) |
336 |
{ |
337 |
AUDDBG ("Get format by content: %s\n", name);
|
338 |
|
339 |
AVInputFormat * f = nullptr;
|
340 |
|
341 |
unsigned char buf[16384 + AVPROBE_PADDING_SIZE]; |
342 |
int size = 16; |
343 |
int filled = 0; |
344 |
int target = 100; |
345 |
int score = 0; |
346 |
|
347 |
while (1) |
348 |
{ |
349 |
if (filled < size)
|
350 |
filled += file.fread (buf + filled, 1, size - filled);
|
351 |
|
352 |
memset (buf + filled, 0, AVPROBE_PADDING_SIZE);
|
353 |
AVProbeData d = {name, buf, filled}; |
354 |
score = target; |
355 |
|
356 |
f = av_probe_input_format2 (& d, true, & score);
|
357 |
if (f)
|
358 |
break;
|
359 |
|
360 |
if (size < 16384 && filled == size) |
361 |
size *= 4;
|
362 |
else if (target > 10) |
363 |
target = 10;
|
364 |
else
|
365 |
break;
|
366 |
} |
367 |
|
368 |
if (f)
|
369 |
AUDDBG ("Format %s, buffer size %d, score %d.\n", f->name, filled, score);
|
370 |
else
|
371 |
AUDDBG ("Format unknown.\n");
|
372 |
|
373 |
if (file.fseek (0, VFS_SEEK_SET) < 0) |
374 |
; /* ignore errors here */
|
375 |
|
376 |
return f;
|
377 |
} |
378 |
|
379 |
static AVInputFormat * get_format (const char * name, VFSFile & file) |
380 |
{ |
381 |
AVInputFormat * f = get_format_by_extension (name); |
382 |
return f ? f : get_format_by_content (name, file);
|
383 |
} |
384 |
|
385 |
static AVFormatContext * open_input_file (const char * name, VFSFile & file) |
386 |
{ |
387 |
int ret;
|
388 |
AVFormatContext * c; |
389 |
|
390 |
play_video = aud_get_bool ("ffaudio", "play_video"); /* JWT:RESET PLAY-VIDEO, CASE TURNED OFF ON PREV. PLAY. */ |
391 |
if (!strcmp(name, "-") || strstr(name, "://-.")) |
392 |
{ |
393 |
AUDDBG("-open_input_file(stdin)\n");
|
394 |
c = NULL;
|
395 |
const char * xname = "pipe:"; |
396 |
ret = avformat_open_input (&c, xname, NULL, NULL); |
397 |
if (ret < 0) |
398 |
{ |
399 |
AUDERR ("avformat_open_input failed for %s: %s.\n", xname, ffaudio_strerror (ret));
|
400 |
return nullptr; |
401 |
} |
402 |
} |
403 |
else
|
404 |
{ |
405 |
AUDDBG("-open_input_file(%s)\n", name);
|
406 |
AVInputFormat * f = get_format (name, file); |
407 |
|
408 |
if (! f)
|
409 |
{ |
410 |
AUDERR ("Unknown format for %s.\n", name);
|
411 |
return nullptr; |
412 |
} |
413 |
|
414 |
AVFormatContext * c = avformat_alloc_context (); |
415 |
AVIOContext * io = io_context_new (file); |
416 |
c->pb = io; |
417 |
|
418 |
ret = avformat_open_input (&c, name, f, nullptr);
|
419 |
if (ret < 0) |
420 |
{ |
421 |
AUDERR ("avformat_open_input failed for %s: %s.\n", name, ffaudio_strerror (ret));
|
422 |
io_context_free (io); |
423 |
return nullptr; |
424 |
} |
425 |
} |
426 |
|
427 |
AUDDBG("-open_input_file - success!\n");
|
428 |
return c;
|
429 |
} |
430 |
|
431 |
static void close_input_file (AVFormatContext * c, bool fromstdin) |
432 |
{ |
433 |
AVIOContext * io = NULL;
|
434 |
|
435 |
if (!fromstdin)
|
436 |
io = c->pb; |
437 |
|
438 |
#if CHECK_LIBAVFORMAT_VERSION (53, 25, 0, 53, 17, 0) |
439 |
avformat_close_input (&c); |
440 |
#else
|
441 |
av_close_input_file (c); |
442 |
#endif
|
443 |
|
444 |
if (!fromstdin)
|
445 |
io_context_free (io); |
446 |
} |
447 |
|
448 |
static bool find_codec (AVFormatContext * c, CodecInfo * cinfo, CodecInfo * vcinfo) |
449 |
{ |
450 |
int videoStream;
|
451 |
int audioStream;
|
452 |
avformat_find_stream_info (c, NULL);
|
453 |
|
454 |
videoStream=-1;
|
455 |
audioStream=-1;
|
456 |
for (unsigned i = 0; i < c->nb_streams; i++) |
457 |
{ |
458 |
if (c->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
|
459 |
videoStream=i; |
460 |
else if (c->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audioStream < 0) |
461 |
audioStream=i; |
462 |
} |
463 |
if (audioStream==-1) /* PUNT IF NO AUDIO SINCE AUDACIOUS IS AN *AUDIO* PLAYER! */ |
464 |
return false; |
465 |
|
466 |
AVCodec * codec = avcodec_find_decoder (c->streams[audioStream]->codec->codec_id); |
467 |
if (codec)
|
468 |
{ |
469 |
cinfo->stream_idx = audioStream; |
470 |
cinfo->stream = c->streams[audioStream]; |
471 |
cinfo->context = c->streams[audioStream]->codec; |
472 |
cinfo->codec = codec; |
473 |
|
474 |
/* JWT: NOW IF USER WANTS VIDEO, SEE IF WE GOT A VIDEO STREAM TOO: */
|
475 |
if (play_video && videoStream >= 0) |
476 |
{ |
477 |
AVCodec * vcodec = avcodec_find_decoder (c->streams[videoStream]->codec->codec_id); |
478 |
if (vcodec)
|
479 |
{ |
480 |
vcinfo->stream_idx = videoStream; |
481 |
vcinfo->stream = c->streams[videoStream]; |
482 |
vcinfo->context = c->streams[videoStream]->codec; |
483 |
vcinfo->codec = vcodec; |
484 |
} |
485 |
} |
486 |
else
|
487 |
play_video = false; /* turn off video playback, since we could not find stream! */ |
488 |
|
489 |
return true; |
490 |
} |
491 |
|
492 |
return false; |
493 |
} |
494 |
|
495 |
bool FFaudio::is_our_file (const char * filename, VFSFile & file) |
496 |
{ |
497 |
return (bool) get_format (filename, file); |
498 |
} |
499 |
|
500 |
static const struct { |
501 |
Tuple::ValueType ttype; /* Tuple field value type */
|
502 |
Tuple::Field field; /* Tuple field constant */
|
503 |
const char * keys[5]; /* Keys to match (case-insensitive), ended by nullptr */ |
504 |
} metaentries[] = { |
505 |
{Tuple::String, Tuple::Artist, {"author", "hor", "artist", nullptr}}, |
506 |
{Tuple::String, Tuple::Title, {"title", "le", nullptr}}, |
507 |
{Tuple::String, Tuple::Album, {"album", "WM/AlbumTitle", nullptr}}, |
508 |
{Tuple::String, Tuple::Performer, {"performer", nullptr}}, |
509 |
{Tuple::String, Tuple::Copyright, {"copyright", nullptr}}, |
510 |
{Tuple::String, Tuple::Genre, {"genre", "WM/Genre", nullptr}}, |
511 |
{Tuple::String, Tuple::Comment, {"comment", nullptr}}, |
512 |
{Tuple::String, Tuple::Composer, {"composer", nullptr}}, |
513 |
{Tuple::Int, Tuple::Year, {"year", "WM/Year", "date", nullptr}}, |
514 |
{Tuple::Int, Tuple::Track, {"track", "WM/TrackNumber", nullptr}}, |
515 |
}; |
516 |
|
517 |
static void read_metadata_dict (Tuple & tuple, AVDictionary * dict) |
518 |
{ |
519 |
for (auto & meta : metaentries) |
520 |
{ |
521 |
AVDictionaryEntry * entry = nullptr;
|
522 |
|
523 |
for (int j = 0; ! entry && meta.keys[j]; j ++) |
524 |
entry = av_dict_get (dict, meta.keys[j], nullptr, 0); |
525 |
|
526 |
if (entry && entry->value)
|
527 |
{ |
528 |
if (meta.ttype == Tuple::String)
|
529 |
tuple.set_str (meta.field, entry->value); |
530 |
else if (meta.ttype == Tuple::Int) |
531 |
tuple.set_int (meta.field, atoi (entry->value)); |
532 |
} |
533 |
} |
534 |
} |
535 |
|
536 |
Tuple FFaudio::read_tuple (const char * filename, VFSFile & file) |
537 |
{ |
538 |
Tuple tuple; |
539 |
bool fromstdin;
|
540 |
|
541 |
fromstdin = (!strcmp(filename, "-") || strstr(filename, "://-.")) ? true : false; |
542 |
AUDDBG("Filename =%s=\n", filename);
|
543 |
if (fromstdin)
|
544 |
tuple.set_filename (filename); |
545 |
else /* JWT:THIS STUFF DEFERRED UNTIL PLAY() FOR STDIN, BUT SEEMS TO HAVE TO BE HERE FOR DIRECT */ |
546 |
{ |
547 |
AVFormatContext * ic = open_input_file (filename, file); |
548 |
|
549 |
if (ic)
|
550 |
{ |
551 |
CodecInfo cinfo, vcinfo; |
552 |
|
553 |
if (find_codec (ic, & cinfo, & vcinfo))
|
554 |
{ |
555 |
tuple.set_filename (filename); |
556 |
|
557 |
tuple.set_int (Tuple::Length, ic->duration / 1000);
|
558 |
tuple.set_int (Tuple::Bitrate, ic->bit_rate / 1000);
|
559 |
if (cinfo.codec->long_name)
|
560 |
tuple.set_str (Tuple::Codec, cinfo.codec->long_name); |
561 |
if (ic->metadata)
|
562 |
read_metadata_dict (tuple, ic->metadata); |
563 |
if (cinfo.stream->metadata)
|
564 |
read_metadata_dict (tuple, cinfo.stream->metadata); |
565 |
if (play_video && vcinfo.stream->metadata)
|
566 |
read_metadata_dict (tuple, vcinfo.stream->metadata); |
567 |
} |
568 |
|
569 |
close_input_file (ic, fromstdin); |
570 |
} |
571 |
|
572 |
if (tuple && ! file.fseek (0, VFS_SEEK_SET)) |
573 |
audtag::tuple_read (tuple, file); |
574 |
/* JWT:BUILD NOTE: IF USING LATEST AUDACIOUS VERSIONS YOU MAY NEED TO REPLACE ABOVE LINE WITH:
|
575 |
audtag::read_tag (file, & tuple, nullptr);
|
576 |
*/
|
577 |
} |
578 |
|
579 |
return tuple;
|
580 |
} |
581 |
|
582 |
bool FFaudio::write_tuple (const char * filename, VFSFile & file, const Tuple & tuple) |
583 |
{ |
584 |
if (str_has_suffix_nocase (filename, ".ape")) |
585 |
return audtag::tuple_write (tuple, file, audtag::TagType::APE);
|
586 |
|
587 |
/* return audtag::tuple_write (tuple, file, audtag::TagType::None); */
|
588 |
/* JWT:FIXME: FOR SOME REASON ABOVE LINE WON'T COMPILE WITH SDL_syswm.h AND X11/Xlib.h INCLUDED?!?!
|
589 |
"error: expected unqualified-id before numeric constant return audtag::tuple_write (tuple, file, audtag::TagType::None);"
|
590 |
SO REPLACED WITH NEXT LINE:
|
591 |
*/
|
592 |
return audtag::tuple_write (tuple, file, audtag::TagType::ID3v2);
|
593 |
} |
594 |
|
595 |
Index<char> FFaudio::read_image (const char * filename, VFSFile & file) |
596 |
{ |
597 |
if (str_has_suffix_nocase (filename, ".m4a") || str_has_suffix_nocase (filename, ".mp4")) |
598 |
return read_itunes_cover (filename, file);
|
599 |
|
600 |
return Index<char> (); |
601 |
} |
602 |
|
603 |
/* JWT: NEW FUNCTION TO WRITE VIDEO FRAMES TO THE POPUP WINDOW: */
|
604 |
void write_videoframe (SwsContext * sws_ctx, CodecInfo * cinfo, CodecInfo * vcinfo,
|
605 |
AVFrame * vframe, SDL_Overlay * bmp, AVPacket *pkt, int video_width, int video_height) |
606 |
{ |
607 |
SDL_Rect rect; |
608 |
|
609 |
int len = 0; |
610 |
int frameFinished = 0; |
611 |
while (1) |
612 |
{ |
613 |
len = avcodec_decode_video2 (vcinfo->context, vframe, &frameFinished, pkt); |
614 |
/* Did we get a video frame? */
|
615 |
if (len < 0) |
616 |
{ |
617 |
AUDERR ("decode_video() failed, code %d\n", len);
|
618 |
return;
|
619 |
} |
620 |
else if (frameFinished) |
621 |
{ |
622 |
AVPicture pict = { { 0 } };
|
623 |
SDL_LockYUVOverlay(bmp); |
624 |
pict.data[0] = bmp->pixels[0]; |
625 |
pict.data[1] = bmp->pixels[2]; |
626 |
pict.data[2] = bmp->pixels[1]; |
627 |
pict.linesize[0] = bmp->pitches[0]; |
628 |
pict.linesize[1] = bmp->pitches[2]; |
629 |
pict.linesize[2] = bmp->pitches[1]; |
630 |
/* Convert the image into YUV format that SDL uses. */
|
631 |
sws_scale(sws_ctx, (uint8_t const * const *)vframe->data, |
632 |
vframe->linesize, 0, vcinfo->context->height,
|
633 |
pict.data, pict.linesize); |
634 |
SDL_UnlockYUVOverlay(bmp); |
635 |
|
636 |
rect.x = 0;
|
637 |
rect.y = 0;
|
638 |
rect.w = video_width; |
639 |
rect.h = video_height; |
640 |
SDL_DisplayYUVOverlay(bmp, &rect); |
641 |
AUDDBG("+++++Process video packet\n");
|
642 |
return;
|
643 |
} |
644 |
} |
645 |
} |
646 |
|
647 |
/* JWT: (*nix ONLY) NEW FUNCTION TO SAVE POPUP VIDEO WINDOW'S LOCATION TO CONFIG SO IT POPS UP THERE NEXT TIME! */
|
648 |
/* FIXME:NEED DIFFERENT CODE TO ADD THIS FEATURE TO WINDOWS! */
|
649 |
void save_window_xy ()
|
650 |
{ |
651 |
#ifndef _WINDOWS
|
652 |
SDL_SysWMinfo info; |
653 |
SDL_VERSION(&info.version); |
654 |
if (SDL_GetWMInfo(&info) > 0) |
655 |
{ |
656 |
int x=0,y=0; |
657 |
Window chldwin; |
658 |
XWindowAttributes attr0, attr; |
659 |
info.info.x11.lock_func(); |
660 |
XGetWindowAttributes(info.info.x11.display, info.info.x11.wmwindow, &attr0); |
661 |
XGetWindowAttributes(info.info.x11.display, info.info.x11.window, &attr); |
662 |
XTranslateCoordinates(info.info.x11.display, info.info.x11.window, attr.root, attr.x, attr.y, &x, &y, &chldwin); |
663 |
info.info.x11.unlock_func(); |
664 |
aud_set_int ("ffaudio", "video_window_x", (x-attr0.x)-1); |
665 |
aud_set_int ("ffaudio", "video_window_y", (y-attr0.y)-1); |
666 |
aud_set_int ("ffaudio", "video_window_w", attr.width); |
667 |
aud_set_int ("ffaudio", "video_window_h", attr.height); |
668 |
} |
669 |
#endif
|
670 |
} |
671 |
bool FFaudio::play (const char * filename, VFSFile & file) |
672 |
{ |
673 |
AUDDBG ("Playing %s.\n", filename);
|
674 |
|
675 |
AVPacket pkt = AVPacket(); |
676 |
int errcount;
|
677 |
bool codec_opened = false; |
678 |
bool vcodec_opened = false; |
679 |
int out_fmt;
|
680 |
bool planar;
|
681 |
bool error;
|
682 |
SDL_Overlay *bmp; /* JWT: ALL SDL_* STUFF IS FOR PLAYING VIDEOS */
|
683 |
SDL_Surface *screen; |
684 |
SDL_Event event; |
685 |
int video_width;
|
686 |
int video_height;
|
687 |
int video_window_x;
|
688 |
int video_window_y;
|
689 |
int video_window_w;
|
690 |
int video_window_h;
|
691 |
float video_aspect_ratio;
|
692 |
int video_resizedelay;
|
693 |
bool fromstdin; /* JWT:SAVE (static)fromstdin's STATE AT START OF PLAY, SINCE PROBES WILL CHANGE IT IN PLAYLIST ADVANCE BEFORE WE CLOSE! */ |
694 |
struct SwsContext *sws_ctx;
|
695 |
Index<char> buf;
|
696 |
|
697 |
video_width = 0;
|
698 |
video_height = 0;
|
699 |
video_window_x = 0;
|
700 |
video_window_y = 0;
|
701 |
video_window_w = 0;
|
702 |
video_window_h = 0;
|
703 |
video_aspect_ratio = 0;
|
704 |
video_resizedelay = aud_get_int ("ffaudio", "video_resizedelay"); |
705 |
|
706 |
fromstdin = (!strcmp(filename, "-") || strstr(filename, "://-.")) ? true : false; |
707 |
error = false;
|
708 |
bmp = nullptr;
|
709 |
screen = nullptr;
|
710 |
sws_ctx = NULL;
|
711 |
AVFormatContext * ic = open_input_file (filename, file); |
712 |
if (! ic)
|
713 |
return false; |
714 |
|
715 |
CodecInfo cinfo, vcinfo; |
716 |
|
717 |
if (fromstdin) /* JWT: FOR STDIN: TRY TO GET "read_tuple()" STUFF NOW, SINCE FILE COULD NOT BE OPENED EARLIER IN read_tuple()! */ |
718 |
{ |
719 |
Tuple tuple; |
720 |
|
721 |
AUDDBG ("---- playing from STDIN: get TUPLE stuff now: IC is defined\n");
|
722 |
if (find_codec (ic, & cinfo, & vcinfo))
|
723 |
{ |
724 |
tuple.set_filename (filename); |
725 |
|
726 |
tuple.set_int (Tuple::Length, ic->duration / 1000);
|
727 |
tuple.set_int (Tuple::Bitrate, ic->bit_rate / 1000);
|
728 |
if (cinfo.codec->long_name)
|
729 |
tuple.set_str (Tuple::Codec, cinfo.codec->long_name); |
730 |
if (ic->metadata)
|
731 |
read_metadata_dict (tuple, ic->metadata); |
732 |
if (cinfo.stream->metadata)
|
733 |
read_metadata_dict (tuple, cinfo.stream->metadata); |
734 |
if (play_video && vcinfo.stream->metadata)
|
735 |
read_metadata_dict (tuple, vcinfo.stream->metadata); |
736 |
set_playback_tuple (tuple.ref ()); |
737 |
} |
738 |
else
|
739 |
{ |
740 |
AUDERR ("No codec found for %s.\n", filename);
|
741 |
goto error_exit;
|
742 |
} |
743 |
} |
744 |
else
|
745 |
{ |
746 |
if (! find_codec (ic, & cinfo, & vcinfo))
|
747 |
{ |
748 |
AUDERR ("No codec found for %s.\n", filename);
|
749 |
goto error_exit;
|
750 |
} |
751 |
} |
752 |
|
753 |
AUDDBG ("got codec %s for stream index %d, opening\n", cinfo.codec->name, cinfo.stream_idx);
|
754 |
|
755 |
if (avcodec_open2 (cinfo.context, cinfo.codec, NULL) < 0) |
756 |
goto error_exit;
|
757 |
|
758 |
codec_opened = true;
|
759 |
|
760 |
/* JWT: IF abUSER ALSO WANTS TO PLAY VIDEO THEN WE SET UP POP-UP VIDEO SCREEN: */
|
761 |
if (play_video)
|
762 |
{ |
763 |
int vx, vy, vw, vh;
|
764 |
String video_windowtitle; |
765 |
int video_sws_scale;
|
766 |
int video_xmove = 0; |
767 |
|
768 |
vx = vy = vw = vh = 0;
|
769 |
video_xmove = aud_get_int ("ffaudio", "video_xmove"); |
770 |
/* -1:always let windowmanager place (random); 0(default):place window via
|
771 |
SDL_putenv() - may work with Windows?; 1:relocate window via XMoveWindow
|
772 |
(X-specific); 2:both (0, then 1). This is sometimes useful for multiple X
|
773 |
desktops where the default of placing the window via SDL_putenv will ALWAYS
|
774 |
place the window in the same desktop that Audacious is in. By setting to 1,
|
775 |
the window will be moved to the proper location relative to the current
|
776 |
desktop, and Audacious is treated as "sticky" by the window manager. Setting
|
777 |
to 2 MAY be useful IF for some reason, none of the other choices work properly.
|
778 |
*/
|
779 |
video_window_x = video_window_y = video_window_w = video_window_h = 0;
|
780 |
video_sws_scale = 0;
|
781 |
/* GET SAVED PREV. VIDEO WINDOW LOCN. AND SIZE AND TRY TO PLACE NEW WINDOW ACCORDINGLY: */
|
782 |
/* JWT: I ADDED THIS TO AVOID NEW VID. WINDOW RANDOMLY POPPING UP IN NEW LOCN., IE. WHEN REPEATING A VIDEO. */
|
783 |
video_window_x = aud_get_int ("ffaudio", "video_window_x"); |
784 |
video_window_y = aud_get_int ("ffaudio", "video_window_y"); |
785 |
video_window_w = aud_get_int ("ffaudio", "video_window_w"); |
786 |
video_window_h = aud_get_int ("ffaudio", "video_window_h"); |
787 |
/* FIXME?:*MIGHT?* NEED DIFFERENT CODE TO ADD THIS FEATURE TO WINDOWS! */
|
788 |
if (video_xmove >= 0 && video_xmove != 1) |
789 |
{ |
790 |
char video_windowpos[40]; |
791 |
sprintf (video_windowpos, "SDL_VIDEO_WINDOW_POS=%d,%d", video_window_x, video_window_y);
|
792 |
putenv (video_windowpos); |
793 |
} |
794 |
if (avcodec_open2 (vcinfo.context, vcinfo.codec, NULL) < 0) |
795 |
goto error_exit;
|
796 |
|
797 |
vcodec_opened = true;
|
798 |
|
799 |
/* NOW CALCULATE THE WIDTH, HEIGHT, & ASPECT BASED ON VIDEO'S SIZE & AND ANY USER PARAMATERS GIVEN:
|
800 |
IDEALLY, ONE SHOULD ONLY SET X OR Y AND LET Audacious CALCULATE THE OTHER DIMENSION,
|
801 |
SO THAT THE ASPECT RATIO IS MAINTAINED, THOUGH ONE CAN SPECIFY BOTH AND FORCE
|
802 |
THE ASPECT TO BE ADJUSTED TO FIT. IF A SINGLE ONE IS SPECIFIED AS "-1", THEN
|
803 |
THE NEW WINDOW WILL KEEP THE SAME VALUE FOR THAT DIMENSION AS THE PREV. WINDOW,
|
804 |
AND ADJUST THE OTHER DIMENTION ACCORDINGLY TO FIT THE NEW VIDEO'S ASPECT RATIO.
|
805 |
*/
|
806 |
video_aspect_ratio = vcinfo.context->height |
807 |
? (float)vcinfo.context->width / (float)vcinfo.context->height : 1.0; |
808 |
vx = aud_get_int ("ffaudio", "video_xsize"); |
809 |
vy = aud_get_int ("ffaudio", "video_ysize"); |
810 |
if (vx && !vy) /* User specified (or saved) width only, calc. height based on aspect: */ |
811 |
{ |
812 |
video_width = (vx == -1) ? (video_window_w ? video_window_w : vcinfo.context->width) : vx;
|
813 |
video_height = (int)((float)video_width / video_aspect_ratio); |
814 |
} |
815 |
else if (!vx && vy) /* User specified (or saved) height only, calc. height based on aspect: */ |
816 |
{ |
817 |
video_height = (vy == -1) ? (video_window_h ? video_window_h : vcinfo.context->height) : vy;
|
818 |
video_width = (int)((float)video_height * video_aspect_ratio); |
819 |
|
820 |
} |
821 |
else if (vx && vy) /* User specified fixed width and height: */ |
822 |
{ |
823 |
if (vx == -1 && vy == -1) /* Use saved settings or video's settings (SCREW THE ASPECT)! */ |
824 |
{ |
825 |
video_width = video_window_w ? video_window_w : vcinfo.context->width; |
826 |
video_height = video_window_h ? video_window_h : vcinfo.context->height; |
827 |
} |
828 |
else if (vx == -1) /* Use specified height & calculate new width based on aspect: */ |
829 |
{ |
830 |
video_height = vy; |
831 |
video_width = (int)((float)video_height * video_aspect_ratio); |
832 |
} |
833 |
else if (vy == -1) /* Use specified width & calculate new height based on aspect: */ |
834 |
{ |
835 |
video_width = vx; |
836 |
video_height = (int)((float)video_width / video_aspect_ratio); |
837 |
} |
838 |
else /* User specified window size (SCREW THE ASPECT)! */ |
839 |
{ |
840 |
video_width = vx; |
841 |
video_height = vy; |
842 |
} |
843 |
} |
844 |
else /* User specified nothing, use the video's desired wXh (& ignore saved settings!): */ |
845 |
{ |
846 |
video_width = vcinfo.context->width; |
847 |
video_height = vcinfo.context->height; |
848 |
} |
849 |
video_aspect_ratio = video_height |
850 |
? (float)video_width / (float)video_height : 1.0; /* Fall thru to square to avoid possibliity of "/0"! */ |
851 |
|
852 |
/* NOW "RESIZE" screen to user's wXh, if user set something: */
|
853 |
#ifndef __DARWIN__
|
854 |
screen = SDL_SetVideoMode (video_width, video_height, 0, SDL_RESIZABLE);
|
855 |
#else
|
856 |
screen = SDL_SetVideoMode (video_width, video_height, 24, SDL_RESIZABLE);
|
857 |
#endif
|
858 |
if (!screen) {
|
859 |
AUDERR ("SDL: could not re-set video mode - exiting\n");
|
860 |
goto error_exit;
|
861 |
} |
862 |
if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER))
|
863 |
{ |
864 |
AUDERR ("Could not initialize SDL - %s\n", SDL_GetError());
|
865 |
goto error_exit;
|
866 |
} |
867 |
/* JWT:FIXME:IF *nix THEN MOVE (POPUP) WINDOW (AGAIN) TO WHERE IT WAS LEFT LAST TIME (ONLY IF USER SAYS WE NEED THIS (ie. ALT. DESKTOP) */
|
868 |
#ifndef _WINDOWS
|
869 |
if (video_xmove > 0) |
870 |
{ |
871 |
SDL_SysWMinfo info; |
872 |
SDL_VERSION (&info.version); |
873 |
if (SDL_GetWMInfo (&info) > 0 ) { |
874 |
SDL_Delay(50);
|
875 |
XMoveWindow (info.info.x11.gfxdisplay, info.info.x11.wmwindow, video_window_x, video_window_y); |
876 |
} |
877 |
} |
878 |
#endif
|
879 |
video_windowtitle = aud_get_str ("ffaudio", "video_windowtitle"); |
880 |
if (video_windowtitle)
|
881 |
SDL_WM_SetCaption(video_windowtitle, NULL);
|
882 |
if (aud_get_int ("ffaudio", "video_sws_scale")) /* USER CAN CHOOSE SWS_SCALE VALUE. */ |
883 |
video_sws_scale = aud_get_int ("ffaudio", "video_sws_scale"); |
884 |
else
|
885 |
video_sws_scale = SWS_BICUBIC; /* default=4 (SWS_BICUBIC). */
|
886 |
|
887 |
bmp = SDL_CreateYUVOverlay( |
888 |
vcinfo.context->width, |
889 |
vcinfo.context->height, |
890 |
SDL_YV12_OVERLAY, |
891 |
screen |
892 |
); |
893 |
sws_ctx = sws_getContext ( |
894 |
vcinfo.context->width, |
895 |
vcinfo.context->height, |
896 |
vcinfo.context->pix_fmt, |
897 |
vcinfo.context->width, |
898 |
vcinfo.context->height, |
899 |
PIX_FMT_YUV420P, |
900 |
video_sws_scale, |
901 |
NULL,
|
902 |
NULL,
|
903 |
NULL
|
904 |
); |
905 |
} |
906 |
|
907 |
switch (cinfo.context->sample_fmt)
|
908 |
{ |
909 |
case AV_SAMPLE_FMT_U8: out_fmt = FMT_U8; planar = false; break; |
910 |
case AV_SAMPLE_FMT_S16: out_fmt = FMT_S16_NE; planar = false; break; |
911 |
case AV_SAMPLE_FMT_S32: out_fmt = FMT_S32_NE; planar = false; break; |
912 |
case AV_SAMPLE_FMT_FLT: out_fmt = FMT_FLOAT; planar = false; break; |
913 |
|
914 |
case AV_SAMPLE_FMT_U8P: out_fmt = FMT_U8; planar = true; break; |
915 |
case AV_SAMPLE_FMT_S16P: out_fmt = FMT_S16_NE; planar = true; break; |
916 |
case AV_SAMPLE_FMT_S32P: out_fmt = FMT_S32_NE; planar = true; break; |
917 |
case AV_SAMPLE_FMT_FLTP: out_fmt = FMT_FLOAT; planar = true; break; |
918 |
|
919 |
default:
|
920 |
AUDERR ("Unsupported audio format %d\n", (int) cinfo.context->sample_fmt); |
921 |
goto error_exit;
|
922 |
} |
923 |
|
924 |
/* Open audio output */
|
925 |
AUDDBG("opening audio output - bitrate=%d=\n", ic->bit_rate);
|
926 |
|
927 |
set_stream_bitrate (ic->bit_rate); |
928 |
open_audio (out_fmt, cinfo.context->sample_rate, cinfo.context->channels); |
929 |
|
930 |
errcount = 0;
|
931 |
|
932 |
int seek_value, ret, decoded, len, size, video_qsize, acount, vcount;
|
933 |
bool knit1perl2;
|
934 |
acount = 0; vcount = 0; /* JWT:LET'S COUNT PACKETS OF EACH SIZE FOR DEBUGGING. */ |
935 |
AVFrame * frame; |
936 |
AVFrame * vframe; |
937 |
/* JWT:video_qsize: MAX # VIDEO PACKETS TO QUEUE UP FOR INTERLACING TO SMOOTH VIDEO
|
938 |
PLAYBACK - GOOD RANGE IS 8-56, NOT ENOUGH=JITTERY VIDEO,
|
939 |
TOO MANY=AUDIO/VIDEO BECOME NOTICABLY OUT OF SYNC!
|
940 |
*/
|
941 |
video_qsize = (aud_get_int ("ffaudio", "video_qsize")) |
942 |
? aud_get_int ("ffaudio", "video_qsize") : 16; |
943 |
if (video_qsize < 1) |
944 |
video_qsize = 1;
|
945 |
|
946 |
pktQueue *pktQ; |
947 |
pktQ = createQueue (video_qsize); |
948 |
/*
|
949 |
JWT: THIS FLAG FORCES THE VIDEO QUEUE TO BE POPPED ONCE FOR EVERY *TWO* AUDIO
|
950 |
PACKETS - MOST VIDEOS SEEM TO HAVE A/B 2-1 RATIO OF AUDIO TO VIDEO PACKETS &
|
951 |
THIS PRETTY MUCH *ELIMINATED* THE VIDEO JITTER!!!
|
952 |
*/
|
953 |
knit1perl2 = true; /* ALTERNATES BETWEEN ON AND OFF EVERY OTHER AUDIO PACKET */ |
954 |
|
955 |
AUDDBG ("video queue size %d\n", video_qsize);
|
956 |
#if CHECK_LIBAVCODEC_VERSION (55, 45, 101, 55, 28, 1) |
957 |
frame = av_frame_alloc (); |
958 |
vframe = av_frame_alloc (); |
959 |
#else
|
960 |
frame = avcodec_alloc_frame (); |
961 |
vframe = avcodec_alloc_frame (); |
962 |
#endif
|
963 |
|
964 |
while (! check_stop ())
|
965 |
{ |
966 |
seek_value = check_seek (); |
967 |
|
968 |
if (seek_value >= 0) |
969 |
{ |
970 |
/* JWT:FIRST, FLUSH ANY VIDEO PACKETS SITTING IN THE QUEUE TO CLEAR THE QUEUE! */
|
971 |
QFlush (pktQ); |
972 |
knit1perl2 = true;
|
973 |
|
974 |
/* JWT: HAD TO CHANGE THIS FROM "AVSEEK_FLAG_ANY" TO AVSEEK_FLAG_BACKWARD
|
975 |
TO GET SEEK TO NOT RANDOMLY BRICK?! */
|
976 |
if (av_seek_frame (ic, -1, (int64_t) seek_value * AV_TIME_BASE / |
977 |
1000, AVSEEK_FLAG_BACKWARD) < 0) |
978 |
{ |
979 |
AUDERR ("error while seeking\n");
|
980 |
} else
|
981 |
errcount = 0;
|
982 |
|
983 |
seek_value = -1;
|
984 |
} |
985 |
|
986 |
AVPacket tmp; |
987 |
|
988 |
/* Read next frame (or more) of data */
|
989 |
if ((ret = av_read_frame (ic, &pkt)) < 0) |
990 |
{ |
991 |
if (ret == (int) AVERROR_EOF) |
992 |
{ |
993 |
AUDDBG ("eof reached\n");
|
994 |
break;
|
995 |
} |
996 |
else
|
997 |
{ |
998 |
if (++errcount > 4) |
999 |
{ |
1000 |
AUDERR ("av_read_frame error %d, giving up.\n", ret);
|
1001 |
break;
|
1002 |
} else
|
1003 |
continue;
|
1004 |
} |
1005 |
} else
|
1006 |
errcount = 0;
|
1007 |
|
1008 |
/* Ignore any other substreams */
|
1009 |
if (pkt.stream_index != cinfo.stream_idx)
|
1010 |
{ |
1011 |
if (!play_video || pkt.stream_index != vcinfo.stream_idx)
|
1012 |
{ |
1013 |
av_free_packet (&pkt); |
1014 |
continue;
|
1015 |
} |
1016 |
} |
1017 |
|
1018 |
/* Decode and play packet/frame */
|
1019 |
memcpy (&tmp, &pkt, sizeof(tmp));
|
1020 |
while (tmp.size > 0 && ! check_stop ()) |
1021 |
{ |
1022 |
/* Check for seek request and bail out if we have one */
|
1023 |
if (seek_value < 0) |
1024 |
seek_value = check_seek (); |
1025 |
|
1026 |
if (seek_value >= 0) |
1027 |
break;
|
1028 |
|
1029 |
decoded = 0;
|
1030 |
if (pkt.stream_index == cinfo.stream_idx) /* WE READ AN AUDIO PACKET: */ |
1031 |
{ |
1032 |
++acount; |
1033 |
AUDDBG("-read audio frame\n");
|
1034 |
/* JWT:IF PLAYING VIDEO, CHECK VIDEO QUEUE. */
|
1035 |
if (play_video)
|
1036 |
{ |
1037 |
if (knit1perl2)
|
1038 |
{ |
1039 |
/* CHECK VIDEO QUEUE FIRST EVERY OTHER AUDIO PACKET AND POP AND
|
1040 |
PROCESS ONE VIDEO PACKET (BEFORE EVERY OTHER AUDIO PACKET)
|
1041 |
*/
|
1042 |
AVPacket * pktRef; |
1043 |
|
1044 |
if (!isQueueFull (pktQ))
|
1045 |
knit1perl2 = false;
|
1046 |
if ((pktRef = QFront (pktQ)))
|
1047 |
{ |
1048 |
write_videoframe (sws_ctx, & cinfo, & vcinfo, vframe, bmp, pktRef, |
1049 |
video_width, video_height); |
1050 |
if (!Dequeue (pktQ))
|
1051 |
AUDDBG ("Queue is Empty\n");
|
1052 |
} |
1053 |
} |
1054 |
else
|
1055 |
knit1perl2 = true; /* JWT:WE'RE ASSUMING LIKELY NEAR 2x AUDIO TO VIDEO FRAMES: */ |
1056 |
} |
1057 |
/* NOW PROCESS THE AUDIO PACKET: */
|
1058 |
len = avcodec_decode_audio4 (cinfo.context, frame, & decoded, & tmp); |
1059 |
if (len < 0) |
1060 |
{ |
1061 |
AUDERR ("decode_audio() failed, code %d\n", len);
|
1062 |
break;
|
1063 |
} |
1064 |
|
1065 |
tmp.size -= len; |
1066 |
tmp.data += len; |
1067 |
|
1068 |
if (! decoded)
|
1069 |
/* continue; // JWT:NOT SURE WHY THIS WAS A CONTINUE INSTEAD OF A BREAK?! */
|
1070 |
break;
|
1071 |
|
1072 |
size = FMT_SIZEOF (out_fmt) * cinfo.context->channels * frame->nb_samples; |
1073 |
|
1074 |
AUDDBG ("+++++Process AUDIO packet\n");
|
1075 |
if (planar)
|
1076 |
{ |
1077 |
if (size > buf.len ())
|
1078 |
buf.resize (size); |
1079 |
|
1080 |
audio_interlace ((const void * *) frame->data, out_fmt, |
1081 |
cinfo.context->channels, buf.begin (), frame->nb_samples); |
1082 |
write_audio (buf.begin (), size); |
1083 |
} |
1084 |
else
|
1085 |
write_audio (frame->data[0], size);
|
1086 |
} |
1087 |
else /* WE READ A VIDEO PACKET: */ |
1088 |
{ |
1089 |
++vcount; |
1090 |
/* JWT: IF QUEUE IS FULL, PROCESS NEXT VIDEO PACKET FROM QUEUE. */
|
1091 |
AUDDBG ("+read video frame\n");
|
1092 |
if (isQueueFull (pktQ))
|
1093 |
{ |
1094 |
AVPacket * pktRef; |
1095 |
|
1096 |
if ((pktRef = QFront (pktQ)))
|
1097 |
{ |
1098 |
write_videoframe (sws_ctx, & cinfo, & vcinfo, vframe, bmp, pktRef, |
1099 |
video_width, video_height); |
1100 |
if (!Dequeue (pktQ))
|
1101 |
AUDERR ("Queue is Empty\n");
|
1102 |
} |
1103 |
AUDDBG ("Queue filled.\n");
|
1104 |
} |
1105 |
/* JWT: VIDEO PACKETS TAKE A NUMBER & GET IN LINE! */
|
1106 |
if (Enqueue (pktQ, pkt))
|
1107 |
{ |
1108 |
knit1perl2 = true;
|
1109 |
break;
|
1110 |
} |
1111 |
AUDERR ("Could not enqueue packet, refuses to wait in line, so shoot it!!\n");
|
1112 |
break;
|
1113 |
} |
1114 |
|
1115 |
/* JWT: NOW HANDLE VIDEO UI EVENTS SUCH AS RESIZE OR KILL SCREEN: */
|
1116 |
if (play_video)
|
1117 |
{ |
1118 |
SDL_Rect rect; |
1119 |
SDL_PollEvent (&event); |
1120 |
switch (event.type) {
|
1121 |
case SDL_QUIT:
|
1122 |
save_window_xy(); |
1123 |
SDL_Quit (); |
1124 |
play_video = false;
|
1125 |
break;
|
1126 |
|
1127 |
case SDL_VIDEORESIZE:
|
1128 |
/* Resize the screen. */
|
1129 |
float new_aspect_ratio;
|
1130 |
|
1131 |
new_aspect_ratio = event.resize.h |
1132 |
? (float)event.resize.w / (float)event.resize.h : 1.0; |
1133 |
if (new_aspect_ratio > video_aspect_ratio)
|
1134 |
{ |
1135 |
video_height = event.resize.h; |
1136 |
video_width = (int)(video_aspect_ratio * (float)video_height); |
1137 |
} |
1138 |
else
|
1139 |
{ |
1140 |
video_width = event.resize.w; |
1141 |
video_height = (int)((float)video_width / video_aspect_ratio); |
1142 |
} |
1143 |
|
1144 |
#ifndef __DARWIN__
|
1145 |
screen = SDL_SetVideoMode (video_width, video_height, 0, SDL_RESIZABLE);
|
1146 |
#else
|
1147 |
screen = SDL_SetVideoMode (video_width, video_height, 24, SDL_RESIZABLE);
|
1148 |
#endif
|
1149 |
|
1150 |
/* If there's an error, punt! */
|
1151 |
if (video_resizedelay > 0) |
1152 |
SDL_Delay(video_resizedelay); |
1153 |
if (screen == NULL) |
1154 |
{ |
1155 |
AUDERR ("Could not recreate screen after resizing!");
|
1156 |
break;
|
1157 |
} |
1158 |
case SDL_VIDEOEXPOSE:
|
1159 |
rect.x = 0;
|
1160 |
rect.y = 0;
|
1161 |
rect.w = video_width; |
1162 |
rect.h = video_height; |
1163 |
SDL_DisplayYUVOverlay (bmp, &rect); |
1164 |
break;
|
1165 |
|
1166 |
default:
|
1167 |
break;
|
1168 |
} |
1169 |
} |
1170 |
} |
1171 |
} |
1172 |
|
1173 |
error_exit:
|
1174 |
AUDDBG("end of playback - % audio frames, % video frames processed.", acount, vcount);
|
1175 |
if (play_video)
|
1176 |
{ |
1177 |
save_window_xy (); |
1178 |
SDL_Quit(); |
1179 |
} |
1180 |
|
1181 |
#if CHECK_LIBAVCODEC_VERSION (55, 45, 101, 55, 28, 1) |
1182 |
av_frame_free (& vframe); |
1183 |
av_frame_free (& frame); |
1184 |
#elif CHECK_LIBAVCODEC_VERSION (54, 59, 100, 54, 28, 0) |
1185 |
avcodec_free_frame (& vframe); |
1186 |
avcodec_free_frame (& frame); |
1187 |
#else
|
1188 |
av_free (vframe); |
1189 |
av_free (frame); |
1190 |
#endif
|
1191 |
|
1192 |
if (pkt.data)
|
1193 |
av_free_packet (&pkt); |
1194 |
if (vcodec_opened)
|
1195 |
avcodec_close(vcinfo.context); |
1196 |
if (codec_opened)
|
1197 |
avcodec_close(cinfo.context); |
1198 |
if (ic != nullptr) |
1199 |
close_input_file (ic, fromstdin); |
1200 |
|
1201 |
return ! error;
|
1202 |
} |
1203 |
|
1204 |
const char FFaudio::about[] = |
1205 |
N_("Multi-format audio decoding plugin for Audacious using\n"
|
1206 |
"FFmpeg multimedia framework (http://www.ffmpeg.org/)\n"
|
1207 |
"\n"
|
1208 |
"Audacious plugin by:\n"
|
1209 |
"William Pitcock <nenolod@nenolod.net>\n"
|
1210 |
"Matti Hämäläinen <ccr@tnsp.org>\n"
|
1211 |
"\n"
|
1212 |
"Video-playing capability (added 2015) by:\n"
|
1213 |
"Jim Turner <turnerjw784@yahoo.com>");
|
1214 |
|
1215 |
const char * const FFaudio::exts[] = { |
1216 |
/* musepack, SV7/SV8 */
|
1217 |
"mpc", "mp+", "mpp", |
1218 |
|
1219 |
/* windows media audio */
|
1220 |
"wma",
|
1221 |
|
1222 |
/* shorten */
|
1223 |
"shn",
|
1224 |
|
1225 |
/* atrac3 */
|
1226 |
"aa3", "oma", |
1227 |
|
1228 |
/* MPEG 2/4 AC3 */
|
1229 |
"ac3",
|
1230 |
|
1231 |
/* monkey's audio */
|
1232 |
"ape",
|
1233 |
|
1234 |
/* DTS */
|
1235 |
"dts",
|
1236 |
|
1237 |
/* VQF */
|
1238 |
"vqf",
|
1239 |
|
1240 |
/* MPEG-4 */
|
1241 |
"m4a", "mp4", |
1242 |
|
1243 |
/* WAV (there are some WAV formats sndfile can't handle) */
|
1244 |
"wav",
|
1245 |
|
1246 |
/* Handle OGG streams (FLAC/Vorbis etc.) */
|
1247 |
"ogg", "oga", |
1248 |
|
1249 |
/* Opus */
|
1250 |
"opus",
|
1251 |
|
1252 |
/* Speex */
|
1253 |
"spx",
|
1254 |
|
1255 |
/* True Audio */
|
1256 |
"tta",
|
1257 |
|
1258 |
/* AVI // JWT:ADDED */
|
1259 |
"avi",
|
1260 |
|
1261 |
/* FLV // JWT:ADDED */
|
1262 |
"flv",
|
1263 |
|
1264 |
/* end of table */
|
1265 |
nullptr
|
1266 |
}; |
1267 |
|
1268 |
const char * const FFaudio::mimes[] = {"application/ogg", nullptr}; |