Project

General

Profile

ffaudio-core.cc

Source - Jim Turner, October 29, 2015 06:59

 
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};