c++ Live.55 फ़्रेमयुक्त स्रोत को कैसे लिखने के लिए मुझे एच.264 लाइव स्ट्रीम करने के लिए अनुमति दें




ffmpeg h.264 (2)

मैं एक क्लास लिखने का प्रयास कर रहा हूं जो Live555 में फ़्रेमयुक्त स्रोत से प्राप्त होता है जो मुझे अपने डी 3 डी 9 एप्लिकेशन से लाइव डेटा स्ट्रीम करने के लिए एक MP4 या इसी तरह की अनुमति देगा।

मैं जो प्रत्येक फ्रेम करता हूं वह बैकबफर को सिस्टम मेमोरी में एक बनावट के रूप में ले लेता है, फिर उसे आरजीबी -> वाईयूवी 420 पी से परिवर्तित कर देता है, फिर इसे एक्स 264 का उपयोग करके एन्कोड करें, फिर एनएएल पैकेट्स को लाइव555 पर आदर्श रूप से पास करें। मैंने H264FramedSource नामक एक वर्ग बनाया है जो मूल रूप से DeviceSource फ़ाइल को कॉपी करके फ़्रेमयुक्त स्रोत से प्राप्त किया गया था। इनपुट इनपुट फ़ाइल होने के बजाय, मैंने इसे एक एनएएल पैकेट बना दिया है जो मैं प्रत्येक फ्रेम को अपडेट करता हूं

मैं काफी codecs और स्ट्रीमिंग के लिए नया हूँ, तो मैं सब कुछ पूरी तरह से गलत कर रहा हो सकता है प्रत्येक doGetNextFrame () में मुझे एनएएल पैकेट को पकड़ना चाहिए और ऐसा कुछ करना चाहिए

memcpy(fTo, nal->p_payload, nal->i_payload)

मुझे लगता है कि पेलोड मेरे फ्रेम डेटा बाइट्स में है? अगर किसी के पास एक ऐसा वर्ग का उदाहरण है जिसे वे फ़्रेमयुक्त स्रोत से प्राप्त करते हैं, जो कम से कम मैं क्या करने की कोशिश कर रहा हूं, जो मुझे देखने के लिए अच्छा लगेगा, यह मेरे लिए सब कुछ है और यह पता लगाने में थोड़ा मुश्किल है कि क्या हो रहा है। Live555 का दस्तावेज़ीकरण बहुत ही कोड है जो बिल्कुल मेरे लिए यह समझना आसान नहीं बनाता है।


वितरितफ़्रेम विधि में इसकी शुरुआत निम्न चेक की कमी है:

if (!isCurrentlyAwaitingData()) return;    

देखिए DeviceSource.cpp लाइव में


ठीक है, मैं अंत में इस पर खर्च करने के लिए कुछ समय मिला और यह काम कर रहा है! मुझे यकीन है कि ऐसे अन्य लोग हैं जो यह जानने के लिए भीख मांग रहे होंगे कि यह कैसे करना है, यहां यह है।

प्रत्येक फ़्रेम को लेने, एन्कोड करने और स्ट्रीमिंग के लिए तैयार करने के लिए आपको अपने स्वयं के फ़्रेमयुक्त स्रोत की आवश्यकता होगी, मैं इसके लिए जल्द ही कुछ स्रोत कोड प्रदान करूंगा।

अनिवार्य रूप से अपने फ़्रेमयुक्त स्रोत को H264VideoStreamDiscreteFramer में फेंक दें, फिर उसे H264RTPSink में फेंक दें कुछ इस तरह

scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);   

framedSource = H264FramedSource::createNew(*env, 0,0);

h264VideoStreamDiscreteFramer 
= H264VideoStreamDiscreteFramer::createNew(*env, framedSource);

// initialise the RTP Sink stuff here, look at 
// testH264VideoStreamer.cpp to find out how

videoSink->startPlaying(*h264VideoStreamDiscreteFramer, NULL, videoSink);

env->taskScheduler().doEventLoop();

अब अपने मुख्य रेंडर लूप में, अपने बैकबफर को फेंक दें जिसे आपने सिस्टम मेमोरी में अपने फ़्रेमयुक्त स्रोत में सहेज लिया है ताकि इसे एन्कोड किया जा सके। इस एन्कोडिंग चीजों को कैसे सेटअप करें इस उत्तर की जांच करने के बारे में अधिक जानकारी के लिए, एक श्रृंखला x264 सी एपीआई का उपयोग करते हुए एच 264 में छवियाँ?

मेरा क्रियान्वयन एक हैकी अवस्था में बहुत अधिक है और अभी तक इसे अनुकूलित नहीं किया जा सकता है, मेरे डी 3 डी अनुप्रयोग एन्कोडिंग, आउच के कारण लगभग 15 एफपीएस पर चलता है, इसलिए मुझे इस पर गौर करना होगा। लेकिन सभी इरादों और प्रयोजनों के लिए इस स्टैक ऑवरफ्लो प्रश्न का उत्तर दिया गया है क्योंकि मैं इसे स्ट्रीम करने के बाद ज्यादातर था। मुझे उम्मीद है कि यह अन्य लोगों की मदद करता है

मेरे फ़्रेमयुक्त स्रोत के लिए यह कुछ ऐसा दिखता है

concurrent_queue<x264_nal_t> m_queue;
SwsContext* convertCtx;
x264_param_t param;
x264_t* encoder;
x264_picture_t pic_in, pic_out;


EventTriggerId H264FramedSource::eventTriggerId = 0;
unsigned H264FramedSource::FrameSize = 0;
unsigned H264FramedSource::referenceCount = 0;

int W = 720;
int H = 960;

H264FramedSource* H264FramedSource::createNew(UsageEnvironment& env,
                                              unsigned preferredFrameSize, 
                                              unsigned playTimePerFrame) 
{
        return new H264FramedSource(env, preferredFrameSize, playTimePerFrame);
}

H264FramedSource::H264FramedSource(UsageEnvironment& env,
                                   unsigned preferredFrameSize, 
                                   unsigned playTimePerFrame)
    : FramedSource(env),
    fPreferredFrameSize(fMaxSize),
    fPlayTimePerFrame(playTimePerFrame),
    fLastPlayTime(0),
    fCurIndex(0)
{
        if (referenceCount == 0) 
        {

        }
        ++referenceCount;

        x264_param_default_preset(&param, "veryfast", "zerolatency");
        param.i_threads = 1;
        param.i_width = 720;
        param.i_height = 960;
        param.i_fps_num = 60;
        param.i_fps_den = 1;
        // Intra refres:
        param.i_keyint_max = 60;
        param.b_intra_refresh = 1;
        //Rate control:
        param.rc.i_rc_method = X264_RC_CRF;
        param.rc.f_rf_constant = 25;
        param.rc.f_rf_constant_max = 35;
        param.i_sps_id = 7;
        //For streaming:
        param.b_repeat_headers = 1;
        param.b_annexb = 1;
        x264_param_apply_profile(&param, "baseline");


        encoder = x264_encoder_open(&param);
        pic_in.i_type            = X264_TYPE_AUTO;   
        pic_in.i_qpplus1         = 0;
        pic_in.img.i_csp         = X264_CSP_I420;   
        pic_in.img.i_plane       = 3;


        x264_picture_alloc(&pic_in, X264_CSP_I420, 720, 920);

        convertCtx = sws_getContext(720, 960, PIX_FMT_RGB24, 720, 760, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);


        if (eventTriggerId == 0) 
        {
            eventTriggerId = envir().taskScheduler().createEventTrigger(deliverFrame0);
        }
}

H264FramedSource::~H264FramedSource() 
{
    --referenceCount;
    if (referenceCount == 0) 
    {
        // Reclaim our 'event trigger'
        envir().taskScheduler().deleteEventTrigger(eventTriggerId);
        eventTriggerId = 0;
    }
}

void H264FramedSource::AddToBuffer(uint8_t* buf, int surfaceSizeInBytes)
{
    uint8_t* surfaceData = (new uint8_t[surfaceSizeInBytes]);

    memcpy(surfaceData, buf, surfaceSizeInBytes);

    int srcstride = W*3;
    sws_scale(convertCtx, &surfaceData, &srcstride,0, H, pic_in.img.plane, pic_in.img.i_stride);
    x264_nal_t* nals = NULL;
    int i_nals = 0;
    int frame_size = -1;


    frame_size = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out);

    static bool finished = false;

    if (frame_size >= 0)
    {
        static bool alreadydone = false;
        if(!alreadydone)
        {

            x264_encoder_headers(encoder, &nals, &i_nals);
            alreadydone = true;
        }
        for(int i = 0; i < i_nals; ++i)
        {
            m_queue.push(nals[i]);
        }   
    }
    delete [] surfaceData;
    surfaceData = NULL;

    envir().taskScheduler().triggerEvent(eventTriggerId, this);
}

void H264FramedSource::doGetNextFrame() 
{
    deliverFrame();
}

void H264FramedSource::deliverFrame0(void* clientData) 
{
    ((H264FramedSource*)clientData)->deliverFrame();
}

void H264FramedSource::deliverFrame() 
{
    x264_nal_t nalToDeliver;

    if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
        if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
            // This is the first frame, so use the current time:
            gettimeofday(&fPresentationTime, NULL);
        } else {
            // Increment by the play time of the previous data:
            unsigned uSeconds   = fPresentationTime.tv_usec + fLastPlayTime;
            fPresentationTime.tv_sec += uSeconds/1000000;
            fPresentationTime.tv_usec = uSeconds%1000000;
        }

        // Remember the play time of this data:
        fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
        fDurationInMicroseconds = fLastPlayTime;
    } else {
        // We don't know a specific play time duration for this data,
        // so just record the current time as being the 'presentation time':
        gettimeofday(&fPresentationTime, NULL);
    }

    if(!m_queue.empty())
    {
        m_queue.wait_and_pop(nalToDeliver);

        uint8_t* newFrameDataStart = (uint8_t*)0xD15EA5E;

        newFrameDataStart = (uint8_t*)(nalToDeliver.p_payload);
        unsigned newFrameSize = nalToDeliver.i_payload;

        // Deliver the data here:
        if (newFrameSize > fMaxSize) {
            fFrameSize = fMaxSize;
            fNumTruncatedBytes = newFrameSize - fMaxSize;
        }
        else {
            fFrameSize = newFrameSize;
        }

        memcpy(fTo, nalToDeliver.p_payload, nalToDeliver.i_payload);

        FramedSource::afterGetting(this);
    }
}

ओह और उन लोगों के लिए जो मेरी समवर्ती कतार है, यहाँ यह है, और यह शानदार ढंग से काम करता है http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition- variables.html

आनंद लें और शुभकामनाएं!





live555