Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (13)
  • Alexandre Janniaux's avatar
    qt: mlbookmarkmodel: use lambda for player cbs · df4ec732
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    The lambda will allow to use a zero-initializer and only initialize the
    required fields.
    df4ec732
  • Alexandre Janniaux's avatar
    vlc_player: fix typo in doc · d286d255
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    d286d255
  • Alexandre Janniaux's avatar
    VLCPlayerController: use designated initializers · 5d502b0b
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Designated initializer will allow to add new fields in the
    vlc_player_cbs structure while automatically initializing the new fields
    to null.
    5d502b0b
  • Alexandre Janniaux's avatar
    vlc_player: add a new on_stopping_current_media event · 2ae6636c
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Add a new event notifying when the current media is stopping.
    
    The media can be stopping without the player being stopping, ie. when
    the media is finished and the player prepares the next one, but also
    when the player changes media while having the player in the
    VLC_PLAYER_STATE_PLAYING state.
    
    The event will also be fired when the player is simply stopping, in
    which case we expect that VLC_PLAYER_STATE_STOPPING is signaled before
    the input is actually stop, which is coherent with the current
    behaviour.
    2ae6636c
  • Alexandre Janniaux's avatar
    libvlc_events: add libvlc_MediaPlayerMediaStopping · 4ac61be1
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    The event forwards the vlc_player_t event `on_stopping_current_media`
    which signals that the media actually playing inside the player is
    stopping.
    
    The event is essential to the `libvlc_media_new_callbacks` API since
    there's no way to stop the \ref libvlc_media_read_cb callback without
    triggering an EOF, and the EOF should be signalled only as soon as the
    media is stopping.
    4ac61be1
  • Alexandre Janniaux's avatar
    test: player: add player state_to_string function · 9bb751cb
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Most of the code is checking the player state, but we had no way to
    signal in the test logs which state was actually there when it fails.
    9bb751cb
  • Alexandre Janniaux's avatar
    test: player: dump the state vec when asserting · bcba4461
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Dumping the list of state helps finding the root cause of the issue when
    a test fails, but also enable additional manual tests to check whether
    an unexpected untested test transition has happened or not.
    bcba4461
  • Alexandre Janniaux's avatar
    test: player: add a state_equal() function · 52a231b8
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    The function improves the error reporting when the assertion is
    triggered, and also logs what were the resulting state, while nicely
    handling negative index like python arrays.
    52a231b8
  • Alexandre Janniaux's avatar
    lib: media_player: move vlc_assert_unreachable · 4a35193e
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    By moving the vlc_assert_unreachable after the switch(), we can get
    warnings when not every cases are handled since there is no default
    case, and we can keep asserting when it fails to reach any return
    statement before.
    4a35193e
  • Alexandre Janniaux's avatar
    lib: media_player: split function · 6d40b91c
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Splitting the function allows return statement, which removes the need
    for break, and simplify the code. It also allows to use a designated
    initializer for the event structure.
    
    The code also move the vlc_assert_unreachable in the new function.
    
    By moving the vlc_assert_unreachable after the switch(), we can get
    warnings when not every cases are handled since there is no default
    case, and we can keep asserting when it fails to reach any return
    statement before.
    6d40b91c
  • Alexandre Janniaux's avatar
    player: signal on_current_media_stopping event · e651ac80
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Signal that the media is stopping to the client. This is done after
    input_Stop so that application can rely on the fact that the underlying
    input_thread is already stopping, so that we can trigger actions like
    EOS, closing input resources, etc.
    e651ac80
  • Alexandre Janniaux's avatar
    lib: media player: signal stopping media event · 3841df7a
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    Signal that the media is stopping to the libvlc client. This is done
    after notifying the media on the VLC side that we're going to stop it,
    so that application can rely on the fact that the objects using
    application resources are already stopping, so that we can trigger
    actions like EOS, closing those resources, etc.
    3841df7a
  • Alexandre Janniaux's avatar
    test: libvlc_callback: check imem-access behaviour · 30d1237d
    Alexandre Janniaux authored and Steve Lhomme's avatar Steve Lhomme committed
    The previous patches added an event from libvlc_media_player_t which
    notify the application that the current media is stopping, regardless of
    whether the player itself is stopping or that it will process the next
    media.
    
    Those patches solve a design problem on the imem-access and libvlc
    callbacks where the application could not be notified that the input was
    stopped when the read() callback was pending, which happens through
    vlc_interrupt_t in other demux.
    
    In that situation, an application wanting to trigger the stop of the
    media player can only signal the media end-of-stream,
    triggering a pipeline drain, before stopping the player.
    
    ```mermaid
    sequenceDiagram
    
        box Orange VLC side
        participant input as Input thread
        participant imem as imem access
        end
    
        box Cyan Application size
        participant app as Application
        end
    
        input ->> imem: stream::read()
    
        activate imem
        imem ->> app: media callback read()
        app --x imem: return "EOS" (no data to send)
        imem --x input: Trigger pipeline drain (EOS)
        deactivate imem
    
        app ->> input: stop (change media)
    ```mermaid
    
    This decoder and output drain adds latency to the change of a media. The
    duration depends on condition but I've been able to reproduce a steady
    400ms latency addition when changing media (meaning that it will take
    400ms more to start reading the new media), up to a gigantic 1.6s in a
    randomized manner (1.1s being taken by the drain of the audio decoder!).
    
    With the new system, it's now possible to let the application respond to
    the new event to handle the interruption and termination of what it has
    been doing in the libvlc_media_read_cb function, without triggering the
    end of the media when it would have triggered a drain.
    
    ```mermaid
    sequenceDiagram
    
        box Orange VLC side
        participant destructor as Player input destructor
        participant input as Input
        participant imem as imem access
        end
    
        box Cyan Application side
        participant player as Player
        participant app as Application
        end
    
        par Input thread
        activate imem
        input ->> imem: stream::read()
        imem ->> app: media callback read_cb()
        app ->> app: read_cb() might be waiting for some data
    
        and Application thread
        app ->> player: Stop()
        player ->> destructor: Add current input to destructor
    
        and Player destructor thread
        destructor ->> input: input_Stop()
        destructor ->> app: libvlc_MediaPlayerMediaStopping event
        end
    
        app ->> app: Interrupt read_cb() and return EOF
        deactivate imem
    ```
    
    This commit verifies this behaviour in a reliable way to ensure that the
    problem is indeed solved by this approach, while allowing to document
    the solution on the application side.
    
    To do so, two different tests are made:
    
     - A sanity test which checks that we can stop a libvlc media which is
       not blocking, basically checking that the media_callback API works
       and running it when we want to check against sanitizers.
    
     - A specific test checking that we can block the read() and only
       unblock it when the libvlc_MediaPlayerMediaStopping event is emitted,
       checking that the documented behaviour for the media callbacks is
       working correctly.
    
    ---
    
    The test needs:
    
     - No intermediate cache so that we clearly control the access from the
       demux.
    
     - Better deterministic behaviour, typically to check that we don't call
       AccessReadNonBlocking twice if we don't notify between them and try
       to check that read() is correctly terminating in this case.
    
    ---
    
    On a side note, other solutions were investigated to solve this issue:
    
    1/ Provide an `errno` variable as parameter to read:
    
    By providing an errno parameter, we can provide a specific error code
    when -1 is returned, and in particular use EAGAIN/EWOULDBLOCK to inform
    the imem-access code that we don't have data yet. Then, the imem-access
    can wait for a notification from the application to start reading again,
    while listening to interruption coming from the core.
    
    This solution was submitted but it felt like alienating the libVLC imem
    API and libvlc media API to solve this problem, without considering it
    as an application problem as a whole.
    
    2/ Provide the same solution but use `errno` directly:
    
    This solution would be the simplest but there's no way for the
    application to realize that errno returning EAGAIN, ie. mapping the read
    callback directly to read() on a non-blocking socket for instance, might
    create silent regression where the access would not try to fetch more
    data, since the application won't know it must notify the access.
    
    3/ Provide a poll_cb() callback in the imem interface
    
    The solution would require another callback, instead of another
    parameter. The new callback would be called every time before the read()
    callback, and would notify whether the read() is ready or not. This adds
    a new way for the application to exit, differentiating the return code
    for read() from the return code from poll(), but the access still cannot
    be interrupted and the additional cost on function call is higher.
    
    4/ Provide a `waker` assignment (like rust futures)
    
    Provide a new callback to specify an object that can be used to notify
    the access that new data is available. This is much like poll() but it
    has the benefit that the application can immediately notify that no data
    is available, and then provide the object to notify the arrival of data
    asynchronously without exposing a public function on libvlc_media_t.
    This is still quite complex and still has a heavy function call cost,
    but it also fullfill every points from the suggestion here. However, the
    rust async model is not really the idiomatic model in C compared to
    POSIX errno.
    
    5/ Solve the problem at the decoder and output level
    
    One of the main symptoms that can be observed when changing media is the
    increase of the time to close a media, and in particular, to drain the
    decoder. This is also a non-interruptible task, because a decoder drain
    is synchronous. Admittedly, the interruption problem is less problematic
    if the drain itself can be handled smoothly afterwards, and I actually
    think both problems need to be solved eventually. But the decoder
    behaviour on drain depends on the decoder implementation, whereas the
    imem-access suggests a solution on the application side to correctly
    express the required intent, and it is also quite suitable to express an
    injection workflow (data being written from the application to the
    VLC pipeline) as well as a pull workflow (data being read from the
    application by VLC).
    30d1237d
......@@ -241,6 +241,18 @@ enum libvlc_event_e {
* The renderer item is no longer valid.
*/
libvlc_RendererDiscovererItemDeleted,
/**
* The current media set into the \ref libvlc_media_player_t is stopping.
*
* This event can be used to notify when the media callbacks, initialized
* from \ref libvlc_media_new_callbacks, should be interrupted, and in
* particular the \ref libvlc_media_read_cb. It can also be used to signal
* the application state that any input resource (webserver, file mounting,
* etc) can be discarded. Output resources still need to be active until
* the player switches to the \ref libvlc_Stopped state.
*/
libvlc_MediaPlayerMediaStopping,
};
/**
......@@ -371,6 +383,12 @@ typedef struct libvlc_event_t
libvlc_media_t * new_media;
} media_player_media_changed;
struct
{
libvlc_media_t * media;
} media_player_media_stopping;
/* ESAdded, ESDeleted, ESUpdated */
struct
{
......
......@@ -1146,7 +1146,7 @@ vlc_player_GetSelectedChapter(vlc_player_t *player)
* Select a chapter index for the current media
*
* @note A successful call will trigger the
* vlc_player_cbs.on_chaper_selection_changed event.
* vlc_player_cbs.on_chapter_selection_changed event.
*
* @see vlc_player_title.chapters
*
......@@ -3221,6 +3221,23 @@ struct vlc_player_cbs
* @param data opaque pointer set by vlc_player_AddListener()
*/
void (*on_playback_restore_queried)(vlc_player_t *player, void *data);
/**
* Called when the player will stop the current media.
*
* @note This can be called from the PLAYING state, before the
* player requests the next media, or from the STOPPING state, ie.
* when the player is stopping.
*
* @see vlc_player_SetCurrentMedia()
* @see vlc_player_Stop()
*
* @param player locked player instance
* @param prev_media media currently stopping
* @param data opaque pointer set by vlc_player_AddListener()
*/
void (*on_stopping_current_media)(vlc_player_t *player,
input_item_t *current_media, void *data);
};
/**
......
......@@ -80,34 +80,53 @@ on_current_media_changed(vlc_player_t *player, input_item_t *new_media,
}
static void
on_state_changed(vlc_player_t *player, enum vlc_player_state new_state,
void *data)
on_stopping_current_media(vlc_player_t *player, input_item_t *media,
void *data)
{
assert(media != NULL);
(void) player;
libvlc_media_player_t *mp = data;
libvlc_media_t *libmedia = media->libvlc_owner;
assert(libmedia != NULL);
libvlc_event_t event;
event.type = libvlc_MediaPlayerMediaStopping;
event.u.media_player_media_stopping.media = libmedia;
libvlc_event_send(&mp->event_manager, &event);
}
static libvlc_event_type_t
PlayerStateToLibvlcEventType(enum vlc_player_state new_state)
{
switch (new_state) {
case VLC_PLAYER_STATE_STOPPED:
event.type = libvlc_MediaPlayerStopped;
break;
return libvlc_MediaPlayerStopped;
case VLC_PLAYER_STATE_STOPPING:
event.type = libvlc_MediaPlayerStopping;
break;
return libvlc_MediaPlayerStopping;
case VLC_PLAYER_STATE_STARTED:
event.type = libvlc_MediaPlayerOpening;
break;
return libvlc_MediaPlayerOpening;
case VLC_PLAYER_STATE_PLAYING:
event.type = libvlc_MediaPlayerPlaying;
break;
return libvlc_MediaPlayerPlaying;
case VLC_PLAYER_STATE_PAUSED:
event.type = libvlc_MediaPlayerPaused;
break;
default:
vlc_assert_unreachable();
return libvlc_MediaPlayerPaused;
}
vlc_assert_unreachable();
}
static void
on_state_changed(vlc_player_t *player, enum vlc_player_state new_state,
void *data)
{
(void) player;
libvlc_media_player_t *mp = data;
libvlc_event_t event = {
.type = PlayerStateToLibvlcEventType(new_state)
};
libvlc_event_send(&mp->event_manager, &event);
}
......@@ -508,6 +527,7 @@ on_audio_device_changed(audio_output_t *aout, const char *device, void *data)
static const struct vlc_player_cbs vlc_player_cbs = {
.on_current_media_changed = on_current_media_changed,
.on_stopping_current_media = on_stopping_current_media,
.on_state_changed = on_state_changed,
.on_error_changed = on_error_changed,
.on_buffering_changed = on_buffering_changed,
......@@ -1659,9 +1679,8 @@ libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *p_mi )
return libvlc_Playing;
case VLC_PLAYER_STATE_PAUSED:
return libvlc_Paused;
default:
vlc_assert_unreachable();
}
vlc_assert_unreachable();
}
bool libvlc_media_player_is_seekable(libvlc_media_player_t *p_mi)
......
......@@ -474,39 +474,39 @@ static void cb_player_vout_changed(vlc_player_t *p_player,
}
static const struct vlc_player_cbs player_callbacks = {
cb_player_current_media_changed,
cb_player_state_changed,
cb_player_error_changed,
cb_player_buffering,
cb_player_rate_changed,
cb_player_capabilities_changed,
cb_player_position_changed,
cb_player_length_changed,
cb_player_track_list_changed,
cb_player_track_selection_changed,
cb_player_track_delay_changed,
cb_player_program_list_changed,
cb_player_program_selection_changed,
cb_player_titles_changed,
cb_player_title_selection_changed,
cb_player_chapter_selection_changed,
cb_player_teletext_menu_availability_changed,
cb_player_teletext_enabled_changed,
cb_player_teletext_page_changed,
cb_player_teletext_transparency_changed,
cb_player_category_delay_changed,
cb_player_associated_subs_fps_changed,
cb_player_renderer_changed,
cb_player_record_changed,
NULL, //cb_player_signal_changed,
cb_player_stats_changed,
cb_player_atobloop_changed,
cb_player_media_stopped_action_changed,
cb_player_item_meta_changed,
NULL, //cb_player_item_epg_changed,
NULL, //cb_player_subitems_changed,
cb_player_vout_changed,
NULL, //on_cork_changed
.on_current_media_changed = cb_player_current_media_changed,
.on_state_changed = cb_player_state_changed,
.on_error_changed = cb_player_error_changed,
.on_buffering_changed = cb_player_buffering,
.on_rate_changed = cb_player_rate_changed,
.on_capabilities_changed = cb_player_capabilities_changed,
.on_position_changed = cb_player_position_changed,
.on_length_changed = cb_player_length_changed,
.on_track_list_changed = cb_player_track_list_changed,
.on_track_selection_changed = cb_player_track_selection_changed,
.on_track_delay_changed = cb_player_track_delay_changed,
.on_program_list_changed = cb_player_program_list_changed,
.on_program_selection_changed = cb_player_program_selection_changed,
.on_titles_changed = cb_player_titles_changed,
.on_title_selection_changed = cb_player_title_selection_changed,
.on_chapter_selection_changed = cb_player_chapter_selection_changed,
.on_teletext_menu_changed = cb_player_teletext_menu_availability_changed,
.on_teletext_enabled_changed = cb_player_teletext_enabled_changed,
.on_teletext_page_changed = cb_player_teletext_page_changed,
.on_teletext_transparency_changed = cb_player_teletext_transparency_changed,
.on_category_delay_changed = cb_player_category_delay_changed,
.on_associated_subs_fps_changed = cb_player_associated_subs_fps_changed,
.on_renderer_changed = cb_player_renderer_changed,
.on_recording_changed = cb_player_record_changed,
.on_signal_changed = NULL,
.on_statistics_changed = cb_player_stats_changed,
.on_atobloop_changed = cb_player_atobloop_changed,
.on_media_stopped_action_changed = cb_player_media_stopped_action_changed,
.on_media_meta_changed = cb_player_item_meta_changed,
.on_media_epg_changed = NULL,
.on_media_subitems_changed = NULL,
.on_vout_changed = cb_player_vout_changed,
.on_cork_changed = NULL,
};
#pragma mark - video specific callback implementations
......
......@@ -530,42 +530,12 @@ void MLBookmarkModel::setMl(MediaLib* medialib)
void MLBookmarkModel::initModel()
{
static const vlc_player_cbs cbs {
&onCurrentMediaChanged,
&onPlaybackStateChanged,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
};
static const vlc_player_cbs cbs = []{
vlc_player_cbs cbs {};
cbs.on_current_media_changed = &onCurrentMediaChanged;
cbs.on_state_changed = &onPlaybackStateChanged;
return cbs;
}();
QString uri;
{
vlc_player_locker lock{ m_player };
......
......@@ -1025,7 +1025,8 @@ static const struct vlc_player_cbs player_cbs = {
on_player_subitems_changed,
on_player_vout_changed,
on_player_corks_changed,
on_player_playback_restore_queried
on_player_playback_restore_queried,
nullptr, // on_stopping_current_media: not used
};
static const vlc_player_vout_cbs player_vout_cbs = []{
......
......@@ -223,7 +223,12 @@ vlc_player_destructor_Thread(void *data)
VLC_TICK_INVALID);
vlc_player_destructor_AddStoppingInput(player, input);
/* Note: no need to hold the media here, it will be valid
* until input_Close() and the event is sent from the thread
* that will call this function. */
input_item_t *media = input_GetItem(input->thread);
input_Stop(input->thread);
vlc_player_SendEvent(player, on_stopping_current_media, media);
}
bool keep_sout = true;
......
......@@ -12,6 +12,7 @@ check_PROGRAMS = \
test_libvlc_core \
test_libvlc_equalizer \
test_libvlc_media \
test_libvlc_media_callback \
test_libvlc_media_thumbnail_argb \
test_libvlc_media_thumbnail_jpeg \
test_libvlc_media_thumbnail_png \
......@@ -123,6 +124,8 @@ test_libvlc_equalizer_SOURCES = libvlc/equalizer.c
test_libvlc_equalizer_LDADD = $(LIBVLC)
test_libvlc_media_SOURCES = libvlc/media.c
test_libvlc_media_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_libvlc_media_callback_SOURCES = libvlc/media_callback.c
test_libvlc_media_callback_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_libvlc_media_thumbnail_argb_SOURCES = libvlc/media_thumbnail.c
test_libvlc_media_thumbnail_argb_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_libvlc_media_thumbnail_argb_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_THUMB_TYPE=libvlc_picture_Argb -DTEST_REQUIRED_MODULES=\"avcodec\"
......
/*****************************************************************************
* imem.c: test for the imem libvlc code
*****************************************************************************
* Copyright (C) 2023 VideoLabs
*
* Authors: Alexandre Janniaux <ajanni@videolabs.io>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* Define a builtin module for mocked parts */
#define MODULE_NAME test_imem
#undef VLC_DYNAMIC_PLUGIN
#include "./test.h"
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_stream.h>
#include <vlc_threads.h>
#include <vlc_interrupt.h>
#include <vlc/libvlc_media.h>
#include <limits.h>
#include "media_player.h"
const char vlc_module_name[] = MODULE_STRING;
struct imem_root
{
/* root controlled semaphores */
vlc_sem_t available;
vlc_sem_t done;
vlc_sem_t opened;
vlc_sem_t read_blocking;
vlc_sem_t wait;
};
struct imem_access
{
struct imem_root *root;
vlc_sem_t wait;
};
static void AccessClose(void *opaque)
{
struct imem_access *sys = opaque;
fprintf(stderr, "test: Access: Close\n");
vlc_sem_post(&sys->root->done);
vlc_sem_post(&sys->root->available);
free(sys);
}
static int AccessOpen(void *opaque, void **datap, uint64_t *sizep)
{
(void)sizep;
struct imem_root *root = opaque;
vlc_sem_wait(&root->available);
fprintf(stderr, "test: Access: Opening new instance\n");
struct imem_access *sys = *datap = malloc(sizeof *sys);
if (sys == NULL)
return -ENOMEM;
sys->root = root;
vlc_sem_init(&sys->wait, 0);
vlc_sem_post(&sys->root->opened);
return VLC_SUCCESS;
}
static ssize_t AccessRead(void *opaque, unsigned char *buf, size_t len)
{
(void)opaque;
assert(len < SSIZE_MAX);
memset(buf, 0xff, len);
return len;
}
static void SetFlag(void *opaque)
{
bool *flag = opaque;
*flag = true;
}
static ssize_t AccessReadBlocking(void *opaque, unsigned char *buf, size_t len)
{
(void)opaque; (void)buf;
struct imem_access *sys = opaque;
assert(len < SSIZE_MAX);
vlc_sem_post(&sys->root->read_blocking);
/* The interruption is used to detect when the input has been closed. */
bool was_interrupted = false;
vlc_interrupt_register(SetFlag, &was_interrupted);
fprintf(stderr, "test: Access: read() -> blocking\n");
vlc_sem_wait(&sys->root->wait);
fprintf(stderr, "test: Access: read() -> unblocked\n");
vlc_interrupt_unregister();
//assert(was_interrupted);
/* We notify that we don't have data to read right now. */
return 0;
}
static void UnblockRead(const libvlc_event_t *event, void *opaque)
{
(void)event;
fprintf(stderr, "test: Player: Unblock read\n");
struct imem_root *imem = opaque;
vlc_sem_post(&imem->wait);
}
static struct imem_root *imem_root_New(void)
{
struct imem_root *imem = malloc(sizeof *imem);
assert(imem != NULL);
vlc_sem_init(&imem->available, 1);
vlc_sem_init(&imem->done, 0);
vlc_sem_init(&imem->wait, 0);
vlc_sem_init(&imem->opened, 0);
vlc_sem_init(&imem->read_blocking, 0);
return imem;
}
static void test_media_callback(libvlc_instance_t *vlc)
{
struct imem_root *imem = imem_root_New();
fprintf(stderr, "test: 1/ checking that media can terminate\n");
libvlc_media_t *media = libvlc_media_new_callbacks(
AccessOpen,
AccessRead,
NULL,//AccessSeek,
AccessClose,
imem);
assert(media != NULL);
libvlc_media_player_t *player = libvlc_media_player_new(vlc);
assert(player != NULL);
struct mp_event_ctx wait_play;
mp_event_ctx_init(&wait_play);
libvlc_event_manager_t *mgr = libvlc_media_player_event_manager(player);
libvlc_event_attach(mgr, libvlc_MediaPlayerOpening, mp_event_ctx_on_event, &wait_play);
struct mp_event_ctx wait_stopped;
mp_event_ctx_init(&wait_stopped);
libvlc_event_attach(mgr, libvlc_MediaPlayerStopped, mp_event_ctx_on_event, &wait_stopped);
libvlc_media_player_set_media(player, media);
libvlc_media_player_play(player);
mp_event_ctx_wait(&wait_play);
libvlc_event_detach(mgr, libvlc_MediaPlayerOpening, mp_event_ctx_on_event, &wait_play);
// TODO: Wait event Opening
libvlc_media_player_stop_async(player);
mp_event_ctx_wait(&wait_stopped);
libvlc_event_detach(mgr, libvlc_MediaPlayerStopped, mp_event_ctx_on_event, &wait_stopped);
vlc_sem_wait(&imem->done);
libvlc_media_release(media);
libvlc_media_player_release(player);
free(imem);
}
static void test_media_callback_interrupt(libvlc_instance_t *vlc)
{
struct imem_root *imem = imem_root_New();
fprintf(stderr, "test: 2/ checking that we can terminate after the input\n");
libvlc_media_player_t *player = libvlc_media_player_new(vlc);
assert(player != NULL);
libvlc_event_manager_t *mgr = libvlc_media_player_event_manager(player);
assert(mgr != NULL);
libvlc_media_t *media = libvlc_media_new_callbacks(
AccessOpen,
AccessReadBlocking,
NULL,//AccessSeek,
AccessClose,
imem);
assert(media != NULL);
struct mp_event_ctx wait_play;
struct mp_event_ctx wait_stopped;
mp_event_ctx_init(&wait_play);
mp_event_ctx_init(&wait_stopped);
libvlc_event_attach(mgr, libvlc_MediaPlayerMediaStopping, UnblockRead, imem);
fprintf(stderr, "test: set initial media\n");
libvlc_media_player_set_media(player, media);
libvlc_media_player_play(player);
/* We want to be sure that the media has been opened. */
vlc_sem_wait(&imem->opened);
vlc_sem_wait(&imem->read_blocking);
libvlc_media_release(media);
media = libvlc_media_new_callbacks(
AccessOpen,
AccessReadBlocking,
NULL,
AccessClose,
imem);
assert(media != NULL);
fprintf(stderr, "test: changing to new media\n");
libvlc_media_player_set_media(player, media);
fprintf(stderr, "test: waiting for the new media\n");
/* Semaphore notifying that the first access has been closed. */
vlc_sem_wait(&imem->done);
assert(vlc_sem_trywait(&imem->read_blocking) == EAGAIN);
vlc_sem_wait(&imem->opened);
vlc_sem_wait(&imem->read_blocking);
fprintf(stderr, "test: checking that we get the media stopping event\n");
libvlc_media_player_stop_async(player);
vlc_sem_wait(&imem->done);
libvlc_media_release(media);
libvlc_media_player_release(player);
free(imem);
}
int main( int argc, char **argv )
{
(void)argc; (void)argv;
test_init();
const char * const vlc_argv[] = {
"-vvv", "--vout=dummy", "--aout=dummy", "--text-renderer=dummy",
"--demux="MODULE_STRING,
};
libvlc_instance_t *vlc = libvlc_new(ARRAY_SIZE(vlc_argv), vlc_argv);
assert(vlc != NULL);
test_media_callback(vlc);
test_media_callback_interrupt(vlc);
libvlc_release(vlc);
return 0;
}
static int DemuxDemux(demux_t *demux)
{
(void)demux;
return VLC_SUCCESS;
}
static void DemuxClose(vlc_object_t *obj)
{
(void)obj;
}
static int DemuxOpen(vlc_object_t *obj)
{
demux_t *demux = (demux_t *)obj;
static const struct vlc_stream_operations ops =
{
.demux.demux = DemuxDemux,
};
demux->ops = &ops;
return VLC_SUCCESS;
}
vlc_module_begin()
set_capability("demux", 0)
set_callbacks(DemuxOpen, DemuxClose)
vlc_module_end()
VLC_EXPORT const vlc_plugin_cb vlc_static_modules[] = {
VLC_SYMBOL(vlc_entry),
NULL
};
......@@ -20,6 +20,13 @@ vlc_tests += {
'module_depends': vlc_plugins_targets.keys()
}
vlc_tests += {
'name' : 'test_libvlc_media_callback',
'sources' : files('media_callback.c'),
'link_with' : [libvlc, libvlccore],
'module_depends': ['imem'],
}
test_libvlc_media_thumbnail_modules = ['demux_mock']
if avcodec_dep.found()
test_libvlc_media_thumbnail_modules += ['avcodec']
......
......@@ -36,6 +36,19 @@
# define TELETEXT_DECODER ""
#endif
static const char *state_to_string(enum vlc_player_state state)
{
switch (state)
{
case VLC_PLAYER_STATE_STOPPED: return "stopped";
case VLC_PLAYER_STATE_STARTED: return "started";
case VLC_PLAYER_STATE_PLAYING: return "playing";
case VLC_PLAYER_STATE_PAUSED: return "paused";
case VLC_PLAYER_STATE_STOPPING: return "stopping";
}
vlc_assert_unreachable();
};
struct report_capabilities
{
int old_caps;
......@@ -598,13 +611,38 @@ wait_state(struct ctx *ctx, enum vlc_player_state state)
assert(VEC_LAST(vec) == state); \
} while(0)
static inline void state_list_dump(vec_on_state_changed *vec)
{
fprintf(stderr, "Dumping state:\n");
for (size_t i = 0; i < vec->size; ++i)
fprintf(stderr, "state[%zu] = %s\n",
i, state_to_string(vec->data[i]));
}
static inline bool
state_equal(vec_on_state_changed *vec, int location, enum vlc_player_state state)
{
assert(location < (int)vec->size && -location < (int)(vec->size + 1));
size_t index = location < 0 ? vec->size + location : (size_t)location;
const char *str_state_vec = state_to_string(vec->data[index]);
const char *str_state_check = state_to_string(state);
fprintf(stderr, "Checking state[%d] == '%s', is '%s'\n",
location, str_state_vec, str_state_check);
return vec->data[index] == state;
}
#define state_equal(ctx, index, state) \
(state_equal)(&ctx->report.on_state_changed, index, state)
#define assert_normal_state(ctx) do { \
vec_on_state_changed *vec = &ctx->report.on_state_changed; \
state_list_dump(vec); \
assert(vec->size >= 4); \
assert(vec->data[vec->size - 4] == VLC_PLAYER_STATE_STARTED); \
assert(vec->data[vec->size - 3] == VLC_PLAYER_STATE_PLAYING); \
assert(vec->data[vec->size - 2] == VLC_PLAYER_STATE_STOPPING); \
assert(vec->data[vec->size - 1] == VLC_PLAYER_STATE_STOPPED); \
assert(state_equal(ctx, -4, VLC_PLAYER_STATE_STARTED)); \
assert(state_equal(ctx, -3, VLC_PLAYER_STATE_PLAYING)); \
assert(state_equal(ctx, -2, VLC_PLAYER_STATE_STOPPING)); \
assert(state_equal(ctx, -1, VLC_PLAYER_STATE_STOPPED)); \
} while(0)
static void
......