tdf#124230 pptx: make exported animation sound work.

1. Fix target path for the embedded media. Move the media
file to /ppt/media ( was /media ) and use the relative path
../media for the target path when adding the relation. This
is necessary for MSO to play the sound.

2. Write timenode id for the start or end conditions if the
animation node for the begin event or the end event is
available. Events like BEGIN or END has to refer a timenode.
Without specifying referred timenode in start and end
condition, Impress will not activate the audio node after
importing the document.

Change-Id: I6027be2e836e2f86061e401c8af806b2b1993a49
Reviewed-on: https://gerrit.libreoffice.org/71427
Tested-by: Jenkins
Reviewed-by: Mark Hung <marklh9@gmail.com>
diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx
index 7c27e0a..66c6154 100644
--- a/sd/qa/unit/export-tests-ooxml2.cxx
+++ b/sd/qa/unit/export-tests-ooxml2.cxx
@@ -2087,31 +2087,38 @@
        = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/tdf44223.pptx"), PPTX);
    xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);

    std::shared_ptr<SvStream> const pStream1(parseExportStream(tempFile, "media/audio1.wav"));
    std::shared_ptr<SvStream> const pStream1(parseExportStream(tempFile, "ppt/media/audio1.wav"));
    CPPUNIT_ASSERT_EQUAL(sal_uInt64(11140), pStream1->remainingSize());

    std::shared_ptr<SvStream> const pStream2(parseExportStream(tempFile, "media/audio2.wav"));
    std::shared_ptr<SvStream> const pStream2(parseExportStream(tempFile, "ppt/media/audio2.wav"));
    CPPUNIT_ASSERT_EQUAL(sal_uInt64(28074), pStream2->remainingSize());

    xmlDocPtr pXmlContentType = parseExport(tempFile, "[Content_Types].xml");
    assertXPath(pXmlContentType,
                "/ContentType:Types/ContentType:Override[@PartName='/media/audio1.wav']",
                "/ContentType:Types/ContentType:Override[@PartName='/ppt/media/audio1.wav']",
                "ContentType",
                "audio/x-wav");

    assertXPath(pXmlContentType,
                "/ContentType:Types/ContentType:Override[@PartName='/media/audio2.wav']",
                "/ContentType:Types/ContentType:Override[@PartName='/ppt/media/audio2.wav']",
                "ContentType",
                "audio/x-wav");

    xmlDocPtr pDoc1 = parseExport(tempFile, "ppt/slides/slide1.xml");

    // Start condition: 0s after timenode id 5 begins.
    assertXPath(pDoc1 , "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "evt", "begin");
    assertXPath(pDoc1 , "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond", "delay", "0");
    assertXPath(pDoc1 , "//p:audio/p:cMediaNode/p:cTn/p:stCondLst/p:cond/p:tn", "val", "5");

    xmlDocPtr pDoc2 = parseExport(tempFile, "ppt/slides/slide2.xml");
    assertXPath(pDoc2 , "//p:transition/p:sndAc/p:stSnd/p:snd[@r:embed]", 2);

    xmlDocPtr pRels1 = parseExport(tempFile, "ppt/slides/_rels/slide1.xml.rels");
    assertXPath(pRels1, "//rels:Relationship[@Id='rId1']", "Type",
            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio");
    assertXPath(pRels1, "//rels:Relationship[@Id='rId1']", "Target", "../media/audio1.wav");

    xDocShRef->DoClose();
}

diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx
index a8c062b..da5223c 100644
--- a/sd/source/filter/eppt/pptx-animations.cxx
+++ b/sd/source/filter/eppt/pptx-animations.cxx
@@ -59,6 +59,7 @@
#include "pptexanimations.hxx"
#include "pptx-animations.hxx"
#include "../ppt/pptanimations.hxx"
#include <comphelper/stl_types.hxx>

using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::container;
@@ -569,6 +570,7 @@
    OString msDelay;
    const char* mpEvent;
    Reference<XShape> mxShape;
    Reference<XAnimationNode> mxNode;

    Cond(const Any& rAny, bool bIsMainSeqChild);

@@ -596,7 +598,8 @@
        else
        {
            mpEvent = convertEventTrigger(aEvent.Trigger);
            aEvent.Source >>= mxShape;
            if (!(aEvent.Source >>= mxShape))
                aEvent.Source >>= mxNode;

            if (aEvent.Offset >>= fDelay)
                bHasFDelay = true;
@@ -632,6 +635,11 @@
    const FSHelperPtr& mpFS;
    const NodeContext* mpContext;

    std::map<Reference<XAnimationNode>, sal_Int32, ::comphelper::OInterfaceCompare<XAnimationNode>>
        maAnimationNodeIdMap;
    sal_Int32 GetNextAnimationNodeId(const Reference<XAnimationNode>& rNode);
    sal_Int32 GetAnimationNodeId(const Reference<XAnimationNode>& rNode);

public:
    PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS);
    void WriteAnimations(const Reference<XDrawPage>& rXDrawPage);
@@ -751,6 +759,7 @@

void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond)
{
    sal_Int32 nId = -1;
    if (rCond.mpEvent)
    {
        if (rCond.mxShape.is())
@@ -760,6 +769,13 @@
            WriteAnimationTarget(makeAny(rCond.mxShape));
            mpFS->endElementNS(XML_p, XML_cond);
        }
        else if (rCond.mxNode.is() && (nId = GetAnimationNodeId(rCond.mxNode)) != -1)
        {
            mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
                                 rCond.mpEvent);
            mpFS->singleElementNS(XML_p, XML_tn, XML_val, OString::number(nId));
            mpFS->endElementNS(XML_p, XML_cond);
        }
        else
        {
            mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
@@ -1041,8 +1057,7 @@
    bool bAutoReverse = rXNode->getAutoReverse();

    mpFS->startElementNS(
        XML_p, XML_cTn, XML_id, OString::number(mrPowerPointExport.GetNextAnimationNodeID()),
        XML_dur,
        XML_p, XML_cTn, XML_id, OString::number(GetNextAnimationNodeId(rXNode)), XML_dur,
        fDuration != 0 ? OString::number(static_cast<sal_Int32>(fDuration * 1000.0)).getStr()
                       : pDuration,
        XML_autoRev, bAutoReverse ? "1" : nullptr, XML_restart, pRestart, XML_nodeType, pNodeType,
@@ -1261,6 +1276,24 @@
    }
}

sal_Int32 PPTXAnimationExport::GetNextAnimationNodeId(const Reference<XAnimationNode>& xNode)
{
    sal_Int32 nId = mrPowerPointExport.GetNextAnimationNodeID();
    maAnimationNodeIdMap[xNode] = nId;
    return nId;
}

sal_Int32 PPTXAnimationExport::GetAnimationNodeId(const Reference<XAnimationNode>& xNode)
{
    sal_Int32 nId = -1;
    const auto& aIter = maAnimationNodeIdMap.find(xNode);
    if (aIter != maAnimationNodeIdMap.end())
    {
        nId = aIter->second;
    }
    return nId;
}

NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild,
                         bool bIsIterateChild)
    : mxNode(xNode)
diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx
index abe09c4..a7d51cf 100644
--- a/sd/source/filter/eppt/pptx-epptooxml.cxx
+++ b/sd/source/filter/eppt/pptx-epptooxml.cxx
@@ -1971,14 +1971,15 @@
    int nLastSlash = sUrl.lastIndexOf('/');
    sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0);

    OUString sPath = OUStringBuffer().append("/media/")
    OUString sPath = OUStringBuffer().append("../media/")
                                     .append(sName)
                                     .makeStringAndClear();

    sRelId = addRelation(pFS->getOutputStream(),
                        oox::getRelationship(Relationship::AUDIO), sPath);

    uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath, "audio/x-wav");
    uno::Reference<io::XOutputStream> xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, "/ppt"),
            "audio/x-wav");

    comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream);
}