2012-11-08 52 views
5

मैंने ffmpeg का उपयोग शुरू किया और मैं avi फ़ाइल को mp4/h264 फ़ाइल में कनवर्ट करना चाहता हूं। मैंने this समेत कई पोस्ट पढ़ी हैं, लेकिन मुझे कोई अच्छा उदाहरण नहीं मिला कि फ्रेम को mp4 फ़ाइल में कैसे सहेजना है। नीचे दिया गया कोड सरलीकृत है जो एवीआई फ़ाइल से फ्रेम को डीकोड करता है और इसे H264/mp4 फ़ाइल में एन्कोड करता है, लेकिन जब मैं फ्रेम को सहेजता हूं तो mp4 फ़ाइल नहीं खेला जा सकता है। मुझे लगता है कि मैं एन्कोडिंगएवी कंटेनर से फ़्रेम को डीकोड करना और उन्हें h264/mp4 में एन्कोड क्यों नहीं करता है?

में कुछ गड़बड़ कर दूंगा यदि आप मुझे बता सकते हैं कि क्या गलत है और इसे कैसे ठीक किया जाए।

const char* aviFileName = "aviFrom.avi"; 
const char* mp4FileName = "mp4To.mp4"; 

// Filling pFormatCtx by open video file and Retrieve stream information 
// ... 
// Retrieving codecCtxDecode and opening codecDecode 
//... 


// Get encoder 
codecCtxEncode = avcodec_alloc_context(); 
codecCtxEncode->qmax = 69; 
codecCtxEncode->max_qdiff = 4; 
codecCtxEncode->bit_rate = 400000; 
codecCtxEncode->width = codecCtxDecode->width; 
codecCtxEncode->height = codecCtxDecode->height; 
codecCtxEncode->pix_fmt = AV_PIX_FMT_YUV420P; 
codecEncode = avcodec_find_encoder(CODEC_ID_H264); 
if(codecEncode == NULL) 
    return -1; 
if(avcodec_open2(codecCtxEncode, codecEncode, NULL)) 
    return -1; 

SwsContext *sws_ctx = sws_getContext(codecCtxDecode->width, codecCtxDecode->height, codecCtxDecode->pix_fmt, 
          codecCtxDecode->width, codecCtxDecode->height, AV_PIX_FMT_YUV420P, 
          SWS_BILINEAR, NULL, NULL, NULL); 

// Allocate an AVFrame structure  
frameDecoded = avcodec_alloc_frame(); 
frameEncoded = avcodec_alloc_frame();  

avpicture_alloc((AVPicture *)frameEncoded, AV_PIX_FMT_YUV420P, codecCtxDecode->width, codecCtxDecode->height); 

while(av_read_frame(pFormatCtx, &packet)>=0) 
{  
    // Is this a packet from the video stream? 
    if(packet.stream_index==videoStreamIndex) { 
     avcodec_decode_video2(codecCtxDecode, frameDecoded, &frameFinished, &packet); 
     // Did we get a video frame? 
     if(frameFinished) 
     {   
      fwrite(packet.data, packet.size, 
      sws_scale(sws_ctx, frameDecoded->data, frameDecoded->linesize, 0, codecCtxDecode->height, 
         frameEncoded->data, frameEncoded->linesize); 



      int64_t pts = packet.pts; 
      av_free_packet(&packet); 
      av_init_packet(&packet); 
      packet.data = NULL; 
      packet.size = 0;  
      frameEncoded->pts = pts;     

      int failed = avcodec_encode_video2(codecCtxEncode, &packet, frameEncoded, &got_output); 
      if(failed) 
      { 
       exit(1); 
      } 
      fwrite(packet.data,1,packet.size, mp4File); 
     } 
    } 

    av_free_packet(&packet); 
} 

उत्तर

8

आपको सीधे कच्चे पैकेट लिखने के बजाय ffmpeg आउटपुट संदर्भ का उपयोग करना होगा।

बुनियादी कदम तुम करने की आवश्यकता:

// find output format 
AVOutputFormat * outputFormat = av_guess_format("mp4", NULL, NULL); 
AVFormatContext *outFmtCtx = NULL; 

// create output cotext 
avformat_alloc_output_context2(&outFmtCtx, outputFormat, NULL, NULL); 

// create new stream 
AVStream * outStrm = avformat_new_stream(outFmtCtx, codecEncode); 
avcodec_get_context_defaults3(outStrm->codec, *codec); 

outStrm->codec->codec_id = codec_id; 
outStrm->codec->coder_type = AVMEDIA_TYPE_VIDEO; 
/// outStrm->codec-> ... 
/// set all fields marked as "MUST be set by user" in avcodec.h 
/// .... 

// create file 
avio_open2(&outFmtCtx->pb, file_name, AVIO_FLAG_WRITE, NULL, NULL); 
avformat_write_header(outFmtCtx, NULL); 

/// write packets 
/// for () 
av_interleaved_write_frame(outFmtCtx, packet); 

/// finish 
av_write_trailer(outFmtCtx); 
avio_close(outFmtCtx->pb); 
avformat_free_context(outFmtCtx); 

युपीडी: पूरा कोड है कि बिना फिर से एन्कोडिंग प्रतियां वीडियो:

extern "C" { 
#include <libavformat/avformat.h> 
#include <libavcodec/avcodec.h> 
#include <libavutil/avutil.h> 
} 

int main(int argc, char* argv[]) 
{ 
    const char * kInputFileName = "f:/Projects/Temp/testFFMPEG2/test/test_in.avi"; 
    const char * kOutputFileName = "f:/Projects/Temp/testFFMPEG2/test/text_out.avi"; 
    const char * kOutputFileType = "avi"; 

    av_register_all(); 

    AVFormatContext * inCtx = NULL; 
    int err = avformat_open_input(&inCtx, kInputFileName, NULL, NULL); 
    if (err < 0) 
     exit(1); 

    err = av_find_stream_info(inCtx); 
    if (err < 0) 
     exit(1); 


    int vs = -1; 
    for (unsigned int s = 0; s < inCtx->nb_streams; ++s) 
    { 
     if (inCtx->streams[s] && 
      inCtx->streams[s]->codec && 
      inCtx->streams[s]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 
     { 
      vs = s; 
      break; 
     }   
    } 

    if (vs == -1) 
     exit(1); 

    AVOutputFormat * outFmt = av_guess_format(kOutputFileType, NULL, NULL); 
    if (!outFmt) 
     exit(1); 

    AVFormatContext *outCtx = NULL; 
    err = avformat_alloc_output_context2(&outCtx, outFmt, NULL, NULL); 

    if (err < 0 || !outCtx) 
     exit(1); 

    AVStream * outStrm = av_new_stream(outCtx, 0); 
    AVStream const * const inStrm = inCtx->streams[vs]; 
    AVCodec * codec = NULL; 
    avcodec_get_context_defaults3(outStrm->codec, codec); 
    outStrm->codec->thread_count = 1; 

#if (LIBAVFORMAT_VERSION_MAJOR == 53) 
    outStrm->stream_copy = 1; 
#endif 

    outStrm->codec->coder_type = AVMEDIA_TYPE_VIDEO; 
    if(outCtx->oformat->flags & AVFMT_GLOBALHEADER) 
     outStrm->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 

    outStrm->codec->sample_aspect_ratio = outStrm->sample_aspect_ratio = inStrm->sample_aspect_ratio; 

#if (LIBAVFORMAT_VERSION_MAJOR == 53) 
    outCtx->timestamp = 0; 
#endif 

    err = avio_open(&outCtx->pb, kOutputFileName, AVIO_FLAG_WRITE); 
    if (err < 0) 
     exit(1); 

#if (LIBAVFORMAT_VERSION_MAJOR == 53) 
    AVFormatParameters params = {0}; 
    err = av_set_parameters(outCtx, &params); 
    if (err < 0) 
     exit(1); 
#endif 

    outStrm->disposition = inStrm->disposition; 
    outStrm->codec->bits_per_raw_sample = inStrm->codec->bits_per_raw_sample; 
    outStrm->codec->chroma_sample_location = inStrm->codec->chroma_sample_location; 
    outStrm->codec->codec_id = inStrm->codec->codec_id; 
    outStrm->codec->codec_type = inStrm->codec->codec_type; 

    if (!outStrm->codec->codec_tag) 
    { 
     if (! outCtx->oformat->codec_tag 
      || av_codec_get_id (outCtx->oformat->codec_tag, inStrm->codec->codec_tag) == outStrm->codec->codec_id 
      || av_codec_get_tag(outCtx->oformat->codec_tag, inStrm->codec->codec_id) <= 0) 
        outStrm->codec->codec_tag = inStrm->codec->codec_tag; 
    } 

    outStrm->codec->bit_rate = inStrm->codec->bit_rate; 
    outStrm->codec->rc_max_rate = inStrm->codec->rc_max_rate; 
    outStrm->codec->rc_buffer_size = inStrm->codec->rc_buffer_size; 

    const size_t extra_size_alloc = (inStrm->codec->extradata_size > 0) ? 
            (inStrm->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE) : 
            0; 

    if (extra_size_alloc) 
    { 
     outStrm->codec->extradata = (uint8_t*)av_mallocz(extra_size_alloc);  
     memcpy(outStrm->codec->extradata, inStrm->codec->extradata, inStrm->codec->extradata_size); 
    } 
    outStrm->codec->extradata_size = inStrm->codec->extradata_size; 

    AVRational input_time_base = inStrm->time_base; 
    AVRational frameRate = {25, 1}; 
    if (inStrm->r_frame_rate.num && inStrm->r_frame_rate.den 
     && (1.0 * inStrm->r_frame_rate.num/inStrm->r_frame_rate.den < 1000.0)) 
    { 
     frameRate.num = inStrm->r_frame_rate.num; 
     frameRate.den = inStrm->r_frame_rate.den; 
    } 

    outStrm->r_frame_rate = frameRate; 
    outStrm->codec->time_base = inStrm->codec->time_base; 

    outStrm->codec->pix_fmt = inStrm->codec->pix_fmt; 
    outStrm->codec->width = inStrm->codec->width; 
    outStrm->codec->height = inStrm->codec->height; 
    outStrm->codec->has_b_frames = inStrm->codec->has_b_frames; 
    if (! outStrm->codec->sample_aspect_ratio.num) { 
     AVRational r0 = {0, 1}; 
     outStrm->codec->sample_aspect_ratio = 
      outStrm->sample_aspect_ratio = 
      inStrm->sample_aspect_ratio.num ? inStrm->sample_aspect_ratio : 
      inStrm->codec->sample_aspect_ratio.num ? 
      inStrm->codec->sample_aspect_ratio : r0; 
    } 
#if LIBAVFORMAT_VERSION_MAJOR == 53 
    av_write_header(outFmtCtx); 
#else 
    avformat_write_header(outCtx, NULL); 
#endif 


    for (;;) 
    { 
     AVPacket packet = {0}; 
     av_init_packet(&packet); 

     err = AVERROR(EAGAIN); 
     while (AVERROR(EAGAIN) == err) 
      err = av_read_frame(inCtx, &packet); 

     if (err < 0) 
     { 
      if (AVERROR_EOF != err && AVERROR(EIO) != err) 
      { 
       // error 
       exit(1);    
      } 
      else 
      { 
       // end of file 
       break; 
      }    
     } 


     if (packet.stream_index == vs) 
     { 

      err = av_interleaved_write_frame(outCtx, &packet); 
      if (err < 0) 
       exit(1); 
     }    

     av_free_packet(&packet);   

    } 

    av_write_trailer(outCtx); 
    if (!(outCtx->oformat->flags & AVFMT_NOFILE) && outCtx->pb) 
     avio_close(outCtx->pb); 

    avformat_free_context(outCtx); 
    av_close_input_file(inCtx); 
    return 0; 
} 
+0

hypothetically, अगर मैं मैन्युअल रूप से होगा फ़ाइल में हेडर और पाद लेख जोड़ें, क्या यह ठीक होगा? – theateist

+0

कुछ प्रारूपों के लिए, यह काम कर सकता है, लेकिन सामान्य रूप से, यह गलत तरीका है। – pogorskiy

+0

आपके द्वारा लिखे गए प्रयोग के प्रयोग के रूप में मैंने एवीआई फ़ाइल से पढ़ने की कोशिश की और नई एवी फ़ाइल में मैंने जो पैकेट पढ़ा है (डीकोडिंग के बिना) लिखा है। मुझे एक ही फाइल प्राप्त करने की उम्मीद है लेकिन नई फाइल 3KB पर बड़ी है और मीडिया प्लेयर इसे – theateist