ffplay не может воспроизводить более одной песни - PullRequest
0 голосов
/ 01 февраля 2020

Я взял файл ffplay. c из http://ffmpeg.org/doxygen/trunk/ffplay_8c-source.html и отредактировал его в файл cpp для встраивания в мое приложение win32 gui. я внес следующие изменения в него.

  1. превратил основную функцию int в локальную функцию следующим образом: я могу передать HWND для встраивания проигрывателя
void Ffplay::play_song(string file, HWND parent, bool* successfull)
{
    int flags;
    VideoState* is;
    input_filename = file; 
    /* register all codecs, demux and protocols */
#if CONFIG_AVDEVICE
    avdevice_register_all();
#endif
    //avformat_network_init();
    //check whether the filename is valid
    if (input_filename.empty())
    {
        logger.log(logger.LEVEL_ERROR, "filename %s is not valid\n", file);
        return;
    }
    if (display_disable)
    {
        video_disable = 1;
    }
    flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
    if (audio_disable)
        flags &= ~SDL_INIT_AUDIO;
    else
    {
        /* Try to work around an occasional ALSA buffer underflow issue when the
         * period size is NPOT due to ALSA resampling by forcing the buffer size. */
        if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
            SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE", "1", 1);
    }
    if (display_disable)
        flags &= ~SDL_INIT_VIDEO;
    SDL_SetMainReady();
    if (SDL_Init(flags))
    {
        logger.log(logger.LEVEL_ERROR, "Could not initialize SDL - %s\n", SDL_GetError());
        logger.log(logger.LEVEL_ERROR, "(Did you set the DISPLAY variable?)\n");
        return;
    }
    //Initialize optional fields of a packet with default values.
    //Note, this does not touch the data and size members, which have to be initialized separately.
    av_init_packet(&flush_pkt);
    flush_pkt.data = (uint8_t*)&flush_pkt;

    if (!display_disable)
    {
        int flags = SDL_WINDOW_HIDDEN;
        if (alwaysontop)
#if SDL_VERSION_ATLEAST(2,0,5)
            flags |= SDL_WINDOW_ALWAYS_ON_TOP;
#else
            logger.log(logger.LEVEL_INFO, "SDL version doesn't support SDL_WINDOW_ALWAYS_ON_TOP. Feature will be inactive.\n");
#endif
        if (borderless)
            flags |= SDL_WINDOW_BORDERLESS;
        else
            flags |= SDL_WINDOW_RESIZABLE;
        SDL_InitSubSystem(flags);
        ShowWindow(parent, true);
        //window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
        window = SDL_CreateWindowFrom(parent);
        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
        if (window) {
            renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
            if (!renderer)
            {
                logger.log(logger.LEVEL_ERROR, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
                renderer = SDL_CreateRenderer(window, -1, 0);
            }
            if (renderer)
            {
                if (!SDL_GetRendererInfo(renderer, &renderer_info))
                {
                    logger.log(logger.LEVEL_INFO, "Initialized %s renderer.\n", renderer_info.name);
                }
            }
        }
        if (!window || !renderer || !renderer_info.num_texture_formats)
        {
            logger.log(logger.LEVEL_ERROR, "Failed to create window or renderer: %s\n", SDL_GetError());
            return;
        }
    }

    is = stream_open(input_filename.c_str(), file_iformat);
    if (!is)
    {
        logger.log(logger.LEVEL_ERROR, "Failed to initialize VideoState!\n");
        return;
    }
    //the song is playing now
    *successfull = true;
    event_loop(is);
    //the song has quit;
    *successfull = false;
}
изменил функции обратного вызова, так как c * stati c не могут быть использованы, например,
void Ffplay::static_sdl_audio_callback(void* opaque, Uint8* stream, int len)
{
    static_cast<Ffplay*>(opaque)->sdl_audio_callback(opaque, stream, len);
}

закрытие не меняется из основного файла для закрытия аудио и SDL framework

void Ffplay::do_exit(VideoState* is)
{
    abort = true;
    if(is)
    {
        stream_close(is);
    }
    if (renderer)
        SDL_DestroyRenderer(renderer);
    if (window)
         SDL_DestroyWindow(window);
#if CONFIG_AVFILTER
    av_freep(&vfilters_list);
#endif
    avformat_network_deinit();
    SDL_Quit();

}

я вызываю функции следующим образом из main gui

ft=std::async(launch::async, &Menu::play_song, this, songs_to_play.at(0));

menu::play_song функция:

void Menu::play_song(wstring song_path)
{
    ready_to_play_song = false;
    OutputDebugString(L"\nbefore song\n");
    using std::future;
    using std::async;
    using std::launch;

    string input{ song_path.begin(),song_path.end() };
    Ffplay ffplay;
    ffplay.play_song(input, h_sdl_window, &song_opened);

    OutputDebugString(L"\nafter song\n");
    ready_to_play_song = true;
}

ПРОБЛЕМА это я может играть только одну песню. если я снова вызываю функцию menu::play_song, звук пропадает, и иногда отсутствует обложка для видео / обложки. кажется, что некоторые ресурсы не были освобождены или что-то в этом роде.

Я локализовал проблему для этой функции

int Ffplay::packet_queue_get(PacketQueue* q, AVPacket* pkt, int block, int* serial)
{

    MyAVPacketList* pkt1;
    int ret;
    int count=0;
    SDL_LockMutex(q->mutex);

    for (;;) 
    {


        if (q->abort_request)
        {
            ret = -1;
            break;
        }

        pkt1 = q->first_pkt;
        if (pkt1) {
            q->first_pkt = pkt1->next;
            if (!q->first_pkt)
                q->last_pkt = NULL;
            q->nb_packets--;
            q->size -= pkt1->pkt.size + sizeof(*pkt1);
            q->duration -= pkt1->pkt.duration;
            *pkt = pkt1->pkt;
            if (serial)
                *serial = pkt1->serial;
            av_free(pkt1);
            ret = 1;
            break;
        }
        else if (!block) {
            ret = 0;
            break;
        }
        else 
        {
            logger.log(logger.LEVEL_INFO, "packet_queue before");
            SDL_CondWait(q->cond, q->mutex);
            logger.log(logger.LEVEL_INFO, "packet_queue after");

        }
    }
    SDL_UnlockMutex(q->mutex);
    return ret;
}

вызов SDL_CondWait(q->cond, q->mutex); никогда не возвращает

1 Ответ

1 голос
/ 05 февраля 2020

Во-первых, извините, вопрос был немного расплывчатым, так как я не мог загрузить большую часть кода, так как он был длинным, поэтому я разместил ссылку на оригинальную, похожую на мою, единственное отличие - я изменил функции от C stati c единиц до C ++ publi c единиц.

Проблема была в переменной SDL_AudioDeviceID audio_dev

Назначение переменной с помощью audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE) не назначало переменную, как планировалось, и когда дело дошло до закрытия аудиоустройства с использованием SDL_CloseAudioDevice(audio_dev); аудиоустройства переменная была 0, поэтому устройство не было закрыто, и в результате во второй песне отсутствовал звук или видео могло зависнуть. Это результат использования различных потоков, которые вызывают локальные функции обратного вызова, приведенные к состоянию c, как и ожидалось от API SDL.

Ответом было изменение переменной устройства на stati c один, static SDL_AudioDeviceID audio_dev;, поэтому переменная может быть доступна из любого места в программе

...