static int ffmpeg_frame_is_sync (codec_data_t *ifptr, uint8_t *buffer, uint32_t buflen, void *userdata) { int ret; int ftype; uint32_t offset; ffmpeg_codec_t *ffmpeg = (ffmpeg_codec_t *)ifptr; switch (ffmpeg->m_codecId) { case CODEC_ID_H264: // look for idr nal do { uint8_t nal_type = h264_nal_unit_type(buffer); if (nal_type == H264_NAL_TYPE_SEQ_PARAM) return 1; //ffmpeg_message(LOG_DEBUG, "ffmpeg", "nal type %u", nal_type); if (h264_nal_unit_type_is_slice(nal_type)) { if (nal_type == H264_NAL_TYPE_IDR_SLICE) return 1; #if 0 uint8_t slice_type; if (h264_find_slice_type(buffer, buflen, &slice_type) >= 0) { return H264_TYPE_IS_I(slice_type) ? 1 : 0; } return 0; #else return 0; #endif } offset = h264_find_next_start_code(buffer, buflen); buffer += offset; buflen -= offset; } while (offset != 0); break; case CODEC_ID_MPEG2VIDEO: // this would be for mpeg2 ret = MP4AV_Mpeg3FindPictHdr(buffer, buflen, &ftype); ffmpeg_message(LOG_ERR, "ffmpeg", "ret %u type %u", ret, ftype); if (ret >= 0 && ftype == 1) { return 1; } break; case CODEC_ID_MPEG4: { uint8_t *vop = MP4AV_Mpeg4FindVop(buffer, buflen); if (vop == NULL) return 0; if (MP4AV_Mpeg4GetVopType(vop, buflen - (vop - buffer)) == VOP_TYPE_I) return 1; } break; default: // for every other, return that it is sync return 1; } return 0; }
static bool ffmpeg_find_h264_size (ffmpeg_codec_t *ffmpeg, const uint8_t *ud, uint32_t ud_size) { uint32_t offset = 0; do { if (h264_is_start_code(ud + offset)) { if (h264_nal_unit_type(ud + offset) == H264_NAL_TYPE_SEQ_PARAM) { h264_decode_t dec; memset(&dec, 0, sizeof(dec)); if (h264_read_seq_info(ud + offset, ud_size - offset, &dec) == 0) { ffmpeg->m_c->width = dec.pic_width; ffmpeg->m_c->height = dec.pic_height; return true; } } } offset += h264_find_next_start_code(ud + offset, ud_size - offset); } while (offset < ud_size); return false; }
static int ffmpeg_decode (codec_data_t *ptr, frame_timestamp_t *pts, int from_rtp, int *sync_frame, uint8_t *buffer, uint32_t buflen, void *ud) { ffmpeg_codec_t *ffmpeg = (ffmpeg_codec_t *)ptr; uint32_t bytes_used = 0; int got_picture = 0; uint64_t ts = pts->msec_timestamp; //ffmpeg_message(LOG_ERR, "ffmpeg", "%u timestamp "U64, buflen, ts); if (ffmpeg->m_codec_opened == false) { // look for header, like above, and open it bool open_codec = true; switch (ffmpeg->m_codecId) { case CODEC_ID_H264: open_codec = ffmpeg_find_h264_size(ffmpeg, buffer, buflen); break; default: break; } if (open_codec) { if (avcodec_open(ffmpeg->m_c, ffmpeg->m_codec) < 0) { ffmpeg_message(LOG_CRIT, "ffmpeg", "failed to open codec"); return buflen; } ffmpeg->m_codec_opened = true; ffmpeg_message(LOG_ERR, "ffmpeg", "opened codec"); } else { ffmpeg_message(LOG_ERR, "ffmpeg", "no open %u "U64, buflen, ts); return buflen; } } // look and see if we have read the I frame. if (ffmpeg->m_got_i == false) { if (ffmpeg_frame_is_sync(ptr, buffer, buflen, NULL) == 0) { return buflen; } ffmpeg->m_got_i = true; } int ret; do { int local_got_picture; ret = avcodec_decode_video(ffmpeg->m_c, ffmpeg->m_picture, &local_got_picture, buffer + bytes_used, buflen - bytes_used); bytes_used += ret; //ffmpeg_message(LOG_CRIT, "ffmpeg", "used %d %d", ret, local_got_picture); got_picture |= local_got_picture; } while (ret != -1 && bytes_used < buflen); if (pts->timestamp_is_pts) { //ffmpeg_message(LOG_ERR, "ffmpeg", "pts timestamp "U64, ts); if (ffmpeg->m_codecId == CODEC_ID_MPEG2VIDEO) { if (ffmpeg->pts_convert.frame_rate == 0.0) { int have_mpeg2; uint32_t h, w; double bitrate, aspect_ratio; uint8_t profile; MP4AV_Mpeg3ParseSeqHdr(buffer, buflen, &have_mpeg2, &h, &w, &ffmpeg->pts_convert.frame_rate, &bitrate, &aspect_ratio, &profile); } int ftype; int header = MP4AV_Mpeg3FindPictHdr(buffer, buflen, &ftype); if (header >= 0) { uint16_t temp_ref = MP4AV_Mpeg3PictHdrTempRef(buffer + header); uint64_t ret; if (got_picture == 0 || mpeg3_find_dts_from_pts(&ffmpeg->pts_convert, ts, ftype, temp_ref, &ret) < 0) { ffmpeg->have_cached_ts = false; return buflen; } #if 0 ffmpeg->m_vft->log_msg(LOG_DEBUG, "ffmpeg", "pts "U64" dts "U64" temp %u type %u %u", ts, ret, temp_ref, ftype, got_picture); #endif ts = ret; // ffmpeg_message(LOG_ERR, "ffmpeg", "type %d ref %u "U64, ftype, temp_ref, ret); } } else if (ffmpeg->m_codecId == CODEC_ID_MPEG4) { uint8_t *vopstart = MP4AV_Mpeg4FindVop(buffer, buflen); if (vopstart) { int ftype = MP4AV_Mpeg4GetVopType(vopstart, buflen); uint64_t dts; if (MP4AV_calculate_dts_from_pts(&ffmpeg->pts_to_dts, ts, ftype, &dts) < 0) { ffmpeg->have_cached_ts = false; #ifdef DEBUG_FFMPEG_PTS ffmpeg_message(LOG_DEBUG, "ffmpeg", "type %d %d pts "U64" failed to calc", ftype, got_picture, ts); #endif return buflen; } #ifdef DEBUG_FFMPEG_PTS ffmpeg_message(LOG_DEBUG, "ffmpeg", "type %d %d pts "U64" dts "U64, ftype, got_picture, ts, dts); #endif ts = dts; } } else if (ffmpeg->m_codecId == CODEC_ID_H264) { uint8_t *nal_ptr = buffer; uint32_t len = buflen; bool have_b_nal = false; do { if (h264_nal_unit_type_is_slice(h264_nal_unit_type(nal_ptr))) { uint8_t slice_type; if (h264_find_slice_type(nal_ptr, len, &slice_type, false) >= 0) { have_b_nal = H264_TYPE_IS_B(slice_type); } } uint32_t offset = h264_find_next_start_code(nal_ptr, len); if (offset == 0) { len = 0; } else { nal_ptr += offset; len -= offset; } } while (len > 0 && have_b_nal == false); uint64_t dts; if (MP4AV_calculate_dts_from_pts(&ffmpeg->pts_to_dts, ts, have_b_nal ? VOP_TYPE_B : VOP_TYPE_P, &dts) < 0) { ffmpeg->have_cached_ts = false; #ifdef DEBUG_FFMPEG_PTS ffmpeg_message(LOG_DEBUG, "ffmpeg", "pts "U64" failed to calc", ts); #endif return buflen; } ts = dts; } } if (got_picture != 0) { if (ffmpeg->m_video_initialized == false) { double aspect; if (ffmpeg->m_c->sample_aspect_ratio.den == 0) { aspect = 0.0; // don't have one } else { aspect = av_q2d(ffmpeg->m_c->sample_aspect_ratio); } if (ffmpeg->m_c->width == 0) { return buflen; } ffmpeg->m_vft->video_configure(ffmpeg->m_ifptr, ffmpeg->m_c->width, ffmpeg->m_c->height, VIDEO_FORMAT_YUV, aspect); ffmpeg->m_video_initialized = true; } if (ffmpeg->m_c->pix_fmt != PIX_FMT_YUV420P) { // convert the image from whatever it is to YUV 4:2:0 AVPicture from, to; int ret; // get the buffer to copy into (put it right into the ring buffer) ret = ffmpeg->m_vft->video_get_buffer(ffmpeg->m_ifptr, &to.data[0], &to.data[1], &to.data[2]); if (ret == 0) { return buflen; } // set up the AVPicture structures to.linesize[0] = ffmpeg->m_c->width; to.linesize[1] = ffmpeg->m_c->width / 2; to.linesize[2] = ffmpeg->m_c->width / 2; for (int ix = 0; ix < 4; ix++) { from.data[ix] = ffmpeg->m_picture->data[ix]; from.linesize[ix] = ffmpeg->m_picture->linesize[ix]; } img_convert(&to, PIX_FMT_YUV420P, &from, ffmpeg->m_c->pix_fmt, ffmpeg->m_c->width, ffmpeg->m_c->height); ffmpeg->m_vft->video_filled_buffer(ffmpeg->m_ifptr, ffmpeg->have_cached_ts ? ffmpeg->cached_ts : ts); } else { ffmpeg->m_vft->video_have_frame(ffmpeg->m_ifptr, ffmpeg->m_picture->data[0], ffmpeg->m_picture->data[1], ffmpeg->m_picture->data[2], ffmpeg->m_picture->linesize[0], ffmpeg->m_picture->linesize[1], ffmpeg->have_cached_ts ? ffmpeg->cached_ts : ts); } ffmpeg->cached_ts = ts; } else { ffmpeg->cached_ts = ts; ffmpeg->have_cached_ts = true; } #ifdef DEBUG_FFMPEG_FRAME ffmpeg_message(LOG_DEBUG, "ffmpeg", "used %u of %u", bytes_used, buflen); #endif return (buflen); }
/* * avi2raw * required arg1 should be the input AVI file * required arg2 should be the output RAW file */ int main(int argc, char** argv) { /* configurable variables from command line */ bool extractVideo = TRUE; /* FALSE implies extract audio */ u_int32_t start = 0; /* secs, start offset */ u_int32_t duration = 0; /* secs, 0 implies entire file */ bool quiet = FALSE; /* internal variables */ char* aviFileName = NULL; char* rawFileName = NULL; avi_t* aviFile = NULL; FILE* rawFile = NULL; int verbose = FALSE; u_int32_t numBytes, totBytes = 0; bool eliminate_short_frames = FALSE; uint32_t short_frames_len; /* begin process command line */ progName = argv[0]; while (1) { int c = -1; int option_index = 0; static struct option long_options[] = { { "audio", 0, 0, 'a' }, { "eliminate-short-frames", optional_argument, 0, 'e'}, { "length", 1, 0, 'l' }, { "quiet", 0, 0, 'q' }, { "start", 1, 0, 's' }, { "video", 0, 0, 'v' }, { "version", 0, 0, 'V'}, { "verbose", 0, 0, 'R'}, { "help", 0, 0, 'h'}, { NULL, 0, 0, 0 } }; c = getopt_long_only(argc, argv, "ael:qs:vVh", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': fprintf(stderr, "%s - %s version %s\n", progName, MPEG4IP_PACKAGE, MPEG4IP_VERSION); fprintf(stderr, "options:\n"); fprintf(stderr, " --audio - extract audio track\n"); fprintf(stderr, " --length <length> - extract <length> secs\n"); fprintf(stderr, " --quiet - quiet mode\n"); fprintf(stderr, " --start <time> - extract from <start> time\n"); fprintf(stderr, " --video - extract video track\n"); fprintf(stderr, " --eliminate-short-frames <bytes> - eliminate short frames of bytes or less - default 4\n"); return 0; case 'a': { extractVideo = FALSE; break; } case 'e': eliminate_short_frames = TRUE; if (optarg) { if (optarg[0] == '=') optarg[0] = ' '; if (sscanf(optarg, "%u", &short_frames_len) != 1) { fprintf(stderr, "%s:bad eliminate length %s\n", progName, optarg); } } else { short_frames_len = 4; } break; case 'l': { /* --length=<secs> */ u_int i; if (sscanf(optarg, "%u", &i) < 1) { fprintf(stderr, "%s: bad length specified: %s\n", progName, optarg); } else { duration = i; } break; } case 'q': { quiet = TRUE; break; } case 's': { /* --start=<secs> */ u_int i; if (sscanf(optarg, "%u", &i) < 1) { fprintf(stderr, "%s: bad start specified: %s\n", progName, optarg); } else { start = i; } break; } case 'v': { extractVideo = TRUE; break; } case '?': break; case 'R': verbose = TRUE; quiet = FALSE; break; case 'V': fprintf(stderr, "%s - %s version %s\n", progName, MPEG4IP_PACKAGE, MPEG4IP_VERSION); return(0); default: fprintf(stderr, "%s: unknown option specified, ignoring: %c\n", progName, c); } } /* check that we have at least two non-option arguments */ if ((argc - optind) < 2) { fprintf(stderr, "usage: %s <avi-file> <raw-file>\n", progName); exit(1); } /* point to the specified file names */ aviFileName = argv[optind++]; rawFileName = argv[optind++]; /* warn about extraneous non-option arguments */ if (optind < argc) { fprintf(stderr, "%s: unknown options specified, ignoring: ", progName); while (optind < argc) { fprintf(stderr, "%s ", argv[optind++]); } fprintf(stderr, "\n"); } /* end processing of command line */ /* open the AVI file */ aviFile = AVI_open_input_file(aviFileName, TRUE); if (aviFile == NULL) { fprintf(stderr, "%s: error %s: %s\n", progName, aviFileName, AVI_strerror()); exit(4); } if (!quiet) { fprintf(stderr, "%s - %s version %s\n", progName, MPEG4IP_PACKAGE, MPEG4IP_VERSION); } /* open the RAW file */ rawFile = fopen(rawFileName, "wb"); if (rawFile == NULL) { fprintf(stderr, "%s: error opening %s: %s\n", progName, rawFileName, strerror(errno)); exit(5); } if (extractVideo) { double videoFrameRate = AVI_video_frame_rate(aviFile); u_int32_t numVideoFrames = AVI_video_frames(aviFile); u_int32_t fileDuration = ceil(numVideoFrames / videoFrameRate); u_int32_t numDesiredVideoFrames; u_int32_t videoFramesRead = 0; u_int32_t emptyFramesRead = 0; /* get a buffer large enough to handle a frame of raw SDTV */ u_char* buf = (u_char*)malloc(768 * 576 * 4); if (duration) { numDesiredVideoFrames = duration * videoFrameRate; } else { numDesiredVideoFrames = numVideoFrames; } if (buf == NULL) { fprintf(stderr, "%s: error allocating memory: %s\n", progName, strerror(errno)); exit(6); } /* check that start offset is valid */ if (start > fileDuration) { fprintf(stderr, "%s: specified start is past the end of the file\n", progName); exit(7); } if (AVI_seek_start(aviFile)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(8); } if (AVI_set_video_position(aviFile, (long) ROUND(start * videoFrameRate), NULL)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(9); } #ifdef DEBUG_H264 h264_decode_t dec; #endif while (TRUE) { numBytes = AVI_read_frame(aviFile, (char *)buf); /* read error */ if (numBytes < 0) { break; } totBytes += numBytes; videoFramesRead++; if (verbose) { printf("frame %d - len %u total %u\n", videoFramesRead, numBytes, totBytes); } /* * note some capture programs * insert a zero length frame occasionally * hence numBytes == 0, but we're not a EOF */ if ((eliminate_short_frames && numBytes > short_frames_len) || (eliminate_short_frames == FALSE && numBytes)) { // test #ifdef DEBUG_H264 uint32_t offset = 0, read; do { if (h264_is_start_code(buf + offset)) { int ret = h264_detect_boundary(buf + offset, numBytes - offset, &dec); printf(" frame offset %d nal type %d slice %d %d\n", offset, dec.nal_unit_type, dec.slice_type, ret); } read = h264_find_next_start_code(buf + offset, numBytes - offset); offset += read; } while (read != 0 && offset < numBytes); #endif if (fwrite(buf, 1, numBytes, rawFile) != numBytes) { fprintf(stderr, "%s: error writing %s: %s\n", progName, rawFileName, strerror(errno)); break; } } else { emptyFramesRead++; } if (videoFramesRead >= numDesiredVideoFrames) { break; } } if (verbose) { printf("read %u video bytes\n", totBytes); } if (numBytes < 0) { printf("%s: error reading %s, frame %d, %s\n", progName, aviFileName, videoFramesRead + 1, AVI_strerror()); } if (videoFramesRead < numDesiredVideoFrames) { fprintf(stderr, "%s: warning: could only extract %u seconds of video (%u of %u frames)\n", progName, (unsigned int)ceil(videoFramesRead / videoFrameRate), videoFramesRead, numDesiredVideoFrames); } if (emptyFramesRead) { fprintf(stderr, "%s: warning: %u zero length frames ignored\n", progName, emptyFramesRead); } if (!quiet) { printf("%u video frames written\n", videoFramesRead - emptyFramesRead); } /* cleanup */ free(buf); } else { /* extract audio */ u_int32_t audioBytesRead = 0; u_char *buf = (u_char*) malloc(8*1024); u_int32_t numDesiredAudioBytes = AVI_audio_bytes(aviFile); u_int32_t audioBytesPerSec = 0; if (start != 0) { u_int32_t numAudioBytes = numDesiredAudioBytes; u_int32_t fileDuration; audioBytesPerSec = AVI_audio_rate(aviFile) * ((AVI_audio_bits(aviFile) + 7) / 8) * AVI_audio_channels(aviFile); fileDuration = ceil(numAudioBytes / audioBytesPerSec); numDesiredAudioBytes = duration * audioBytesPerSec; /* check that start offset is valid */ if (start > fileDuration) { fprintf(stderr, "%s: specified start is past the end of the file\n", progName); exit(7); } if (AVI_seek_start(aviFile)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(8); } if (AVI_set_audio_position(aviFile, start * audioBytesPerSec)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(9); } } else { if (AVI_seek_start(aviFile)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(8); } if (AVI_set_audio_position(aviFile, 0)) { fprintf(stderr, "%s: bad seek: %s\n", progName, AVI_strerror()); exit(9); } } while ((numBytes = AVI_read_audio(aviFile, (char *)buf, sizeof(buf))) > 0) { if (fwrite(buf, 1, numBytes, rawFile) != numBytes) { fprintf(stderr, "%s: error writing %s: %s\n", progName, rawFileName, strerror(errno)); break; } audioBytesRead += numBytes; if (numDesiredAudioBytes && audioBytesRead >= numDesiredAudioBytes) { break; } } if (verbose) { printf("read %u audio bytes\n", audioBytesRead); } if (duration && audioBytesRead < numDesiredAudioBytes) { fprintf(stderr, "%s: warning: could only extract %u seconds of audio\n", progName, audioBytesPerSec == 0 ? audioBytesRead : audioBytesRead / audioBytesPerSec); } if (!quiet && AVI_audio_bits(aviFile) != 0) { printf("%u audio samples written\n", audioBytesRead / ((AVI_audio_bits(aviFile) + 7) / 8)); } } /* cleanup */ AVI_close(aviFile); fclose(rawFile); return(0); }