2015-08-12 6 views
5

सिंक करने के लिए मैं mov मल्टीमीडिया कंटेनर में H264 इनकोडिंग डेटा और G711 पीसीएम डेटा mux करने के लिए कोशिश कर रहा हूँ। मैं एन्कोडेड डेटा से AVPacket बनाने रहा हूँ और शुरू में वीडियो/ऑडियो फ्रेम के सार्वजनिक टेलीफोन और डीटीएस मूल्य AV_NOPTS_VALUE के बराबर है। तो मैंने वर्तमान समय की जानकारी का उपयोग कर डीटीएस की गणना की। मेरे कोड -कंप्यूट सार्वजनिक टेलीफोन और डीटीएस सही ढंग से ऑडियो और वीडियो ffmpeg सी ++

bool AudioVideoRecorder::WriteVideo(const unsigned char *pData, size_t iDataSize, bool const bIFrame) { 
    ..................................... 
    ..................................... 
    ..................................... 
    AVPacket pkt = {0}; 
    av_init_packet(&pkt); 
    int64_t dts = av_gettime(); 
    dts = av_rescale_q(dts, (AVRational){1, 1000000}, m_pVideoStream->time_base); 
    int duration = 90000/VIDEO_FRAME_RATE; 
    if(m_prevVideoDts > 0LL) { 
     duration = dts - m_prevVideoDts; 
    } 
    m_prevVideoDts = dts; 

    pkt.pts = AV_NOPTS_VALUE; 
    pkt.dts = m_currVideoDts; 
    m_currVideoDts += duration; 
    pkt.duration = duration; 
    if(bIFrame) { 
     pkt.flags |= AV_PKT_FLAG_KEY; 
    } 
    pkt.stream_index = m_pVideoStream->index; 
    pkt.data = (uint8_t*) pData; 
    pkt.size = iDataSize; 

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt); 

    if(ret < 0) { 
     LogErr("Writing video frame failed."); 
     return false; 
    } 

    Log("Writing video frame done."); 

    av_free_packet(&pkt); 
    return true; 
} 

bool AudioVideoRecorder::WriteAudio(const unsigned char *pEncodedData, size_t iDataSize) { 
    ................................. 
    ................................. 
    ................................. 
    AVPacket pkt = {0}; 
    av_init_packet(&pkt); 

    int64_t dts = av_gettime(); 
    dts = av_rescale_q(dts, (AVRational){1, 1000000}, (AVRational){1, 90000}); 
    int duration = AUDIO_STREAM_DURATION; // 20 
    if(m_prevAudioDts > 0LL) { 
     duration = dts - m_prevAudioDts; 
    } 
    m_prevAudioDts = dts; 
    pkt.pts = AV_NOPTS_VALUE; 
    pkt.dts = m_currAudioDts; 
    m_currAudioDts += duration; 
    pkt.duration = duration; 

    pkt.stream_index = m_pAudioStream->index; 
    pkt.flags |= AV_PKT_FLAG_KEY; 
    pkt.data = (uint8_t*) pEncodedData; 
    pkt.size = iDataSize; 

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt); 
    if(ret < 0) { 
     LogErr("Writing audio frame failed: %d", ret); 
     return false; 
    } 

    Log("Writing audio frame done."); 

    av_free_packet(&pkt); 
    return true; 
} 

और मैं इस तरह धारा जोड़ा -

AVStream* AudioVideoRecorder::AddMediaStream(enum AVCodecID codecID) { 
    ................................ 
    ................................. 
    pStream = avformat_new_stream(m_pFormatCtx, codec); 
    if (!pStream) { 
     LogErr("Could not allocate stream."); 
     return NULL; 
    } 
    pStream->id = m_pFormatCtx->nb_streams - 1; 
    pCodecCtx = pStream->codec; 
    pCodecCtx->codec_id = codecID; 

    switch(codec->type) { 
    case AVMEDIA_TYPE_VIDEO: 
     pCodecCtx->bit_rate = VIDEO_BIT_RATE; 
     pCodecCtx->width = PICTURE_WIDTH; 
     pCodecCtx->height = PICTURE_HEIGHT; 
     pStream->time_base = (AVRational){1, 90000}; 
     pStream->avg_frame_rate = (AVRational){90000, 1}; 
     pStream->r_frame_rate = (AVRational){90000, 1}; // though the frame rate is variable and around 15 fps 
     pCodecCtx->pix_fmt = STREAM_PIX_FMT; 
     m_pVideoStream = pStream; 
     break; 

    case AVMEDIA_TYPE_AUDIO: 
     pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; 
     pCodecCtx->bit_rate = AUDIO_BIT_RATE; 
     pCodecCtx->sample_rate = AUDIO_SAMPLE_RATE; 
     pCodecCtx->channels = 1; 
     m_pAudioStream = pStream; 
     break; 

    default: 
     break; 
    } 

    /* Some formats want stream headers to be separate. */ 
    if (m_pOutputFmt->flags & AVFMT_GLOBALHEADER) 
     m_pFormatCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; 

    return pStream; 
} 

इस गणना के साथ कई समस्याएं हैं:

  1. वीडियो laggy है और साथ तेजी से ऑडियो से पीछे है पहर।

  2. मान लीजिए, एक ऑडियो फ्रेम प्राप्त होता है (WriteAudio(..)) थोड़ा हाल ही में 3 सेकंड की तरह, तो देर से फ्रेम 3 सेकंड देरी के साथ खेलना शुरू किया जाना चाहिए, लेकिन ऐसा नहीं है। देरी फ्रेम पिछले फ्रेम के साथ लगातार खेला जाता है।

  3. कभी कभी मैं के लिए रिकॉर्ड किया ~ 40 सेकंड लेकिन फाइल अवधि ज्यादा 2 मिनट की तरह है, लेकिन ऑडियो/वीडियो 40 सेकंड की तरह केवल कुछ ही क्षणों खेला जाता है और शेष फ़ाइल के लिए कुछ भी नहीं होता है और seekbar एन 40 के तुरंत बाद सेकंड में कूदता (वीएलसी में परीक्षण)।

संपादित करें:

रोनाल्ड एस Bultje के सुझाव के अनुसार

, मैं समझता हूँ क्या है:

m_pAudioStream->time_base = (AVRational){1, 9000}; // actually no need to set as 9000 is already default value for audio as you said 
m_pVideoStream->time_base = (AVRational){1, 9000}; 

के रूप में स्थापित किया जाना चाहिए अब ऑडियो और वीडियो दोनों धाराओं एक ही समय आधार में अब कर रहे हैं इकाइयों।

और वीडियो के लिए:

................... 
................... 

int64_t dts = av_gettime(); // get current time in microseconds 
dts *= 9000; 
dts /= 1000000; // 1 second = 10^6 microseconds 
pkt.pts = AV_NOPTS_VALUE; // is it okay? 
pkt.dts = dts; 
// and no need to set pkt.duration, right? 

और ऑडियो के लिए: (वास्तव में वीडियो के रूप में ही, है ना?)

................... 
................... 

int64_t dts = av_gettime(); // get current time in microseconds 
dts *= 9000; 
dts /= 1000000; // 1 second = 10^6 microseconds 
pkt.pts = AV_NOPTS_VALUE; // is it okay? 
pkt.dts = dts; 
// and no need to set pkt.duration, right? 

और मुझे लगता है कि वे अब कर रहे हैं की तरह बांटने के समान currDts, है ना? अगर मैं कहीं भी गलत हूं या कुछ खो रहा हूं तो कृपया मुझे सही करें।

इसके अलावा, अगर मैं (AVRational){1, frameRate} के रूप में वीडियो स्ट्रीम टाइम बेस का उपयोग करना चाहता हूं और (AVRational){1, sampleRate} के रूप में ऑडियो स्ट्रीम टाइम बेस, सही कोड कैसा दिखना चाहिए?

संपादित 2.0:

m_pAudioStream->time_base = (AVRational){1, VIDEO_FRAME_RATE}; 
m_pVideoStream->time_base = (AVRational){1, VIDEO_FRAME_RATE}; 

और

bool AudioVideoRecorder::WriteAudio(const unsigned char *pEncodedData, size_t iDataSize) { 
    ........................... 
    ...................... 
    AVPacket pkt = {0}; 
    av_init_packet(&pkt); 

    int64_t dts = av_gettime()/1000; // convert into millisecond 
    dts = dts * VIDEO_FRAME_RATE; 
    if(m_dtsOffset < 0) { 
     m_dtsOffset = dts; 
    } 

    pkt.pts = AV_NOPTS_VALUE; 
    pkt.dts = (dts - m_dtsOffset); 

    pkt.stream_index = m_pAudioStream->index; 
    pkt.flags |= AV_PKT_FLAG_KEY; 
    pkt.data = (uint8_t*) pEncodedData; 
    pkt.size = iDataSize; 

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt); 
    if(ret < 0) { 
     LogErr("Writing audio frame failed: %d", ret); 
     return false; 
    } 

    Log("Writing audio frame done."); 

    av_free_packet(&pkt); 
    return true; 
} 

bool AudioVideoRecorder::WriteVideo(const unsigned char *pData, size_t iDataSize, bool const bIFrame) { 
    ........................................ 
    ................................. 
    AVPacket pkt = {0}; 
    av_init_packet(&pkt); 
    int64_t dts = av_gettime()/1000; 
    dts = dts * VIDEO_FRAME_RATE; 
    if(m_dtsOffset < 0) { 
     m_dtsOffset = dts; 
    } 
    pkt.pts = AV_NOPTS_VALUE; 
    pkt.dts = (dts - m_dtsOffset); 

    if(bIFrame) { 
     pkt.flags |= AV_PKT_FLAG_KEY; 
    } 
    pkt.stream_index = m_pVideoStream->index; 
    pkt.data = (uint8_t*) pData; 
    pkt.size = iDataSize; 

    int ret = av_interleaved_write_frame(m_pFormatCtx, &pkt); 

    if(ret < 0) { 
     LogErr("Writing video frame failed."); 
     return false; 
    } 

    Log("Writing video frame done."); 

    av_free_packet(&pkt); 
    return true; 
} 

अंतिम परिवर्तन ठीक है? वीडियो और ऑडियो सिंक लगता है। केवल समस्या यह है - ऑडियो देरी परवाह किए बिना पैकेट देरी में पहुंचे बिना खेला जाता है। की तरह -

पैकेट आगमन: 1 2 3 4 ... (फिर अगले फ्रेम 3 सेकंड के बाद पहुंचे) ..5

ऑडियो खेला: 1 2 3 4 (कोई देरी) 5

संपादित 3.0:

ध्यान केंद्रित किया ऑडियो नमूना डेटा:

AVFrame* pSilentData; 
pSilentData = av_frame_alloc(); 
memset(&pSilentData->data[0], 0, iDataSize); 

pkt.data = (uint8_t*) pSilentData; 
pkt.size = iDataSize; 

av_freep(&pSilentData->data[0]); 
av_frame_free(&pSilentData); 

यह ठीक है? लेकिन फ़ाइल कंटेनर में इसे लिखने के बाद, मीडिया खेलने के दौरान डॉट डॉट शोर हैं। समस्या क्या है?

संपादित 4.0:

खैर, For µ-Law audio the zero value is represented as 0xff। तो -

memset(&pSilentData->data[0], 0xff, iDataSize); 

मेरी समस्या का समाधान।

+1

AFAIK ऑडियो में आमतौर पर एक डीटीएस नहीं होना चाहिए, केवल एक अंक। यदि स्रोत फ्रेम में डीटी भी है तो वीडियो में केवल एक डीटीएस होना चाहिए। (यदि इसे बी-फ्रेम द्वारा संदर्भ के रूप में उपयोग किया जाता है)। – wimh

+0

ऑडियो और वीडियो के लिए आपका टाइमबेस आपके नमूना आवृत्ति से मेल खाना चाहिए। उदाहरण के लिए यदि आप प्रति सेकंड 25 फ्रेम पर अपने वीडियो का नमूना दे रहे हैं तो पुनर्विक्रय 1/25 से 1/90000 तक है। मुझे यकीन नहीं है कि आप कहीं भी 100000 का उपयोग क्यों कर रहे हैं। – sipwiz

+0

@sipwiz क्या आप EDIT 2.0 की जांच कर सकते हैं? –

उत्तर

2

टाइमस्टैम्प (जैसे dts) AVStream.time_base इकाइयों में होना चाहिए। आप 1/90000 के वीडियो टाइमबेस और एक डिफ़ॉल्ट ऑडियो टाइमबेस (1/9000) का अनुरोध कर रहे हैं, लेकिन आप dts मान लिखने के लिए 1/100000 का टाइमबेस का उपयोग कर रहे हैं। मुझे यह भी यकीन नहीं है कि क्या यह गारंटी है कि अनुरोधित टाइमबेस को हेडर लेखन के दौरान बनाए रखा जाता है, तो आपका मक्सर मूल्य बदल सकता है और उम्मीद करता है कि आप नए मानों से निपटें।

तो कोड इस तरह:

int64_t dts = av_gettime(); 
dts = av_rescale_q(dts, (AVRational){1, 1000000}, (AVRational){1, 90000}); 
int duration = AUDIO_STREAM_DURATION; // 20 
if(m_prevAudioDts > 0LL) { 
    duration = dts - m_prevAudioDts; 
} 

काम नहीं करेंगे। उस ऑडिस्ट्रीम के टाइमबेस का उपयोग करने वाले किसी चीज़ को बदलें, और अवधि निर्धारित न करें जबतक कि आप यह नहीं जानते कि आप क्या कर रहे हैं। (वीडियो के लिए एक ही।)

m_prevAudioDts = dts; 
pkt.pts = AV_NOPTS_VALUE; 
pkt.dts = m_currAudioDts; 
m_currAudioDts += duration; 
pkt.duration = duration; 

इस डरावने लग रहा है, विशेष रूप से वीडियो को समान रूप से कोड के साथ संयुक्त। यहां समस्या यह है कि धाराओं के बीच अंतर-पैकेट विलंब के बावजूद, दोनों के लिए पहले पैकेट में शून्य का टाइमस्टैम्प होगा। आपको सभी धाराओं के बीच साझा किए गए एक अभिभावक currDts की आवश्यकता है, अन्यथा आपकी स्ट्रीम हमेशा समन्वयित हो जाएगी।

[संपादित करें]

तो, अपने संपादित के बारे में, यदि आप ऑडियो अंतराल है, मैं आप की खाई की अवधि के लिए मौन (शून्य ऑडियो नमूना डेटा) को सम्मिलित करने की जरूरत है।

+0

महोदय, आपके उत्तर के लिए धन्यवाद! मैंने अपना प्रश्न संपादित कर लिया है और नीचे दिए गए नीचे लिखा है जो मैंने आपके उत्तर से अब तक समझा है। कृप्या मुझे सही करें। और अगर इससे कोई समझ नहीं आती है, तो क्या आप मुझे कुछ सटीक कामकाजी कोड दे सकते हैं? मैं वास्तव में इस मुद्दे के साथ 2-3 दिनों का संघर्ष कर रहा हूं, मैं अंतर्ज्ञान प्राप्त करने के लिए वास्तव में बहुत मूर्ख हूं। –

+0

क्या आप EDIT 2.0 की जांच कर सकते हैं? –

+0

ऑडियो अंतराल समाधान का सुझाव देने के लिए फिर से धन्यवाद। क्या आप कृपया मुझे बता सकते हैं कि अगर मेरा वर्तमान कार्यान्वयन सही है या नहीं? यदि मैं ऑडियो स्ट्रीम टाइम बेस को '{1, sampleRate} 'के रूप में सेट करना चाहता हूं, तो मुझे' writeAudio (..) 'में क्या परिवर्तन करने की आवश्यकता है? –

संबंधित मुद्दे