blob: f9cfd762ef05892d0ef69323ac183388c7ff3692 [file] [log] [blame]
// Copyright (c) 2014 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#import "./ivf_frame_parser.h"
#import <CoreFoundation/CoreFoundation.h>
#include <string>
#include <vector>
namespace VpxExample {
namespace {
const int kIvfFormatVersion = 0;
// IVF file "signature" string.
const char *kIvfSignature = "DKIF";
// IVF file size constants.
const int kIvfSignatureSize = 4;
const int kIvfFileHeaderSize = 32;
const int kIvfFrameHeaderSize = 12;
// IVF file header field offset constants.
// Offsets taken from http://wiki.multimedia.cx/index.php?title=IVF
// Offsets are in position order. The IVF signature offset, which is
// 0, has been omitted. Field value size is inferred by the difference between
// each offset, with the exception of the frame count, the size of which is 4.
const int kIvfVersionOffset = 4;
const int kIvfHeaderSizeOffset = 6;
const int kIvfCodecFourCcOffset = 8;
const int kIvfWidthOffset = 12;
const int kIvfHeightOffset = 14;
const int kIvfRateOffset = 16;
const int kIvfScaleOffset = 20;
const int kIvfFrameCountOffset = 24;
// IVF frame header offset constants.
const int kIvfFrameSizeOffset = 0;
const int kIvfFrameTimestampOffset = 4;
// VPx four CC constants.
const uint32_t kVp8FourCc = 0x30385056;
const uint32_t kVp9FourCc = 0x30395056;
uint16_t ReadIvfHeaderUint16(const std::string &ivf_header, int offset) {
const void *read_head = reinterpret_cast<const void *>(&ivf_header[offset]);
return CFSwapInt16LittleToHost(*reinterpret_cast<const uint16_t*>(read_head));
}
uint32_t ReadIvfHeaderUint32(const std::string &ivf_header, int offset) {
const void *read_head = reinterpret_cast<const void *>(&ivf_header[offset]);
return CFSwapInt32LittleToHost(*reinterpret_cast<const uint32_t*>(read_head));
}
int64_t ReadIvfHeaderInt64(const std::string &ivf_header, int offset) {
const void *read_head = reinterpret_cast<const void *>(&ivf_header[offset]);
return CFSwapInt64LittleToHost(*reinterpret_cast<const int64_t*>(read_head));
}
} // namespace
void IvfFrameParser::FileCloser::operator()(FILE* file) {
fclose(file);
}
// Processes the 32-byte IVF file header and returns true when the IVF file
// appears to contain VPx video.
bool IvfFrameParser::HasVpxFrames(const std::string &file_path,
VpxFormat *vpx_format) {
file_.reset(fopen(file_path.c_str(), "rb"));
if (!file_.get()) {
NSLog(@"Unable to open %s", file_path.c_str());
return false;
}
std::string ivf_header;
ivf_header.reserve(kIvfFileHeaderSize);
const size_t read_count = fread(reinterpret_cast<void*>(&ivf_header[0]),
1,
kIvfFileHeaderSize,
file_.get());
if (read_count != kIvfFileHeaderSize) {
NSLog(@"Read failure after %zu bytes", read_count);
return false;
}
const void *read_head = reinterpret_cast<const void*>(&ivf_header[0]);
if (memcmp(read_head, kIvfSignature, kIvfSignatureSize) != 0) {
NSLog(@"%s is not an IVF file.", file_path.c_str());
return false;
}
const uint16_t version = ReadIvfHeaderUint16(ivf_header, kIvfVersionOffset);
if (version != kIvfFormatVersion) {
NSLog(@"%s has an invalid IVF file version (%hu).",
file_path.c_str(), version);
return false;
}
const uint16_t header_size = ReadIvfHeaderUint16(ivf_header,
kIvfHeaderSizeOffset);
if (header_size != kIvfFileHeaderSize) {
NSLog(@"%s has an invalid IVF header size (%hu).",
file_path.c_str(), header_size);
return false;
}
const uint32_t fourcc = ReadIvfHeaderUint32(ivf_header,
kIvfCodecFourCcOffset);
if (fourcc == kVp8FourCc) {
vpx_format_.codec = VP8;
} else if (fourcc == kVp9FourCc) {
vpx_format_.codec = VP9;
} else {
NSLog(@"Invalid codec four CC (%4s) in %s",
reinterpret_cast<const char *>(&fourcc), file_path.c_str());
return false;
}
vpx_format_.width = ReadIvfHeaderUint16(ivf_header, kIvfWidthOffset);
vpx_format_.height = ReadIvfHeaderUint16(ivf_header, kIvfHeightOffset);
*vpx_format = vpx_format_;
timebase_.numerator = ReadIvfHeaderUint32(ivf_header, kIvfScaleOffset);
timebase_.denominator = ReadIvfHeaderUint32(ivf_header, kIvfRateOffset);
// TODO(tomfinegan): Move timebase refactoring to a common location.
if (timebase_.numerator > 1 &&
timebase_.denominator > timebase_.numerator &&
timebase_.denominator % timebase_.numerator == 0) {
timebase_.denominator /= timebase_.numerator;
timebase_.numerator = 1;
}
// TODO(tomfinegan): Decide if it's ever worth it to read this value.
frame_count_ = ReadIvfHeaderUint32(ivf_header, kIvfFrameCountOffset);
NSLog(@"IVF timebase %lld / %lld",
timebase_.numerator, timebase_.denominator);
return true;
}
bool IvfFrameParser::ReadFrame(VpxFrame *frame) {
if (frame == NULL || frame->data == NULL) {
return false;
}
std::string ivf_frame_header;
ivf_frame_header.reserve(kIvfFrameHeaderSize);
size_t read_count = fread(reinterpret_cast<void*>(&ivf_frame_header[0]),
1,
kIvfFrameHeaderSize,
file_.get());
if (feof(file_.get())) {
NSLog(@"End of file.");
return false;
}
if (read_count != kIvfFrameHeaderSize) {
NSLog(@"Read failure after %zu frame header bytes", read_count);
return false;
}
const uint32_t frame_payload_size =
ReadIvfHeaderUint32(ivf_frame_header, kIvfFrameSizeOffset);
if (frame->data->capacity() < frame_payload_size) {
frame->data->reserve(frame_payload_size * 2);
}
frame->timebase = timebase_;
frame->timestamp = ReadIvfHeaderInt64(ivf_frame_header,
kIvfFrameTimestampOffset);
read_count = fread(reinterpret_cast<void*>(&(*frame->data)[0]),
1,
frame_payload_size,
file_.get());
if (read_count != frame_payload_size) {
NSLog(@"Read failure, file truncated? (expected: %u got: %zu)",
frame_payload_size, read_count);
}
frame->length = frame_payload_size;
return true;
}
} // namespace VpxExample