[삽질기록] RAW H.264(AVC)에 MP4컨테이너 씌우기 #3 / 테스트 코드를 이용하여 MP4 컨테이너 씌우기

2019. 6. 3. 18:41Programming/Android

반응형

MediaMuxer를 대신해서 FFmpeg를 사용하여 MP4 컨테이너를 씌우는 예제를 검색하다가, 깃허브에서 [ffmpeg-muxer, Akagi201](https://github.com/Akagi201/ffmpeg-muxer/blob/master/main.c) 코드를 발견했다. 정확히 Muxing을 하는 코드여서, 이 코드를 사용하여 테스트해보기로 한다.

[삽질기록] RAW H.264(AVC)에 MP4컨테이너 씌우기 #2 / FFmpeg 빌드에서 muxer/demuxer모듈이 추가된 ffmpeg 라이브러리를 적용하고 빌드하니, 중간에 Got signal 에러를 뱉고는 앱이 멈춰버린다. 원인을 보니 오디오 파일을 입력하지 않았는데, 이로 인해서 코드 중간에 오동작이 발생해서 앱이 크러쉬되는 것이었다.

위의 ffmpeg-muxer 코드의 main에서 오디오 파일을 사용하는 부분은 다음과 같다.

if ((ret = avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0)) < 0) {
   LOG("Could not open input file.");
   goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx_a, 0)) < 0) {
   LOG("Failed to retrieve input stream information");
   goto end;
}

오디오 파일의 경로를 참조하여, 파일을 여는 코드다.

int audioindex_a = -1, audioindex_out = -1;
for (i = 0; i < ifmt_ctx_a->nb_streams; i++) {
   // Create output AVStream according to input AVStream
   if (ifmt_ctx_a->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
       audioindex_a = i;
       AVStream *in_stream = ifmt_ctx_a->streams[i];
       AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
       if (!out_stream) {
           LOG("Failed allocating output stream");
           ret = AVERROR_UNKNOWN;
           goto end;
       }
       audioindex_out = out_stream->index;
       // Copy the settings of AVCodecContext
       if (avcodec_copy_context(out_stream->codec, in_stream->codec) < 0) {
           LOG("Failed to copy context from input to output stream codec context");
           goto end;
       }
       out_stream->codec->codec_tag = 0;
       if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
           out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

       break;
   }
}

오디오 파일을 아웃풋 스트림으로 복사하는 코드다.

if (av_compare_ts(cur_pts_v, ifmt_ctx_v->streams[videoindex_v]->time_base, cur_pts_a, ifmt_ctx_a->streams[audioindex_a]->time_base) <= 0) {
   ifmt_ctx = ifmt_ctx_v;
   stream_index = videoindex_out;

   if (av_read_frame(ifmt_ctx, &pkt) >= 0) {
       do {
           if (pkt.stream_index == videoindex_v) {
               cur_pts_v = pkt.pts;
               break;
           }
       } while (av_read_frame(ifmt_ctx, &pkt) >= 0);
   } else {
       break;
   }
} else {
   ifmt_ctx = ifmt_ctx_a;
   stream_index = audioindex_out;
   if (av_read_frame(ifmt_ctx, &pkt) >= 0) {
       do {
           if (pkt.stream_index == audioindex_a) {
               cur_pts_a = pkt.pts;
               break;
           }
       } while (av_read_frame(ifmt_ctx, &pkt) >= 0);
   } else {
       break;
   }

}

오디오 파일과 비디오 파일의 각 프레임에 기록되어있는 타임스탬프를 비교하여, 해당 프레임을 복사하는 코드이다.

대충 위에서 오디오와 관련된 구문을 주석처리하고나니, RAW파일이 MP4로 변환되어 잘 동작하는 걸 확인할 수 있다. :)
이제 H265 RAW파일을 MP4로 변환하는 코드와, 오디오 파일을 MUXING하는 코드만 작성하면 동작상의 문제는 해결될 듯 했다.
안드로이드의 라이프사이클과 관련된 문제는 그 이후에 해결하면 될 듯 했다.

반응형