2012-11-08 27 views
5

Ho iniziato a utilizzare ffmpeg e voglio convertire il file avi nel file mp4/h264. Ho letto molti post tra cui this, ma non sono riuscito a trovare alcun buon esempio su come salvare i frame in file mp4. Il codice seguente è semplificato da quello che decodifica i fotogrammi dal file avi e lo codifica nel file H264/mp4, ma quando salgo i frame il file mp4 non può essere riprodotto. Penso di non sbagliare nella codificaPerché decodificare i frame dal contenitore avi e codificarli su h264/mp4 non funziona?

Apprezzerò se potessi dirmi cosa c'è che non va e come risolverlo.

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); 
} 

risposta

8

È necessario utilizzare il contesto di output ffmpeg, invece di scrivere direttamente i pacchetti non elaborati.

I passaggi fondamentali che è necessario eseguire:

// 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); 

UPD: Il codice completo che copia il video senza re-encoding:

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

Ipoteticamente, se lo farei manualmente aggiungere l'intestazione e il piè di pagina al file, sarebbe OK? – theateist

+0

Per alcuni formati, può funzionare, ma in generale, questo è il modo sbagliato. – pogorskiy

+0

come per sperimentare ciò che hai scritto ho provato a leggere dal file avi e scrivere gli stessi pacchetti che ho letto (senza decodificare) in un nuovo file avi. Mi aspettavo di ottenere lo stesso file ma il nuovo file è più grande su 3KB e il lettore multimediale non può aprirlo – theateist