ffplay不能播放多首歌曲

时间:2020-02-01 16:28:19

标签: c++ winapi ffmpeg sdl

我已经从http://ffmpeg.org/doxygen/trunk/ffplay_8c-source.html中获取了ffplay.c文件,并将其重新编辑为一个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;
}
  1. 更改了回调函数,因为c ++无法使用静态函数,例如
void Ffplay::static_sdl_audio_callback(void* opaque, Uint8* stream, int len)
{
    static_cast<Ffplay*>(opaque)->sdl_audio_callback(opaque, stream, len);
}

关闭不会从主文件更改为关闭音频和sdl框架

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();

}

我从主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 个答案:

答案 0 :(得分:1)

首先,对不起,这个问题有点含糊,因为我不能上传太多代码,因为很长很长的时间,这就是为什么我将链接发布到类似于我的原始链接的原因,唯一的区别是我已经更改了功能从C静态的到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,因此未关闭该设备,因此第二首歌丢失声音或视频可能会挂起。 这是因为使用了各种线程,这些线程调用了本地回调函数,这些函数被强制转换为SDL api预期的静态性质。

答案是将设备变量更改为静态变量static SDL_AudioDeviceID audio_dev;,因此可以从程序中的任何位置访问变量

相关问题