Skip to content

Commit 6177855

Browse files
committed
Support read video from stream (instead of file name).
Fixes 144 Signed-off-by: Yong Tang <[email protected]>
1 parent 0fc7e01 commit 6177855

File tree

6 files changed

+71
-8
lines changed

6 files changed

+71
-8
lines changed

tensorflow_io/video/BUILD

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ cc_binary(
1818
includes = ["."],
1919
linkshared = 1,
2020
deps = [
21+
"//tensorflow_io/core:dataset_ops",
2122
"@ffmpeg_3_4//:ffmpeg",
22-
"//tensorflow_io/core:dataset_ops",
2323
],
2424
)
2525

@@ -39,8 +39,8 @@ cc_binary(
3939
includes = ["."],
4040
linkshared = 1,
4141
deps = [
42+
"//tensorflow_io/core:dataset_ops",
4243
"@ffmpeg_2_8//:ffmpeg",
43-
"//tensorflow_io/core:dataset_ops",
4444
],
4545
)
4646

@@ -60,7 +60,7 @@ cc_binary(
6060
includes = ["."],
6161
linkshared = 1,
6262
deps = [
63+
"//tensorflow_io/core:dataset_ops",
6364
"@libav_9_20//:libav",
64-
"//tensorflow_io/core:dataset_ops",
6565
],
6666
)

tensorflow_io/video/kernels/ffmpeg.cc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,63 @@ namespace tensorflow {
3434
namespace data {
3535
namespace video {
3636

37+
static int io_read_packet(void *opaque, uint8_t *buf, int buf_size) {
38+
VideoReader *r = (VideoReader *)opaque;
39+
StringPiece result;
40+
Status status = r->stream_->Read(r->offset_, buf_size, &result, (char *)buf);
41+
if (!(status.ok() || errors::IsOutOfRange(status))) {
42+
return -1;
43+
}
44+
r->offset_ += result.size();
45+
return result.size();
46+
}
47+
48+
static int64_t io_seek(void *opaque, int64_t offset, int whence) {
49+
VideoReader *r = (VideoReader *)opaque;
50+
uint64 file_size = 0;
51+
Status status = r->stream_->GetFileSize(&file_size);
52+
if (!status.ok()) {
53+
return -1;
54+
}
55+
switch (whence)
56+
{
57+
case SEEK_SET:
58+
if (offset > file_size) {
59+
return -1;
60+
}
61+
r->offset_ = offset;
62+
return r->offset_;
63+
case SEEK_CUR:
64+
if (r->offset_ + offset > file_size) {
65+
return -1;
66+
}
67+
r->offset_ += offset;
68+
return r->offset_;
69+
case SEEK_END:
70+
if (offset > file_size) {
71+
return -1;
72+
}
73+
r->offset_ = file_size - offset;
74+
return r->offset_;
75+
case AVSEEK_SIZE:
76+
return file_size;
77+
default:
78+
break;
79+
}
80+
return -1;
81+
}
82+
3783
Status VideoReader::ReadHeader()
3884
{
85+
// Allocate format
86+
if ((format_context_ = avformat_alloc_context()) == NULL) {
87+
return errors::InvalidArgument("could not allocate format context");
88+
}
89+
// Allocate context
90+
if ((io_context_ = avio_alloc_context(NULL, 0, 0, this, io_read_packet, NULL, io_seek)) == NULL) {
91+
return errors::InvalidArgument("could not allocate io context");
92+
}
93+
format_context_->pb = io_context_;
3994
// Open input file, and allocate format context
4095
if (avformat_open_input(&format_context_, filename_.c_str(), NULL, NULL) < 0) {
4196
return errors::InvalidArgument("could not open video file: ", filename_);
@@ -206,6 +261,10 @@ VideoReader::~VideoReader() {
206261
avcodec_free_context(&codec_context_);
207262
#endif
208263
avformat_close_input(&format_context_);
264+
av_free(format_context_);
265+
if (io_context_ != NULL) {
266+
av_free(io_context_);
267+
}
209268
}
210269

211270
} // namespace

tensorflow_io/video/kernels/video_input.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ See the License for the specific language governing permissions and
1313
limitations under the License.
1414
==============================================================================*/
1515

16-
#include "kernels/dataset_ops.h"
1716
#include "kernels/video_reader.h"
1817

1918
namespace tensorflow {
@@ -35,7 +34,7 @@ class VideoInput: public FileInput<video::VideoReader> {
3534
Status ReadRecord(io::InputStreamInterface* s, IteratorContext* ctx, std::unique_ptr<video::VideoReader>& state, int64 record_to_read, int64* record_read, std::vector<Tensor>* out_tensors) const override {
3635
if (state.get() == nullptr) {
3736
VideoReaderInit();
38-
state.reset(new video::VideoReader(filename()));
37+
state.reset(new video::VideoReader(dynamic_cast<SizedRandomAccessInputStreamInterface*>(s), filename()));
3938
TF_RETURN_IF_ERROR(state.get()->ReadHeader());
4039
}
4140
// Read the first frame to get height and width

tensorflow_io/video/kernels/video_reader.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616
#include "tensorflow/core/framework/dataset.h"
1717
#include "tensorflow/core/lib/io/buffered_inputstream.h"
1818
#include "tensorflow/core/platform/file_system.h"
19+
#include "kernels/dataset_ops.h"
1920

2021
extern "C" {
2122

@@ -33,7 +34,7 @@ namespace video {
3334

3435
class VideoReader {
3536
public:
36-
explicit VideoReader(const string &filename) : filename_(filename) {}
37+
explicit VideoReader(SizedRandomAccessInputStreamInterface* s, const string& filename) : stream_(s), filename_(filename) {}
3738

3839
Status ReadHeader();
3940

@@ -43,6 +44,9 @@ class VideoReader {
4344

4445
virtual ~VideoReader();
4546

47+
public:
48+
SizedRandomAccessInputStreamInterface* stream_;
49+
int64 offset_ = 0;
4650
private:
4751
std::string ahead_;
4852
std::string filename_;
@@ -58,6 +62,7 @@ class VideoReader {
5862
AVCodecContext *codec_context_ = 0;
5963
AVFrame *frame_ = 0;
6064
AVPacket packet_;
65+
AVIOContext *io_context_ = NULL;
6166
TF_DISALLOW_COPY_AND_ASSIGN(VideoReader);
6267
};
6368

tests/test_video.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
video_path = os.path.join(
3030
os.path.dirname(os.path.abspath(__file__)), "test_video", "small.mp4")
31-
31+
video_path = "file://" + video_path
3232
def test_video_predict():
3333
model = tf.keras.applications.resnet50.ResNet50(weights='imagenet')
3434
x = video_io.VideoDataset(video_path, batch=1).map(lambda x: tf.keras.applications.resnet50.preprocess_input(tf.image.resize(x, (224, 224))))

tests/test_video_eager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
video_path = os.path.join(
3232
os.path.dirname(os.path.abspath(__file__)), "test_video", "small.mp4")
33-
33+
video_path = "file://" + video_path
3434
def test_video_dataset():
3535
"""test_video_dataset"""
3636
num_repeats = 2

0 commit comments

Comments
 (0)